Merge "Remove a use of String16::remove." am: 7f48273b74 am: 2b1fe316ea am: f2175d3cba
Original change: https://android-review.googlesource.com/c/platform/frameworks/av/+/1704488
Change-Id: I4b7c23d7941515235b9343e254c53c84bc071481
diff --git a/Android.bp b/Android.bp
index 9937b98..60f0ff1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,18 +1,3 @@
-//
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
// *** THIS PACKAGE HAS SPECIAL LICENSING CONDITIONS. PLEASE
// CONSULT THE OWNERS AND opensource-licensing@google.com BEFORE
// DEPENDING ON IT IN YOUR PROJECT. ***
@@ -48,3 +33,66 @@
"NOTICE",
],
}
+
+aidl_interface {
+ name: "av-types-aidl",
+ unstable: true,
+ host_supported: true,
+ vendor_available: true,
+ double_loadable: true,
+ local_include_dir: "aidl",
+ srcs: [
+ "aidl/android/media/InterpolatorConfig.aidl",
+ "aidl/android/media/InterpolatorType.aidl",
+ "aidl/android/media/MicrophoneInfoData.aidl",
+ "aidl/android/media/VolumeShaperConfiguration.aidl",
+ "aidl/android/media/VolumeShaperConfigurationOptionFlag.aidl",
+ "aidl/android/media/VolumeShaperConfigurationType.aidl",
+ "aidl/android/media/VolumeShaperOperation.aidl",
+ "aidl/android/media/VolumeShaperOperationFlag.aidl",
+ "aidl/android/media/VolumeShaperState.aidl",
+ ],
+ backend: {
+ cpp: {
+ min_sdk_version: "29",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.bluetooth.updatable",
+ "com.android.media",
+ "com.android.media.swcodec",
+ ],
+ },
+ },
+}
+
+cc_library_headers {
+ name: "av-headers",
+ export_include_dirs: ["include"],
+ static_libs: [
+ "av-types-aidl-cpp",
+ ],
+ export_static_lib_headers: [
+ "av-types-aidl-cpp",
+ ],
+ header_libs: [
+ "libaudioclient_aidl_conversion_util",
+ ],
+ export_header_lib_headers: [
+ "libaudioclient_aidl_conversion_util",
+ ],
+ host_supported: true,
+ vendor_available: true,
+ double_loadable: true,
+ min_sdk_version: "29",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.bluetooth.updatable",
+ "com.android.media",
+ "com.android.media.swcodec",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
diff --git a/MainlineFiles.cfg b/MainlineFiles.cfg
index 37d714c..490bbbf 100644
--- a/MainlineFiles.cfg
+++ b/MainlineFiles.cfg
@@ -1,10 +1,13 @@
-#
+#
# mainline files for frameworks/av
+# this list used by tools/mainline_hook_*.sh to help separate
+# mainline changes vs framework changes, which release at different paces.
+#
#
# ignore comment (#) lines and blank lines
# rest are path prefixes starting at root of the project
# (so OWNERS, not frameworks/av/OWNERS)
-#
+#
# path
# INCLUDE path
# EXCLUDE path
@@ -24,11 +27,5 @@
media/codec2/components/
media/codecs/
media/extractors/
-media/libstagefright/codecs/amrnb/
-media/libstagefright/codecs/amrwb/
-media/libstagefright/codecs/amrwbenc/
-media/libstagefright/codecs/common/
-media/libstagefright/codecs/flac/
-media/libstagefright/codecs/m4v_h263/
-media/libstagefright/codecs/mp3dec/
-media/libstagefright/mpeg2ts
+media/libstagefright/mpeg2ts/
+media/libstagefright/flac/
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index ae920c0..8fe48c2 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,2 +1,11 @@
[Hook Scripts]
mainline_hook = ${REPO_ROOT}/frameworks/av/tools/mainline_hook_partial.sh ${REPO_ROOT} ${PREUPLOAD_FILES}
+
+[Builtin Hooks]
+clang_format = true
+
+[Builtin Hooks Options]
+# Only turn on clang-format check for the following subfolders.
+clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
+ media/libmediatranscoding/
+ services/mediatranscoding/
diff --git a/aidl/android/media/InterpolatorConfig.aidl b/aidl/android/media/InterpolatorConfig.aidl
new file mode 100644
index 0000000..49f90e8
--- /dev/null
+++ b/aidl/android/media/InterpolatorConfig.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.InterpolatorType;
+
+/**
+ * {@hide}
+ */
+parcelable InterpolatorConfig {
+ InterpolatorType type = InterpolatorType.CUBIC;
+ /** For cubic interpolation, the boundary conditions in slope. */
+ float firstSlope;
+ float lastSlope;
+ /** A flattened list of <x, y> pairs, monotonically increasing in x. */
+ float[] xy;
+}
diff --git a/aidl/android/media/InterpolatorType.aidl b/aidl/android/media/InterpolatorType.aidl
new file mode 100644
index 0000000..b722cad
--- /dev/null
+++ b/aidl/android/media/InterpolatorType.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * Polynomial spline interpolators.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+enum InterpolatorType {
+ /** Not continuous. */
+ STEP,
+ /** C0. */
+ LINEAR,
+ /** C1. */
+ CUBIC,
+ /** C1 (to provide locally monotonic curves). */
+ CUBIC_MONOTONIC,
+ // CUBIC_C2, // TODO - requires global computation / cache
+}
diff --git a/aidl/android/media/MicrophoneInfoData.aidl b/aidl/android/media/MicrophoneInfoData.aidl
new file mode 100644
index 0000000..747bfa5
--- /dev/null
+++ b/aidl/android/media/MicrophoneInfoData.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * {@hide}
+ */
+parcelable MicrophoneInfoData {
+ @utf8InCpp String deviceId;
+ int portId;
+ int type;
+ @utf8InCpp String address;
+ int deviceLocation;
+ int deviceGroup;
+ int indexInTheGroup;
+ float[] geometricLocation;
+ float[] orientation;
+ float[] frequencies;
+ float[] frequencyResponses;
+ int[] channelMapping;
+ float sensitivity;
+ float maxSpl;
+ float minSpl;
+ int directionality;
+}
diff --git a/aidl/android/media/VolumeShaperConfiguration.aidl b/aidl/android/media/VolumeShaperConfiguration.aidl
new file mode 100644
index 0000000..d6e6505
--- /dev/null
+++ b/aidl/android/media/VolumeShaperConfiguration.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.InterpolatorConfig;
+import android.media.VolumeShaperConfigurationOptionFlag;
+import android.media.VolumeShaperConfigurationType;
+
+/**
+ * {@hide}
+ */
+parcelable VolumeShaperConfiguration {
+ VolumeShaperConfigurationType type = VolumeShaperConfigurationType.ID;
+ int id;
+ /** Bitmask, indexed by VolumeShaperConfigurationOptionFlag. */
+ int optionFlags;
+ double durationMs;
+ @nullable InterpolatorConfig interpolatorConfig; // null if type == ID
+}
diff --git a/aidl/android/media/VolumeShaperConfigurationOptionFlag.aidl b/aidl/android/media/VolumeShaperConfigurationOptionFlag.aidl
new file mode 100644
index 0000000..f583cee
--- /dev/null
+++ b/aidl/android/media/VolumeShaperConfigurationOptionFlag.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+@Backing(type="int")
+enum VolumeShaperConfigurationOptionFlag {
+ VOLUME_IN_DBFS,
+ CLOCK_TIME,
+}
diff --git a/aidl/android/media/VolumeShaperConfigurationType.aidl b/aidl/android/media/VolumeShaperConfigurationType.aidl
new file mode 100644
index 0000000..aa6334e
--- /dev/null
+++ b/aidl/android/media/VolumeShaperConfigurationType.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+@Backing(type="int")
+enum VolumeShaperConfigurationType {
+ ID,
+ SCALE,
+}
diff --git a/aidl/android/media/VolumeShaperOperation.aidl b/aidl/android/media/VolumeShaperOperation.aidl
new file mode 100644
index 0000000..dd9a0e7
--- /dev/null
+++ b/aidl/android/media/VolumeShaperOperation.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * {@hide}
+ */
+parcelable VolumeShaperOperation {
+ /** Operations to do. Bitmask of VolumeShaperOperationFlag. */
+ int flags;
+ /** If >= 0 the id to remove in a replace operation. */
+ int replaceId;
+ /** Position in the curve to set if a valid number (not nan). */
+ float xOffset;
+}
diff --git a/aidl/android/media/VolumeShaperOperationFlag.aidl b/aidl/android/media/VolumeShaperOperationFlag.aidl
new file mode 100644
index 0000000..8fe5275
--- /dev/null
+++ b/aidl/android/media/VolumeShaperOperationFlag.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+@Backing(type="int")
+enum VolumeShaperOperationFlag {
+ /** The absence of this flag indicates "play". */
+ REVERSE,
+ TERMINATE,
+ JOIN,
+ DELAY,
+ CREATE_IF_NECESSARY,
+}
diff --git a/aidl/android/media/VolumeShaperState.aidl b/aidl/android/media/VolumeShaperState.aidl
new file mode 100644
index 0000000..4085e2b
--- /dev/null
+++ b/aidl/android/media/VolumeShaperState.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * {@hide}
+ */
+parcelable VolumeShaperState {
+ /** Linear volume in the range MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME. */
+ float volume;
+ /** Position on curve expressed from MIN_CURVE_TIME to MAX_CURVE_TIME. */
+ float xOffset;
+}
diff --git a/apex/Android.bp b/apex/Android.bp
index dabf4c2..6c45749 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -24,7 +24,10 @@
apex_defaults {
name: "com.android.media-defaults",
updatable: true,
- java_libs: ["updatable-media"],
+ java_libs: [
+ "updatable-media",
+ "service-media-s",
+ ],
multilib: {
first: {
// Extractor process runs only with the primary ABI.
@@ -40,15 +43,25 @@
"libmpeg2extractor",
"liboggextractor",
"libwavextractor",
- // JNI
- "libmediaparser-jni"
+ ],
+ // Transcoding service only run with primary ABI.
+ binaries: [
+ "mediatranscoding",
],
},
},
+ // JNI
+ native_shared_libs: [
+ "libmediaparser-jni",
+ "libmediaformatshaper",
+ ],
+ compile_multilib: "both",
prebuilts: [
- "mediaextractor.policy",
"code_coverage.policy",
+ "com.android.media-mediatranscoding.rc",
"crash_dump.policy",
+ "mediaextractor.policy",
+ "media-linker-config",
],
key: "com.android.media.key",
certificate: ":com.android.media.certificate",
@@ -73,9 +86,6 @@
name: "com.android.media",
manifest: "manifest.json",
defaults: ["com.android.media-defaults"],
- prebuilts: [
- "media-linker-config",
- ],
}
linker_config {
@@ -100,6 +110,12 @@
binaries: [
"mediaswcodec",
],
+ native_shared_libs: [
+ "libcodec2_hidl@1.0",
+ "libcodec2_hidl@1.1",
+ "libcodec2_hidl@1.2",
+ "libstagefright_foundation",
+ ],
prebuilts: [
"com.android.media.swcodec-mediaswcodec.rc",
"com.android.media.swcodec-ld.config.txt",
@@ -108,7 +124,6 @@
"crash_dump.policy",
"mediaswcodec.xml",
],
- use_vendor: true,
key: "com.android.media.swcodec.key",
certificate: ":com.android.media.swcodec.certificate",
@@ -129,6 +144,13 @@
}
prebuilt_etc {
+ name: "com.android.media-mediatranscoding.rc",
+ src: "mediatranscoding.rc",
+ filename: "init.rc",
+ installable: false,
+}
+
+prebuilt_etc {
name: "com.android.media.swcodec-mediaswcodec.rc",
src: "mediaswcodec.rc",
filename: "init.rc",
diff --git a/apex/TEST_MAPPING b/apex/TEST_MAPPING
index f036516..4b7c019 100644
--- a/apex/TEST_MAPPING
+++ b/apex/TEST_MAPPING
@@ -14,17 +14,9 @@
},
{
"include-filter": "com.google.android.media.gts.WidevineGenericOpsTests"
- }
- ]
- },
- {
- "name": "GtsExoPlayerTestCases",
- "options" : [
- {
- "include-annotation": "android.platform.test.annotations.SocPresubmit"
},
{
- "include-filter": "com.google.android.exoplayer.gts.DashTest#testWidevine23FpsH264Fixed"
+ "include-filter": "com.google.android.media.gts.WidevineH264PlaybackTests"
}
]
}
diff --git a/apex/manifest.json b/apex/manifest.json
index ddd642e..c7e56be 100644
--- a/apex/manifest.json
+++ b/apex/manifest.json
@@ -1,4 +1,10 @@
{
"name": "com.android.media",
- "version": 300000000
+ "version": 309999910,
+ "requireNativeLibs": [
+ "libandroid.so",
+ "libbinder_ndk.so",
+ "libmediandk.so",
+ ":sphal"
+ ]
}
diff --git a/apex/manifest_codec.json b/apex/manifest_codec.json
index 1f05d2e..d36e914 100644
--- a/apex/manifest_codec.json
+++ b/apex/manifest_codec.json
@@ -1,6 +1,6 @@
{
"name": "com.android.media.swcodec",
- "version": 300000000,
+ "version": 309999910,
"requireNativeLibs": [
":sphal"
]
diff --git a/apex/mediaswcodec.rc b/apex/mediaswcodec.rc
index d17481b..0c9b8c8 100644
--- a/apex/mediaswcodec.rc
+++ b/apex/mediaswcodec.rc
@@ -2,6 +2,5 @@
class main
user mediacodec
group camera drmrpc mediadrm
- override
ioprio rt 4
writepid /dev/cpuset/foreground/tasks
diff --git a/apex/mediatranscoding.rc b/apex/mediatranscoding.rc
new file mode 100644
index 0000000..ae9f8ba
--- /dev/null
+++ b/apex/mediatranscoding.rc
@@ -0,0 +1,12 @@
+# media.transcoding service is defined on com.android.media apex which goes back
+# to API29, but we only want it started on API31+ devices. So we declare it as
+# "disabled" and start it explicitly on boot.
+service media.transcoding /apex/com.android.media/bin/mediatranscoding
+ class main
+ user media
+ group media
+ ioprio rt 4
+ # Restrict to little cores only with system-background cpuset.
+ writepid /dev/cpuset/system-background/tasks
+ interface aidl media.transcoding
+ disabled
diff --git a/apex/testing/Android.bp b/apex/testing/Android.bp
index 4ff4d06..8b81090 100644
--- a/apex/testing/Android.bp
+++ b/apex/testing/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_av_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_av_license"],
}
diff --git a/camera/Android.bp b/camera/Android.bp
index 93bc68d..6878c20 100644
--- a/camera/Android.bp
+++ b/camera/Android.bp
@@ -66,10 +66,10 @@
"CameraParameters.cpp",
"CaptureResult.cpp",
"CameraParameters2.cpp",
+ "CameraSessionStats.cpp",
"ICamera.cpp",
"ICameraClient.cpp",
"ICameraRecordingProxy.cpp",
- "ICameraRecordingProxyListener.cpp",
"camera2/CaptureRequest.cpp",
"camera2/ConcurrentCamera.cpp",
"camera2/OutputConfiguration.cpp",
@@ -119,6 +119,8 @@
"aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl",
"aidl/android/hardware/camera2/ICameraDeviceUser.aidl",
"aidl/android/hardware/camera2/ICameraOfflineSession.aidl",
+ "aidl/android/hardware/camera2/ICameraInjectionCallback.aidl",
+ "aidl/android/hardware/camera2/ICameraInjectionSession.aidl",
],
path: "aidl",
}
diff --git a/camera/Camera.cpp b/camera/Camera.cpp
index 84d1d93..f7d194e 100644
--- a/camera/Camera.cpp
+++ b/camera/Camera.cpp
@@ -25,7 +25,6 @@
#include <binder/IMemory.h>
#include <Camera.h>
-#include <ICameraRecordingProxyListener.h>
#include <android/hardware/ICameraService.h>
#include <android/hardware/ICamera.h>
@@ -77,63 +76,6 @@
return CameraBaseT::connect(cameraId, clientPackageName, clientUid, clientPid);
}
-status_t Camera::connectLegacy(int cameraId, int halVersion,
- const String16& clientPackageName,
- int clientUid,
- sp<Camera>& camera)
-{
- ALOGV("%s: connect legacy camera device", __FUNCTION__);
- sp<Camera> c = new Camera(cameraId);
- sp<::android::hardware::ICameraClient> cl = c;
- status_t status = NO_ERROR;
- const sp<::android::hardware::ICameraService>& cs = CameraBaseT::getCameraService();
-
- binder::Status ret;
- if (cs != nullptr) {
- ret = cs.get()->connectLegacy(cl, cameraId, halVersion, clientPackageName,
- clientUid, /*out*/&(c->mCamera));
- }
- if (ret.isOk() && c->mCamera != nullptr) {
- IInterface::asBinder(c->mCamera)->linkToDeath(c);
- c->mStatus = NO_ERROR;
- camera = c;
- } else {
- switch(ret.serviceSpecificErrorCode()) {
- case hardware::ICameraService::ERROR_DISCONNECTED:
- status = -ENODEV;
- break;
- case hardware::ICameraService::ERROR_CAMERA_IN_USE:
- status = -EBUSY;
- break;
- case hardware::ICameraService::ERROR_INVALID_OPERATION:
- status = -EINVAL;
- break;
- case hardware::ICameraService::ERROR_MAX_CAMERAS_IN_USE:
- status = -EUSERS;
- break;
- case hardware::ICameraService::ERROR_ILLEGAL_ARGUMENT:
- status = BAD_VALUE;
- break;
- case hardware::ICameraService::ERROR_DEPRECATED_HAL:
- status = -EOPNOTSUPP;
- break;
- case hardware::ICameraService::ERROR_DISABLED:
- status = -EACCES;
- break;
- case hardware::ICameraService::ERROR_PERMISSION_DENIED:
- status = PERMISSION_DENIED;
- break;
- default:
- status = -EINVAL;
- ALOGW("An error occurred while connecting to camera %d: %s", cameraId,
- (cs != nullptr) ? "Service not available" : ret.toString8().string());
- break;
- }
- c.clear();
- }
- return status;
-}
-
status_t Camera::reconnect()
{
ALOGV("reconnect");
@@ -214,10 +156,6 @@
void Camera::stopRecording()
{
ALOGV("stopRecording");
- {
- Mutex::Autolock _l(mLock);
- mRecordingProxyListener.clear();
- }
sp <::android::hardware::ICamera> c = mCamera;
if (c == 0) return;
c->stopRecording();
@@ -325,12 +263,6 @@
mListener = listener;
}
-void Camera::setRecordingProxyListener(const sp<ICameraRecordingProxyListener>& listener)
-{
- Mutex::Autolock _l(mLock);
- mRecordingProxyListener = listener;
-}
-
void Camera::setPreviewCallbackFlags(int flag)
{
ALOGV("setPreviewCallbackFlags");
@@ -384,19 +316,6 @@
// callback from camera service when timestamped frame is ready
void Camera::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr)
{
- // If recording proxy listener is registered, forward the frame and return.
- // The other listener (mListener) is ignored because the receiver needs to
- // call releaseRecordingFrame.
- sp<ICameraRecordingProxyListener> proxylistener;
- {
- Mutex::Autolock _l(mLock);
- proxylistener = mRecordingProxyListener;
- }
- if (proxylistener != NULL) {
- proxylistener->dataCallbackTimestamp(timestamp, msgType, dataPtr);
- return;
- }
-
sp<CameraListener> listener;
{
Mutex::Autolock _l(mLock);
@@ -413,19 +332,6 @@
void Camera::recordingFrameHandleCallbackTimestamp(nsecs_t timestamp, native_handle_t* handle)
{
- // If recording proxy listener is registered, forward the frame and return.
- // The other listener (mListener) is ignored because the receiver needs to
- // call releaseRecordingFrameHandle.
- sp<ICameraRecordingProxyListener> proxylistener;
- {
- Mutex::Autolock _l(mLock);
- proxylistener = mRecordingProxyListener;
- }
- if (proxylistener != NULL) {
- proxylistener->recordingFrameHandleCallbackTimestamp(timestamp, handle);
- return;
- }
-
sp<CameraListener> listener;
{
Mutex::Autolock _l(mLock);
@@ -444,19 +350,6 @@
const std::vector<nsecs_t>& timestamps,
const std::vector<native_handle_t*>& handles)
{
- // If recording proxy listener is registered, forward the frame and return.
- // The other listener (mListener) is ignored because the receiver needs to
- // call releaseRecordingFrameHandle.
- sp<ICameraRecordingProxyListener> proxylistener;
- {
- Mutex::Autolock _l(mLock);
- proxylistener = mRecordingProxyListener;
- }
- if (proxylistener != NULL) {
- proxylistener->recordingFrameHandleCallbackTimestampBatch(timestamps, handles);
- return;
- }
-
sp<CameraListener> listener;
{
Mutex::Autolock _l(mLock);
@@ -476,10 +369,9 @@
return new RecordingProxy(this);
}
-status_t Camera::RecordingProxy::startRecording(const sp<ICameraRecordingProxyListener>& listener)
+status_t Camera::RecordingProxy::startRecording()
{
ALOGV("RecordingProxy::startRecording");
- mCamera->setRecordingProxyListener(listener);
mCamera->reconnect();
return mCamera->startRecording();
}
@@ -490,23 +382,6 @@
mCamera->stopRecording();
}
-void Camera::RecordingProxy::releaseRecordingFrame(const sp<IMemory>& mem)
-{
- ALOGV("RecordingProxy::releaseRecordingFrame");
- mCamera->releaseRecordingFrame(mem);
-}
-
-void Camera::RecordingProxy::releaseRecordingFrameHandle(native_handle_t* handle) {
- ALOGV("RecordingProxy::releaseRecordingFrameHandle");
- mCamera->releaseRecordingFrameHandle(handle);
-}
-
-void Camera::RecordingProxy::releaseRecordingFrameHandleBatch(
- const std::vector<native_handle_t*>& handles) {
- ALOGV("RecordingProxy::releaseRecordingFrameHandleBatch");
- mCamera->releaseRecordingFrameHandleBatch(handles);
-}
-
Camera::RecordingProxy::RecordingProxy(const sp<Camera>& camera)
{
mCamera = camera;
diff --git a/camera/CameraMetadata.cpp b/camera/CameraMetadata.cpp
index 024311f..a4ae71b 100644
--- a/camera/CameraMetadata.cpp
+++ b/camera/CameraMetadata.cpp
@@ -22,6 +22,7 @@
#include <binder/Parcel.h>
#include <camera/CameraMetadata.h>
+#include <camera_metadata_hidden.h>
namespace android {
@@ -527,6 +528,8 @@
mBuffer = allocate_camera_metadata(newEntryCount,
newDataCount);
if (mBuffer == NULL) {
+ // Maintain old buffer to avoid potential memory leak.
+ mBuffer = oldBuffer;
ALOGE("%s: Can't allocate larger metadata buffer", __FUNCTION__);
return NO_MEMORY;
}
@@ -877,5 +880,8 @@
return OK;
}
+metadata_vendor_id_t CameraMetadata::getVendorId() {
+ return get_camera_metadata_vendor_id(mBuffer);
+}
}; // namespace android
diff --git a/camera/CameraSessionStats.cpp b/camera/CameraSessionStats.cpp
new file mode 100644
index 0000000..28e037f
--- /dev/null
+++ b/camera/CameraSessionStats.cpp
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "CameraSessionStats"
+#include <utils/Log.h>
+#include <utils/String16.h>
+
+#include <camera/CameraSessionStats.h>
+
+#include <binder/Parcel.h>
+
+namespace android {
+namespace hardware {
+
+status_t CameraStreamStats::readFromParcel(const android::Parcel* parcel) {
+ if (parcel == NULL) {
+ ALOGE("%s: Null parcel", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ status_t err = OK;
+
+ int width = 0;
+ if ((err = parcel->readInt32(&width)) != OK) {
+ ALOGE("%s: Failed to read width from parcel", __FUNCTION__);
+ return err;
+ }
+
+ int height = 0;
+ if ((err = parcel->readInt32(&height)) != OK) {
+ ALOGE("%s: Failed to read height from parcel", __FUNCTION__);
+ return err;
+ }
+
+ int format = 0;
+ if ((err = parcel->readInt32(&format)) != OK) {
+ ALOGE("%s: Failed to read format from parcel", __FUNCTION__);
+ return err;
+ }
+
+ int dataSpace = 0;
+ if ((err = parcel->readInt32(&dataSpace)) != OK) {
+ ALOGE("%s: Failed to read dataSpace from parcel", __FUNCTION__);
+ return err;
+ }
+
+ int64_t usage = 0;
+ if ((err = parcel->readInt64(&usage)) != OK) {
+ ALOGE("%s: Failed to read usage from parcel", __FUNCTION__);
+ return err;
+ }
+
+ int64_t requestCount = 0;
+ if ((err = parcel->readInt64(&requestCount)) != OK) {
+ ALOGE("%s: Failed to read request count from parcel", __FUNCTION__);
+ return err;
+ }
+
+ int64_t errorCount = 0;
+ if ((err = parcel->readInt64(&errorCount)) != OK) {
+ ALOGE("%s: Failed to read error count from parcel", __FUNCTION__);
+ return err;
+ }
+
+ int startLatencyMs = 0;
+ if ((err = parcel->readInt32(&startLatencyMs)) != OK) {
+ ALOGE("%s: Failed to read start latency from parcel", __FUNCTION__);
+ return err;
+ }
+
+ int maxHalBuffers = 0;
+ if ((err = parcel->readInt32(&maxHalBuffers)) != OK) {
+ ALOGE("%s: Failed to read max Hal buffers from parcel", __FUNCTION__);
+ return err;
+ }
+
+ int maxAppBuffers = 0;
+ if ((err = parcel->readInt32(&maxAppBuffers)) != OK) {
+ ALOGE("%s: Failed to read max app buffers from parcel", __FUNCTION__);
+ return err;
+ }
+
+ int histogramType = HISTOGRAM_TYPE_UNKNOWN;
+ if ((err = parcel->readInt32(&histogramType)) != OK) {
+ ALOGE("%s: Failed to read histogram type from parcel", __FUNCTION__);
+ return err;
+ }
+
+ std::vector<float> histogramBins;
+ if ((err = parcel->readFloatVector(&histogramBins)) != OK) {
+ ALOGE("%s: Failed to read histogram bins from parcel", __FUNCTION__);
+ return err;
+ }
+
+ std::vector<int64_t> histogramCounts;
+ if ((err = parcel->readInt64Vector(&histogramCounts)) != OK) {
+ ALOGE("%s: Failed to read histogram counts from parcel", __FUNCTION__);
+ return err;
+ }
+
+ mWidth = width;
+ mHeight = height;
+ mFormat = format;
+ mDataSpace = dataSpace;
+ mUsage = usage;
+ mRequestCount = requestCount;
+ mErrorCount = errorCount;
+ mStartLatencyMs = startLatencyMs;
+ mMaxHalBuffers = maxHalBuffers;
+ mMaxAppBuffers = maxAppBuffers;
+ mHistogramType = histogramType;
+ mHistogramBins = std::move(histogramBins);
+ mHistogramCounts = std::move(histogramCounts);
+
+ return OK;
+}
+
+status_t CameraStreamStats::writeToParcel(android::Parcel* parcel) const {
+ if (parcel == NULL) {
+ ALOGE("%s: Null parcel", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ status_t err = OK;
+
+ if ((err = parcel->writeInt32(mWidth)) != OK) {
+ ALOGE("%s: Failed to write stream width!", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeInt32(mHeight)) != OK) {
+ ALOGE("%s: Failed to write stream height!", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeInt32(mFormat)) != OK) {
+ ALOGE("%s: Failed to write stream format!", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeInt32(mDataSpace)) != OK) {
+ ALOGE("%s: Failed to write stream dataSpace!", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeInt64(mUsage)) != OK) {
+ ALOGE("%s: Failed to write stream usage!", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeInt64(mRequestCount)) != OK) {
+ ALOGE("%s: Failed to write stream request count!", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeInt64(mErrorCount)) != OK) {
+ ALOGE("%s: Failed to write stream error count!", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeInt32(mStartLatencyMs)) != OK) {
+ ALOGE("%s: Failed to write stream start latency!", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeInt32(mMaxHalBuffers)) != OK) {
+ ALOGE("%s: Failed to write max hal buffers", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeInt32(mMaxAppBuffers)) != OK) {
+ ALOGE("%s: Failed to write max app buffers", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeInt32(mHistogramType)) != OK) {
+ ALOGE("%s: Failed to write histogram type", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeFloatVector(mHistogramBins)) != OK) {
+ ALOGE("%s: Failed to write histogram bins!", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeInt64Vector(mHistogramCounts)) != OK) {
+ ALOGE("%s: Failed to write histogram counts!", __FUNCTION__);
+ return err;
+ }
+
+ return OK;
+}
+
+const int CameraSessionStats::CAMERA_STATE_OPEN = 0;
+const int CameraSessionStats::CAMERA_STATE_ACTIVE = 1;
+const int CameraSessionStats::CAMERA_STATE_IDLE = 2;
+const int CameraSessionStats::CAMERA_STATE_CLOSED = 3;
+
+const int CameraSessionStats::CAMERA_FACING_BACK = 0;
+const int CameraSessionStats::CAMERA_FACING_FRONT = 1;
+const int CameraSessionStats::CAMERA_FACING_EXTERNAL = 2;
+
+const int CameraSessionStats::CAMERA_API_LEVEL_1 = 1;
+const int CameraSessionStats::CAMERA_API_LEVEL_2 = 2;
+
+CameraSessionStats::CameraSessionStats() :
+ mFacing(CAMERA_FACING_BACK),
+ mNewCameraState(CAMERA_STATE_CLOSED),
+ mApiLevel(0),
+ mIsNdk(false),
+ mLatencyMs(-1),
+ mSessionType(0),
+ mInternalReconfigure(0),
+ mRequestCount(0),
+ mResultErrorCount(0),
+ mDeviceError(false) {}
+
+CameraSessionStats::CameraSessionStats(const String16& cameraId,
+ int facing, int newCameraState, const String16& clientName,
+ int apiLevel, bool isNdk, int32_t latencyMs) :
+ mCameraId(cameraId),
+ mFacing(facing),
+ mNewCameraState(newCameraState),
+ mClientName(clientName),
+ mApiLevel(apiLevel),
+ mIsNdk(isNdk),
+ mLatencyMs(latencyMs),
+ mSessionType(0),
+ mInternalReconfigure(0),
+ mRequestCount(0),
+ mResultErrorCount(0),
+ mDeviceError(0) {}
+
+status_t CameraSessionStats::readFromParcel(const android::Parcel* parcel) {
+ if (parcel == NULL) {
+ ALOGE("%s: Null parcel", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ status_t err = OK;
+
+ String16 id;
+ if ((err = parcel->readString16(&id)) != OK) {
+ ALOGE("%s: Failed to read camera id!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ int facing = 0;
+ if ((err = parcel->readInt32(&facing)) != OK) {
+ ALOGE("%s: Failed to read camera facing from parcel", __FUNCTION__);
+ return err;
+ }
+
+ int32_t newCameraState;
+ if ((err = parcel->readInt32(&newCameraState)) != OK) {
+ ALOGE("%s: Failed to read new camera state from parcel", __FUNCTION__);
+ return err;
+ }
+
+ String16 clientName;
+ if ((err = parcel->readString16(&clientName)) != OK) {
+ ALOGE("%s: Failed to read client name!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ int32_t apiLevel;
+ if ((err = parcel->readInt32(&apiLevel)) != OK) {
+ ALOGE("%s: Failed to read api level from parcel", __FUNCTION__);
+ return err;
+ }
+
+ bool isNdk;
+ if ((err = parcel->readBool(&isNdk)) != OK) {
+ ALOGE("%s: Failed to read isNdk flag from parcel", __FUNCTION__);
+ return err;
+ }
+
+ int32_t latencyMs;
+ if ((err = parcel->readInt32(&latencyMs)) != OK) {
+ ALOGE("%s: Failed to read latencyMs from parcel", __FUNCTION__);
+ return err;
+ }
+
+ int32_t sessionType;
+ if ((err = parcel->readInt32(&sessionType)) != OK) {
+ ALOGE("%s: Failed to read session type from parcel", __FUNCTION__);
+ return err;
+ }
+
+ int32_t internalReconfigure;
+ if ((err = parcel->readInt32(&internalReconfigure)) != OK) {
+ ALOGE("%s: Failed to read internal reconfigure count from parcel", __FUNCTION__);
+ return err;
+ }
+
+ int64_t requestCount;
+ if ((err = parcel->readInt64(&requestCount)) != OK) {
+ ALOGE("%s: Failed to read request count from parcel", __FUNCTION__);
+ return err;
+ }
+
+ int64_t resultErrorCount;
+ if ((err = parcel->readInt64(&resultErrorCount)) != OK) {
+ ALOGE("%s: Failed to read result error count from parcel", __FUNCTION__);
+ return err;
+ }
+
+ bool deviceError;
+ if ((err = parcel->readBool(&deviceError)) != OK) {
+ ALOGE("%s: Failed to read device error flag from parcel", __FUNCTION__);
+ return err;
+ }
+
+ std::vector<CameraStreamStats> streamStats;
+ if ((err = parcel->readParcelableVector(&streamStats)) != OK) {
+ ALOGE("%s: Failed to read stream state from parcel", __FUNCTION__);
+ return err;
+ }
+
+ mCameraId = id;
+ mFacing = facing;
+ mNewCameraState = newCameraState;
+ mClientName = clientName;
+ mApiLevel = apiLevel;
+ mIsNdk = isNdk;
+ mLatencyMs = latencyMs;
+ mSessionType = sessionType;
+ mInternalReconfigure = internalReconfigure;
+ mRequestCount = requestCount;
+ mResultErrorCount = resultErrorCount;
+ mDeviceError = deviceError;
+ mStreamStats = std::move(streamStats);
+
+ return OK;
+}
+
+status_t CameraSessionStats::writeToParcel(android::Parcel* parcel) const {
+ if (parcel == NULL) {
+ ALOGE("%s: Null parcel", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ status_t err = OK;
+
+ if ((err = parcel->writeString16(mCameraId)) != OK) {
+ ALOGE("%s: Failed to write camera id!", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeInt32(mFacing)) != OK) {
+ ALOGE("%s: Failed to write camera facing!", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeInt32(mNewCameraState)) != OK) {
+ ALOGE("%s: Failed to write new camera state!", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeString16(mClientName)) != OK) {
+ ALOGE("%s: Failed to write client name!", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeInt32(mApiLevel)) != OK) {
+ ALOGE("%s: Failed to write api level!", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeBool(mIsNdk)) != OK) {
+ ALOGE("%s: Failed to write isNdk flag!", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeInt32(mLatencyMs)) != OK) {
+ ALOGE("%s: Failed to write latency in Ms!", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeInt32(mSessionType)) != OK) {
+ ALOGE("%s: Failed to write session type!", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeInt32(mInternalReconfigure)) != OK) {
+ ALOGE("%s: Failed to write internal reconfigure count!", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeInt64(mRequestCount)) != OK) {
+ ALOGE("%s: Failed to write request count!", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeInt64(mResultErrorCount)) != OK) {
+ ALOGE("%s: Failed to write result error count!", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeBool(mDeviceError)) != OK) {
+ ALOGE("%s: Failed to write device error flag!", __FUNCTION__);
+ return err;
+ }
+
+ if ((err = parcel->writeParcelableVector(mStreamStats)) != OK) {
+ ALOGE("%s: Failed to write stream states!", __FUNCTION__);
+ return err;
+ }
+
+ return OK;
+}
+
+} // namespace hardware
+} // namesmpace android
diff --git a/camera/ICameraRecordingProxy.cpp b/camera/ICameraRecordingProxy.cpp
index bd6af75..97523a5 100644
--- a/camera/ICameraRecordingProxy.cpp
+++ b/camera/ICameraRecordingProxy.cpp
@@ -18,7 +18,6 @@
#define LOG_TAG "ICameraRecordingProxy"
#include <camera/CameraUtils.h>
#include <camera/ICameraRecordingProxy.h>
-#include <camera/ICameraRecordingProxyListener.h>
#include <binder/IMemory.h>
#include <binder/Parcel.h>
#include <media/hardware/HardwareAPI.h>
@@ -29,10 +28,7 @@
enum {
START_RECORDING = IBinder::FIRST_CALL_TRANSACTION,
- STOP_RECORDING,
- RELEASE_RECORDING_FRAME,
- RELEASE_RECORDING_FRAME_HANDLE,
- RELEASE_RECORDING_FRAME_HANDLE_BATCH,
+ STOP_RECORDING
};
@@ -44,12 +40,11 @@
{
}
- status_t startRecording(const sp<ICameraRecordingProxyListener>& listener)
+ status_t startRecording()
{
ALOGV("startRecording");
Parcel data, reply;
data.writeInterfaceToken(ICameraRecordingProxy::getInterfaceDescriptor());
- data.writeStrongBinder(IInterface::asBinder(listener));
remote()->transact(START_RECORDING, data, &reply);
return reply.readInt32();
}
@@ -61,46 +56,6 @@
data.writeInterfaceToken(ICameraRecordingProxy::getInterfaceDescriptor());
remote()->transact(STOP_RECORDING, data, &reply);
}
-
- void releaseRecordingFrame(const sp<IMemory>& mem)
- {
- ALOGV("releaseRecordingFrame");
- Parcel data, reply;
- data.writeInterfaceToken(ICameraRecordingProxy::getInterfaceDescriptor());
- data.writeStrongBinder(IInterface::asBinder(mem));
- remote()->transact(RELEASE_RECORDING_FRAME, data, &reply);
- }
-
- void releaseRecordingFrameHandle(native_handle_t *handle) {
- ALOGV("releaseRecordingFrameHandle");
- Parcel data, reply;
- data.writeInterfaceToken(ICameraRecordingProxy::getInterfaceDescriptor());
- data.writeNativeHandle(handle);
-
- remote()->transact(RELEASE_RECORDING_FRAME_HANDLE, data, &reply);
-
- // Close the native handle because camera received a dup copy.
- native_handle_close(handle);
- native_handle_delete(handle);
- }
-
- void releaseRecordingFrameHandleBatch(const std::vector<native_handle_t*>& handles) {
- ALOGV("releaseRecordingFrameHandleBatch");
- Parcel data, reply;
- data.writeInterfaceToken(ICameraRecordingProxy::getInterfaceDescriptor());
- uint32_t n = handles.size();
- data.writeUint32(n);
- for (auto& handle : handles) {
- data.writeNativeHandle(handle);
- }
- remote()->transact(RELEASE_RECORDING_FRAME_HANDLE_BATCH, data, &reply);
-
- // Close the native handle because camera received a dup copy.
- for (auto& handle : handles) {
- native_handle_close(handle);
- native_handle_delete(handle);
- }
- }
};
IMPLEMENT_META_INTERFACE(CameraRecordingProxy, "android.hardware.ICameraRecordingProxy");
@@ -114,9 +69,7 @@
case START_RECORDING: {
ALOGV("START_RECORDING");
CHECK_INTERFACE(ICameraRecordingProxy, data, reply);
- sp<ICameraRecordingProxyListener> listener =
- interface_cast<ICameraRecordingProxyListener>(data.readStrongBinder());
- reply->writeInt32(startRecording(listener));
+ reply->writeInt32(startRecording());
return NO_ERROR;
} break;
case STOP_RECORDING: {
@@ -125,46 +78,6 @@
stopRecording();
return NO_ERROR;
} break;
- case RELEASE_RECORDING_FRAME: {
- ALOGV("RELEASE_RECORDING_FRAME");
- CHECK_INTERFACE(ICameraRecordingProxy, data, reply);
- sp<IMemory> mem = interface_cast<IMemory>(data.readStrongBinder());
- releaseRecordingFrame(mem);
- return NO_ERROR;
- } break;
- case RELEASE_RECORDING_FRAME_HANDLE: {
- ALOGV("RELEASE_RECORDING_FRAME_HANDLE");
- CHECK_INTERFACE(ICameraRecordingProxy, data, reply);
-
- // releaseRecordingFrameHandle will be responsble to close the native handle.
- releaseRecordingFrameHandle(data.readNativeHandle());
- return NO_ERROR;
- } break;
- case RELEASE_RECORDING_FRAME_HANDLE_BATCH: {
- ALOGV("RELEASE_RECORDING_FRAME_HANDLE_BATCH");
- CHECK_INTERFACE(ICameraRecordingProxy, data, reply);
- uint32_t n = 0;
- status_t res = data.readUint32(&n);
- if (res != OK) {
- ALOGE("%s: Failed to read batch size: %s (%d)", __FUNCTION__, strerror(-res), res);
- return BAD_VALUE;
- }
- std::vector<native_handle_t*> handles;
- handles.reserve(n);
- for (uint32_t i = 0; i < n; i++) {
- native_handle_t* handle = data.readNativeHandle();
- if (handle == nullptr) {
- ALOGE("%s: Received a null native handle at handles[%d]",
- __FUNCTION__, i);
- return BAD_VALUE;
- }
- handles.push_back(handle);
- }
-
- // releaseRecordingFrameHandleBatch will be responsble to close the native handle.
- releaseRecordingFrameHandleBatch(handles);
- return NO_ERROR;
- } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
@@ -173,4 +86,3 @@
// ----------------------------------------------------------------------------
}; // namespace android
-
diff --git a/camera/ICameraRecordingProxyListener.cpp b/camera/ICameraRecordingProxyListener.cpp
deleted file mode 100644
index 66faf8f..0000000
--- a/camera/ICameraRecordingProxyListener.cpp
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 "ICameraRecordingProxyListener"
-#include <camera/CameraUtils.h>
-#include <camera/ICameraRecordingProxyListener.h>
-#include <binder/IMemory.h>
-#include <binder/Parcel.h>
-#include <media/hardware/HardwareAPI.h>
-#include <utils/Log.h>
-
-namespace android {
-
-enum {
- DATA_CALLBACK_TIMESTAMP = IBinder::FIRST_CALL_TRANSACTION,
- RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP,
- RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP_BATCH
-};
-
-class BpCameraRecordingProxyListener: public BpInterface<ICameraRecordingProxyListener>
-{
-public:
- explicit BpCameraRecordingProxyListener(const sp<IBinder>& impl)
- : BpInterface<ICameraRecordingProxyListener>(impl)
- {
- }
-
- void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& imageData)
- {
- ALOGV("dataCallback");
- Parcel data, reply;
- data.writeInterfaceToken(ICameraRecordingProxyListener::getInterfaceDescriptor());
- data.writeInt64(timestamp);
- data.writeInt32(msgType);
- data.writeStrongBinder(IInterface::asBinder(imageData));
- remote()->transact(DATA_CALLBACK_TIMESTAMP, data, &reply, IBinder::FLAG_ONEWAY);
- }
-
- void recordingFrameHandleCallbackTimestamp(nsecs_t timestamp, native_handle_t* handle) {
- ALOGV("recordingFrameHandleCallbackTimestamp");
- Parcel data, reply;
- data.writeInterfaceToken(ICameraRecordingProxyListener::getInterfaceDescriptor());
- data.writeInt64(timestamp);
- data.writeNativeHandle(handle);
- remote()->transact(RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP, data, &reply,
- IBinder::FLAG_ONEWAY);
-
- // The native handle is dupped in ICameraClient so we need to free it here.
- native_handle_close(handle);
- native_handle_delete(handle);
- }
-
- void recordingFrameHandleCallbackTimestampBatch(
- const std::vector<nsecs_t>& timestamps,
- const std::vector<native_handle_t*>& handles) {
- ALOGV("recordingFrameHandleCallbackTimestampBatch");
- Parcel data, reply;
- data.writeInterfaceToken(ICameraRecordingProxyListener::getInterfaceDescriptor());
-
- uint32_t n = timestamps.size();
- if (n != handles.size()) {
- ALOGE("%s: size of timestamps(%zu) and handles(%zu) mismatch!",
- __FUNCTION__, timestamps.size(), handles.size());
- return;
- }
- data.writeUint32(n);
- for (auto ts : timestamps) {
- data.writeInt64(ts);
- }
- for (auto& handle : handles) {
- data.writeNativeHandle(handle);
- }
- remote()->transact(RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP_BATCH, data, &reply,
- IBinder::FLAG_ONEWAY);
-
- // The native handle is dupped in ICameraClient so we need to free it here.
- for (auto& handle : handles) {
- native_handle_close(handle);
- native_handle_delete(handle);
- }
- }
-};
-
-IMPLEMENT_META_INTERFACE(CameraRecordingProxyListener, "android.hardware.ICameraRecordingProxyListener");
-
-// ----------------------------------------------------------------------
-
-status_t BnCameraRecordingProxyListener::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch(code) {
- case DATA_CALLBACK_TIMESTAMP: {
- ALOGV("DATA_CALLBACK_TIMESTAMP");
- CHECK_INTERFACE(ICameraRecordingProxyListener, data, reply);
- nsecs_t timestamp = data.readInt64();
- int32_t msgType = data.readInt32();
- sp<IMemory> imageData = interface_cast<IMemory>(data.readStrongBinder());
- dataCallbackTimestamp(timestamp, msgType, imageData);
- return NO_ERROR;
- } break;
- case RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP: {
- ALOGV("RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP");
- CHECK_INTERFACE(ICameraRecordingProxyListener, data, reply);
- nsecs_t timestamp;
- status_t res = data.readInt64(×tamp);
- if (res != OK) {
- ALOGE("%s: Failed to read timestamp: %s (%d)", __FUNCTION__, strerror(-res), res);
- return BAD_VALUE;
- }
-
- native_handle_t* handle = data.readNativeHandle();
- if (handle == nullptr) {
- ALOGE("%s: Received a null native handle", __FUNCTION__);
- return BAD_VALUE;
- }
- // The native handle will be freed in
- // BpCameraRecordingProxy::releaseRecordingFrameHandle.
- recordingFrameHandleCallbackTimestamp(timestamp, handle);
- return NO_ERROR;
- } break;
- case RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP_BATCH: {
- ALOGV("RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP_BATCH");
- CHECK_INTERFACE(ICameraRecordingProxyListener, data, reply);
- uint32_t n = 0;
- status_t res = data.readUint32(&n);
- if (res != OK) {
- ALOGE("%s: Failed to read batch size: %s (%d)", __FUNCTION__, strerror(-res), res);
- return BAD_VALUE;
- }
- std::vector<nsecs_t> timestamps;
- std::vector<native_handle_t*> handles;
- timestamps.reserve(n);
- handles.reserve(n);
- for (uint32_t i = 0; i < n; i++) {
- nsecs_t t;
- res = data.readInt64(&t);
- if (res != OK) {
- ALOGE("%s: Failed to read timestamp[%d]: %s (%d)",
- __FUNCTION__, i, strerror(-res), res);
- return BAD_VALUE;
- }
- timestamps.push_back(t);
- }
- for (uint32_t i = 0; i < n; i++) {
- native_handle_t* handle = data.readNativeHandle();
- if (handle == nullptr) {
- ALOGE("%s: Received a null native handle at handles[%d]",
- __FUNCTION__, i);
- return BAD_VALUE;
- }
- handles.push_back(handle);
- }
- // The native handle will be freed in
- // BpCameraRecordingProxy::releaseRecordingFrameHandleBatch.
- recordingFrameHandleCallbackTimestampBatch(timestamps, handles);
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
diff --git a/camera/TEST_MAPPING b/camera/TEST_MAPPING
new file mode 100644
index 0000000..683e183
--- /dev/null
+++ b/camera/TEST_MAPPING
@@ -0,0 +1,11 @@
+{
+ "postsubmit": [
+ {
+ "name": "CtsCameraTestCases"
+ },
+ {
+ "name": "CtsCameraTestCases",
+ "keywords": ["primary-device"]
+ }
+ ]
+}
diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp
index d713d2d..24fa912 100644
--- a/camera/VendorTagDescriptor.cpp
+++ b/camera/VendorTagDescriptor.cpp
@@ -660,6 +660,16 @@
return sGlobalVendorTagDescriptorCache;
}
+bool VendorTagDescriptorCache::isVendorCachePresent(metadata_vendor_id_t vendorId) {
+ Mutex::Autolock al(sLock);
+ if ((sGlobalVendorTagDescriptorCache.get() != nullptr) &&
+ (sGlobalVendorTagDescriptorCache->getVendorIdsAndTagDescriptors().find(vendorId) !=
+ sGlobalVendorTagDescriptorCache->getVendorIdsAndTagDescriptors().end())) {
+ return true;
+ }
+ return false;
+}
+
extern "C" {
int vendor_tag_descriptor_get_tag_count(const vendor_tag_ops_t* /*v*/) {
diff --git a/camera/aidl/android/hardware/CameraSessionStats.aidl b/camera/aidl/android/hardware/CameraSessionStats.aidl
new file mode 100644
index 0000000..a8e6774
--- /dev/null
+++ b/camera/aidl/android/hardware/CameraSessionStats.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+/** @hide */
+parcelable CameraSessionStats cpp_header "camera/CameraSessionStats.h";
diff --git a/camera/aidl/android/hardware/ICameraService.aidl b/camera/aidl/android/hardware/ICameraService.aidl
index ac7a35b..873d738 100644
--- a/camera/aidl/android/hardware/ICameraService.aidl
+++ b/camera/aidl/android/hardware/ICameraService.aidl
@@ -20,6 +20,8 @@
import android.hardware.ICameraClient;
import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.ICameraDeviceCallbacks;
+import android.hardware.camera2.ICameraInjectionCallback;
+import android.hardware.camera2.ICameraInjectionSession;
import android.hardware.camera2.params.VendorTagDescriptor;
import android.hardware.camera2.params.VendorTagDescriptorCache;
import android.hardware.camera2.utils.ConcurrentCameraIdCombination;
@@ -69,7 +71,7 @@
/**
* Default UID/PID values for non-privileged callers of
- * connect(), connectDevice(), and connectLegacy()
+ * connect() and connectDevice()
*/
const int USE_CALLING_UID = -1;
const int USE_CALLING_PID = -1;
@@ -93,20 +95,6 @@
int clientUid);
/**
- * halVersion constant for connectLegacy
- */
- const int CAMERA_HAL_API_VERSION_UNSPECIFIED = -1;
-
- /**
- * Open a camera device in legacy mode, if supported by the camera module HAL.
- */
- ICamera connectLegacy(ICameraClient client,
- int cameraId,
- int halVersion,
- String opPackageName,
- int clientUid);
-
- /**
* Add listener for changes to camera device and flashlight state.
*
* Also returns the set of currently-known camera IDs and state of each device.
@@ -175,6 +163,9 @@
boolean supportsCameraApi(String cameraId, int apiVersion);
// Determines if a cameraId is a hidden physical camera of a logical multi-camera.
boolean isHiddenPhysicalCamera(String cameraId);
+ // Inject the external camera to replace the internal camera session.
+ ICameraInjectionSession injectCamera(String packageName, String internalCamId,
+ String externalCamId, in ICameraInjectionCallback CameraInjectionCallback);
void setTorchMode(String cameraId, boolean enabled, IBinder clientBinder);
@@ -188,6 +179,13 @@
oneway void notifySystemEvent(int eventId, in int[] args);
/**
+ * Notify the camera service of a display configuration change.
+ *
+ * Callers require the android.permission.CAMERA_SEND_SYSTEM_EVENTS permission.
+ */
+ oneway void notifyDisplayConfigurationChange();
+
+ /**
* Notify the camera service of a device physical status change. May only be called from
* a privileged process.
*
diff --git a/camera/aidl/android/hardware/ICameraServiceProxy.aidl b/camera/aidl/android/hardware/ICameraServiceProxy.aidl
index 7575948..bbb0289 100644
--- a/camera/aidl/android/hardware/ICameraServiceProxy.aidl
+++ b/camera/aidl/android/hardware/ICameraServiceProxy.aidl
@@ -16,11 +16,11 @@
package android.hardware;
+import android.hardware.CameraSessionStats;
+
/**
* Binder interface for the camera service proxy running in system_server.
*
- * Keep in sync with frameworks/av/include/camera/ICameraServiceProxy.h
- *
* @hide
*/
interface ICameraServiceProxy
@@ -30,30 +30,15 @@
*/
oneway void pingForUserUpdate();
- /**
- * Values for notifyCameraState newCameraState
- */
- const int CAMERA_STATE_OPEN = 0;
- const int CAMERA_STATE_ACTIVE = 1;
- const int CAMERA_STATE_IDLE = 2;
- const int CAMERA_STATE_CLOSED = 3;
-
- /**
- * Values for notifyCameraState facing
- */
- const int CAMERA_FACING_BACK = 0;
- const int CAMERA_FACING_FRONT = 1;
- const int CAMERA_FACING_EXTERNAL = 2;
-
- /**
- * Values for notifyCameraState api level
- */
- const int CAMERA_API_LEVEL_1 = 1;
- const int CAMERA_API_LEVEL_2 = 2;
/**
* Update the status of a camera device.
*/
- oneway void notifyCameraState(String cameraId, int facing, int newCameraState,
- String clientName, int apiLevel);
+ oneway void notifyCameraState(in CameraSessionStats cameraSessionStats);
+
+ /**
+ * Reports whether the top activity needs a rotate and crop override.
+ */
+ boolean isRotateAndCropOverrideNeeded(String packageName, int sensorOrientation,
+ int lensFacing);
}
diff --git a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
index b183ccc..8e1fcc0 100644
--- a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
+++ b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
@@ -83,9 +83,11 @@
* @param operatingMode The kind of session to create; either NORMAL_MODE or
* CONSTRAINED_HIGH_SPEED_MODE. Must be a non-negative value.
* @param sessionParams Session wide camera parameters
+ * @param startTimeMs The timestamp of session creation start, measured by
+ * SystemClock.uptimeMillis.
* @return a list of stream ids that can be used in offline mode via "switchToOffline"
*/
- int[] endConfigure(int operatingMode, in CameraMetadataNative sessionParams);
+ int[] endConfigure(int operatingMode, in CameraMetadataNative sessionParams, long startTimeMs);
/**
* Check whether a particular session configuration has camera device
@@ -117,10 +119,11 @@
* @param width Width of the input buffers
* @param height Height of the input buffers
* @param format Format of the input buffers. One of HAL_PIXEL_FORMAT_*.
+ * @param isMultiResolution Whether the input stream supports variable resolution image.
*
* @return new stream ID
*/
- int createInputStream(int width, int height, int format);
+ int createInputStream(int width, int height, int format, boolean isMultiResolution);
/**
* Get the surface of the input stream.
diff --git a/camera/aidl/android/hardware/camera2/ICameraInjectionCallback.aidl b/camera/aidl/android/hardware/camera2/ICameraInjectionCallback.aidl
new file mode 100644
index 0000000..9791352
--- /dev/null
+++ b/camera/aidl/android/hardware/camera2/ICameraInjectionCallback.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2;
+
+import android.hardware.camera2.ICameraInjectionSession;
+
+/**
+ * Binder interface used to call back the error state injected by the external camera,
+ * and camera service can be switched back to internal camera when binder signals process death.
+ *
+ * @hide
+ */
+interface ICameraInjectionCallback
+{
+ // Error codes for onInjectionError
+ // To indicate all invalid error codes
+ const int ERROR_INJECTION_INVALID_ERROR = -1;
+ // To indicate the camera injection session has encountered a fatal error, such as injection
+ // init failure, configure failure or injecting failure etc.
+ const int ERROR_INJECTION_SESSION = 0;
+ // To indicate the camera service has encountered a fatal error.
+ const int ERROR_INJECTION_SERVICE = 1;
+ // To indicate the injection camera does not support certain camera functions, such as
+ // unsupport stream format, no capture/record function or no multi-camera function etc.
+ // When this error occurs, the default processing is still in the inject state, and the app is
+ // notified to display an error message and a black screen.
+ const int ERROR_INJECTION_UNSUPPORTED = 2;
+
+ oneway void onInjectionError(int errorCode);
+}
diff --git a/camera/aidl/android/hardware/camera2/ICameraInjectionSession.aidl b/camera/aidl/android/hardware/camera2/ICameraInjectionSession.aidl
new file mode 100644
index 0000000..c31c30b
--- /dev/null
+++ b/camera/aidl/android/hardware/camera2/ICameraInjectionSession.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2;
+
+/** @hide */
+interface ICameraInjectionSession
+{
+ oneway void stopInjection();
+}
diff --git a/camera/camera2/OutputConfiguration.cpp b/camera/camera2/OutputConfiguration.cpp
index 4e9b27d..d6642f3 100644
--- a/camera/camera2/OutputConfiguration.cpp
+++ b/camera/camera2/OutputConfiguration.cpp
@@ -68,6 +68,14 @@
return mPhysicalCameraId;
}
+bool OutputConfiguration::isMultiResolution() const {
+ return mIsMultiResolution;
+}
+
+const std::vector<int32_t> &OutputConfiguration::getSensorPixelModesUsed() const {
+ return mSensorPixelModesUsed;
+}
+
OutputConfiguration::OutputConfiguration() :
mRotation(INVALID_ROTATION),
mSurfaceSetID(INVALID_SET_ID),
@@ -75,7 +83,8 @@
mWidth(0),
mHeight(0),
mIsDeferred(false),
- mIsShared(false) {
+ mIsShared(false),
+ mIsMultiResolution(false) {
}
OutputConfiguration::OutputConfiguration(const android::Parcel& parcel) :
@@ -145,6 +154,17 @@
parcel->readString16(&mPhysicalCameraId);
+ int isMultiResolution = 0;
+ if ((err = parcel->readInt32(&isMultiResolution)) != OK) {
+ ALOGE("%s: Failed to read surface isMultiResolution flag from parcel", __FUNCTION__);
+ return err;
+ }
+
+ std::vector<int32_t> sensorPixelModesUsed;
+ if ((err = parcel->readParcelableVector(&sensorPixelModesUsed)) != OK) {
+ ALOGE("%s: Failed to read sensor pixel mode(s) from parcel", __FUNCTION__);
+ return err;
+ }
mRotation = rotation;
mSurfaceSetID = setID;
mSurfaceType = surfaceType;
@@ -152,6 +172,7 @@
mHeight = height;
mIsDeferred = isDeferred != 0;
mIsShared = isShared != 0;
+ mIsMultiResolution = isMultiResolution != 0;
for (auto& surface : surfaceShims) {
ALOGV("%s: OutputConfiguration: %p, name %s", __FUNCTION__,
surface.graphicBufferProducer.get(),
@@ -159,9 +180,11 @@
mGbps.push_back(surface.graphicBufferProducer);
}
+ mSensorPixelModesUsed = std::move(sensorPixelModesUsed);
+
ALOGV("%s: OutputConfiguration: rotation = %d, setId = %d, surfaceType = %d,"
- " physicalCameraId = %s", __FUNCTION__, mRotation, mSurfaceSetID,
- mSurfaceType, String8(mPhysicalCameraId).string());
+ " physicalCameraId = %s, isMultiResolution = %d", __FUNCTION__, mRotation,
+ mSurfaceSetID, mSurfaceType, String8(mPhysicalCameraId).string(), mIsMultiResolution);
return err;
}
@@ -175,6 +198,7 @@
mIsDeferred = false;
mIsShared = isShared;
mPhysicalCameraId = physicalId;
+ mIsMultiResolution = false;
}
OutputConfiguration::OutputConfiguration(
@@ -183,7 +207,7 @@
int width, int height, bool isShared)
: mGbps(gbps), mRotation(rotation), mSurfaceSetID(surfaceSetID), mSurfaceType(surfaceType),
mWidth(width), mHeight(height), mIsDeferred(false), mIsShared(isShared),
- mPhysicalCameraId(physicalCameraId) { }
+ mPhysicalCameraId(physicalCameraId), mIsMultiResolution(false) { }
status_t OutputConfiguration::writeToParcel(android::Parcel* parcel) const {
@@ -224,24 +248,54 @@
err = parcel->writeString16(mPhysicalCameraId);
if (err != OK) return err;
+ err = parcel->writeInt32(mIsMultiResolution ? 1 : 0);
+ if (err != OK) return err;
+
+ err = parcel->writeParcelableVector(mSensorPixelModesUsed);
+ if (err != OK) return err;
+
return OK;
}
+template <typename T>
+static bool simpleVectorsEqual(T first, T second) {
+ if (first.size() != second.size()) {
+ return false;
+ }
+
+ for (size_t i = 0; i < first.size(); i++) {
+ if (first[i] != second[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
bool OutputConfiguration::gbpsEqual(const OutputConfiguration& other) const {
const std::vector<sp<IGraphicBufferProducer> >& otherGbps =
other.getGraphicBufferProducers();
+ return simpleVectorsEqual(otherGbps, mGbps);
+}
- if (mGbps.size() != otherGbps.size()) {
- return false;
+bool OutputConfiguration::sensorPixelModesUsedEqual(const OutputConfiguration& other) const {
+ const std::vector<int32_t>& othersensorPixelModesUsed = other.getSensorPixelModesUsed();
+ return simpleVectorsEqual(othersensorPixelModesUsed, mSensorPixelModesUsed);
+}
+
+bool OutputConfiguration::sensorPixelModesUsedLessThan(const OutputConfiguration& other) const {
+ const std::vector<int32_t>& spms = other.getSensorPixelModesUsed();
+
+ if (mSensorPixelModesUsed.size() != spms.size()) {
+ return mSensorPixelModesUsed.size() < spms.size();
}
- for (size_t i = 0; i < mGbps.size(); i++) {
- if (mGbps[i] != otherGbps[i]) {
- return false;
+ for (size_t i = 0; i < spms.size(); i++) {
+ if (mSensorPixelModesUsed[i] != spms[i]) {
+ return mSensorPixelModesUsed[i] < spms[i];
}
}
- return true;
+ return false;
}
bool OutputConfiguration::gbpsLessThan(const OutputConfiguration& other) const {
diff --git a/camera/camera2/SessionConfiguration.cpp b/camera/camera2/SessionConfiguration.cpp
index a431a33..7cf6087 100644
--- a/camera/camera2/SessionConfiguration.cpp
+++ b/camera/camera2/SessionConfiguration.cpp
@@ -55,6 +55,12 @@
return err;
}
+ bool inputIsMultiResolution = false;
+ if ((err = parcel->readBool(&inputIsMultiResolution)) != OK) {
+ ALOGE("%s: Failed to read input multi-resolution flag from parcel", __FUNCTION__);
+ return err;
+ }
+
std::vector<OutputConfiguration> outputStreams;
if ((err = parcel->readParcelableVector(&outputStreams)) != OK) {
ALOGE("%s: Failed to read output configurations from parcel", __FUNCTION__);
@@ -65,6 +71,7 @@
mInputWidth = inputWidth;
mInputHeight = inputHeight;
mInputFormat = inputFormat;
+ mInputIsMultiResolution = inputIsMultiResolution;
for (auto& stream : outputStreams) {
mOutputStreams.push_back(stream);
}
@@ -90,6 +97,9 @@
err = parcel->writeInt32(mInputFormat);
if (err != OK) return err;
+ err = parcel->writeBool(mInputIsMultiResolution);
+ if (err != OK) return err;
+
err = parcel->writeParcelableVector(mOutputStreams);
if (err != OK) return err;
diff --git a/camera/cameraserver/Android.bp b/camera/cameraserver/Android.bp
index f58f20c..8ca8920 100644
--- a/camera/cameraserver/Android.bp
+++ b/camera/cameraserver/Android.bp
@@ -42,11 +42,12 @@
"android.hardware.camera.provider@2.4",
"android.hardware.camera.provider@2.5",
"android.hardware.camera.provider@2.6",
+ "android.hardware.camera.provider@2.7",
"android.hardware.camera.device@1.0",
"android.hardware.camera.device@3.2",
"android.hardware.camera.device@3.4",
],
- compile_multilib: "prefer32",
+ compile_multilib: "first",
cflags: [
"-Wall",
"-Wextra",
@@ -57,6 +58,6 @@
init_rc: ["cameraserver.rc"],
vintf_fragments: [
- "manifest_android.frameworks.cameraservice.service@2.1.xml",
+ "manifest_android.frameworks.cameraservice.service@2.2.xml",
],
}
diff --git a/camera/cameraserver/manifest_android.frameworks.cameraservice.service@2.1.xml b/camera/cameraserver/manifest_android.frameworks.cameraservice.service@2.1.xml
deleted file mode 100644
index 5a15b35..0000000
--- a/camera/cameraserver/manifest_android.frameworks.cameraservice.service@2.1.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<manifest version="1.0" type="framework">
- <hal>
- <name>android.frameworks.cameraservice.service</name>
- <transport>hwbinder</transport>
- <version>2.1</version>
- <interface>
- <name>ICameraService</name>
- <instance>default</instance>
- </interface>
- </hal>
-</manifest>
diff --git a/camera/cameraserver/manifest_android.frameworks.cameraservice.service@2.2.xml b/camera/cameraserver/manifest_android.frameworks.cameraservice.service@2.2.xml
new file mode 100644
index 0000000..eeafc91
--- /dev/null
+++ b/camera/cameraserver/manifest_android.frameworks.cameraservice.service@2.2.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="framework">
+ <hal>
+ <name>android.frameworks.cameraservice.service</name>
+ <transport>hwbinder</transport>
+ <version>2.2</version>
+ <interface>
+ <name>ICameraService</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/camera/include/camera/Camera.h b/camera/include/camera/Camera.h
index 2cdb617..5579183 100644
--- a/camera/include/camera/Camera.h
+++ b/camera/include/camera/Camera.h
@@ -24,7 +24,6 @@
#include <gui/IGraphicBufferProducer.h>
#include <system/camera.h>
#include <camera/ICameraRecordingProxy.h>
-#include <camera/ICameraRecordingProxyListener.h>
#include <camera/android/hardware/ICamera.h>
#include <camera/android/hardware/ICameraClient.h>
#include <camera/CameraBase.h>
@@ -84,10 +83,6 @@
const String16& clientPackageName,
int clientUid, int clientPid);
- static status_t connectLegacy(int cameraId, int halVersion,
- const String16& clientPackageName,
- int clientUid, sp<Camera>& camera);
-
virtual ~Camera();
status_t reconnect();
@@ -154,7 +149,6 @@
status_t setVideoTarget(const sp<IGraphicBufferProducer>& bufferProducer);
void setListener(const sp<CameraListener>& listener);
- void setRecordingProxyListener(const sp<ICameraRecordingProxyListener>& listener);
// Configure preview callbacks to app. Only one of the older
// callbacks or the callback surface can be active at the same time;
@@ -187,12 +181,8 @@
explicit RecordingProxy(const sp<Camera>& camera);
// ICameraRecordingProxy interface
- virtual status_t startRecording(const sp<ICameraRecordingProxyListener>& listener);
+ virtual status_t startRecording();
virtual void stopRecording();
- virtual void releaseRecordingFrame(const sp<IMemory>& mem);
- virtual void releaseRecordingFrameHandle(native_handle_t* handle);
- virtual void releaseRecordingFrameHandleBatch(
- const std::vector<native_handle_t*>& handles);
private:
sp<Camera> mCamera;
@@ -203,8 +193,6 @@
Camera(const Camera&);
Camera& operator=(const Camera);
- sp<ICameraRecordingProxyListener> mRecordingProxyListener;
-
friend class CameraBase;
};
diff --git a/camera/include/camera/CameraMetadata.h b/camera/include/camera/CameraMetadata.h
index e883ffa..c56ee6d 100644
--- a/camera/include/camera/CameraMetadata.h
+++ b/camera/include/camera/CameraMetadata.h
@@ -242,6 +242,11 @@
static status_t getTagFromName(const char *name,
const VendorTagDescriptor* vTags, uint32_t *tag);
+ /**
+ * Return the current vendor tag id associated with this metadata.
+ */
+ metadata_vendor_id_t getVendorId();
+
private:
camera_metadata_t *mBuffer;
mutable bool mLocked;
diff --git a/camera/include/camera/CameraSessionStats.h b/camera/include/camera/CameraSessionStats.h
new file mode 100644
index 0000000..c398aca
--- /dev/null
+++ b/camera/include/camera/CameraSessionStats.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_CAMERA_SERVICE_SESSION_STATS_H
+#define ANDROID_HARDWARE_CAMERA_SERVICE_SESSION_STATS_H
+
+#include <binder/Parcelable.h>
+
+namespace android {
+namespace hardware {
+
+/**
+ * Camera stream info and statistics
+ */
+class CameraStreamStats : public android::Parcelable {
+public:
+ enum HistogramType {
+ HISTOGRAM_TYPE_UNKNOWN = 0,
+ HISTOGRAM_TYPE_CAPTURE_LATENCY = 1,
+ };
+
+ int mWidth;
+ int mHeight;
+ int mFormat;
+ int mDataSpace;
+ int64_t mUsage;
+
+ // The number of requested buffers
+ int64_t mRequestCount;
+ // The number of buffer errors
+ int64_t mErrorCount;
+
+ // The capture latency of 1st request for this stream
+ int32_t mStartLatencyMs;
+
+ // Buffer count info
+ int mMaxHalBuffers;
+ int mMaxAppBuffers;
+
+ // Histogram type. So far only capture latency histogram is supported.
+ int mHistogramType;
+ // The bounary values separating adjacent histogram bins.
+ // A vector of {h1, h2, h3} represents bins of [0, h1), [h1, h2), [h2, h3),
+ // and [h3, infinity)
+ std::vector<float> mHistogramBins;
+ // The counts for all histogram bins.
+ // size(mHistogramBins) + 1 = size(mHistogramCounts)
+ std::vector<int64_t> mHistogramCounts;
+
+ CameraStreamStats() :
+ mWidth(0), mHeight(0), mFormat(0), mDataSpace(0), mUsage(0),
+ mRequestCount(0), mErrorCount(0), mStartLatencyMs(0),
+ mMaxHalBuffers(0), mMaxAppBuffers(0), mHistogramType(HISTOGRAM_TYPE_UNKNOWN) {}
+ CameraStreamStats(int width, int height, int format, int dataSpace, int64_t usage,
+ int maxHalBuffers, int maxAppBuffers)
+ : mWidth(width), mHeight(height), mFormat(format), mDataSpace(dataSpace),
+ mUsage(usage), mRequestCount(0), mErrorCount(0), mStartLatencyMs(0),
+ mMaxHalBuffers(maxHalBuffers), mMaxAppBuffers(maxAppBuffers),
+ mHistogramType(HISTOGRAM_TYPE_UNKNOWN) {}
+
+ virtual status_t readFromParcel(const android::Parcel* parcel) override;
+ virtual status_t writeToParcel(android::Parcel* parcel) const override;
+};
+
+/**
+ * Camera session statistics
+ *
+ * This includes session wide info and stream statistics.
+ */
+class CameraSessionStats : public android::Parcelable {
+public:
+ /**
+ * Values for notifyCameraState newCameraState
+ */
+ static const int CAMERA_STATE_OPEN;
+ static const int CAMERA_STATE_ACTIVE;
+ static const int CAMERA_STATE_IDLE;
+ static const int CAMERA_STATE_CLOSED;
+
+ /**
+ * Values for notifyCameraState facing
+ */
+ static const int CAMERA_FACING_BACK;
+ static const int CAMERA_FACING_FRONT;
+ static const int CAMERA_FACING_EXTERNAL;
+
+ /**
+ * Values for notifyCameraState api level
+ */
+ static const int CAMERA_API_LEVEL_1;
+ static const int CAMERA_API_LEVEL_2;
+
+ String16 mCameraId;
+ int mFacing;
+ int mNewCameraState;
+ String16 mClientName;
+ int mApiLevel;
+ bool mIsNdk;
+ // latency in ms for camera open, close, or session creation.
+ int mLatencyMs;
+
+ // Session info and statistics
+ int mSessionType;
+ int mInternalReconfigure;
+ // The number of capture requests
+ int64_t mRequestCount;
+ // The number of result error
+ int64_t mResultErrorCount;
+ // Whether the device runs into an error state
+ bool mDeviceError;
+ std::vector<CameraStreamStats> mStreamStats;
+
+ // Constructors
+ CameraSessionStats();
+ CameraSessionStats(const String16& cameraId, int facing, int newCameraState,
+ const String16& clientName, int apiLevel, bool isNdk, int32_t latencyMs);
+
+ virtual status_t readFromParcel(const android::Parcel* parcel) override;
+ virtual status_t writeToParcel(android::Parcel* parcel) const override;
+};
+
+}; // namespace hardware
+}; // namespace android
+
+#endif // ANDROID_HARDWARE_CAMERA_SERVICE_SESSION_STATS_H
diff --git a/camera/include/camera/ICameraRecordingProxy.h b/camera/include/camera/ICameraRecordingProxy.h
index 02af2f3..4306dc1 100644
--- a/camera/include/camera/ICameraRecordingProxy.h
+++ b/camera/include/camera/ICameraRecordingProxy.h
@@ -24,13 +24,11 @@
namespace android {
-class ICameraRecordingProxyListener;
-class IMemory;
class Parcel;
/*
- * The purpose of ICameraRecordingProxy and ICameraRecordingProxyListener is to
- * allow applications using the camera during recording.
+ * The purpose of ICameraRecordingProxy is to
+ * allow applications to use the camera during recording with the old camera API.
*
* Camera service allows only one client at a time. Since camcorder application
* needs to own the camera to do things like zoom, the media recorder cannot
@@ -42,35 +40,29 @@
* ICameraRecordingProxy
* startRecording()
* stopRecording()
- * releaseRecordingFrame()
*
- * ICameraRecordingProxyListener
- * dataCallbackTimestamp()
-
* The camcorder app opens the camera and starts the preview. The app passes
* ICamera and ICameraRecordingProxy to the media recorder by
* MediaRecorder::setCamera(). The recorder uses ICamera to setup the camera in
* MediaRecorder::start(). After setup, the recorder disconnects from camera
- * service. The recorder calls ICameraRecordingProxy::startRecording() and
- * passes a ICameraRecordingProxyListener to the app. The app connects back to
- * camera service and starts the recording. The app owns the camera and can do
- * things like zoom. The media recorder receives the video frames from the
- * listener and releases them by ICameraRecordingProxy::releaseRecordingFrame.
- * The recorder calls ICameraRecordingProxy::stopRecording() to stop the
- * recording.
+ * service. The recorder calls ICameraRecordingProxy::startRecording() and The
+ * app owns the camera and can do things like zoom. The media recorder receives
+ * the video frames via a buffer queue. The recorder calls
+ * ICameraRecordingProxy::stopRecording() to stop the recording.
*
* The call sequences are as follows:
* 1. The app: Camera.unlock().
* 2. The app: MediaRecorder.setCamera().
* 3. Start recording
* (1) The app: MediaRecorder.start().
- * (2) The recorder: ICamera.unlock() and ICamera.disconnect().
- * (3) The recorder: ICameraRecordingProxy.startRecording().
- * (4) The app: ICamera.reconnect().
- * (5) The app: ICamera.startRecording().
+ * (2) The recorder: ICamera.setVideoTarget(buffer queue).
+ * (3) The recorder: ICamera.unlock() and ICamera.disconnect().
+ * (4) The recorder: ICameraRecordingProxy.startRecording().
+ * (5) The app: ICamera.reconnect().
+ * (6) The app: ICamera.startRecording().
* 4. During recording
- * (1) The recorder: receive frames from ICameraRecordingProxyListener.dataCallbackTimestamp()
- * (2) The recorder: release frames by ICameraRecordingProxy.releaseRecordingFrame().
+ * (1) The recorder: receive frames via a buffer queue
+ * (2) The recorder: release frames via a buffer queue
* 5. Stop recording
* (1) The app: MediaRecorder.stop()
* (2) The recorder: ICameraRecordingProxy.stopRecording().
@@ -82,12 +74,8 @@
public:
DECLARE_META_INTERFACE(CameraRecordingProxy);
- virtual status_t startRecording(const sp<ICameraRecordingProxyListener>& listener) = 0;
+ virtual status_t startRecording() = 0;
virtual void stopRecording() = 0;
- virtual void releaseRecordingFrame(const sp<IMemory>& mem) = 0;
- virtual void releaseRecordingFrameHandle(native_handle_t *handle) = 0;
- virtual void releaseRecordingFrameHandleBatch(
- const std::vector<native_handle_t*>& handles) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/camera/include/camera/ICameraRecordingProxyListener.h b/camera/include/camera/ICameraRecordingProxyListener.h
deleted file mode 100644
index da03c56..0000000
--- a/camera/include/camera/ICameraRecordingProxyListener.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_ICAMERA_RECORDING_PROXY_LISTENER_H
-#define ANDROID_HARDWARE_ICAMERA_RECORDING_PROXY_LISTENER_H
-
-#include <vector>
-#include <binder/IInterface.h>
-#include <cutils/native_handle.h>
-#include <stdint.h>
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
-
-namespace android {
-
-class Parcel;
-class IMemory;
-
-class ICameraRecordingProxyListener: public IInterface
-{
-public:
- DECLARE_META_INTERFACE(CameraRecordingProxyListener);
-
- virtual void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType,
- const sp<IMemory>& data) = 0;
-
- virtual void recordingFrameHandleCallbackTimestamp(nsecs_t timestamp,
- native_handle_t* handle) = 0;
-
- virtual void recordingFrameHandleCallbackTimestampBatch(
- const std::vector<nsecs_t>& timestamps,
- const std::vector<native_handle_t*>& handles) = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-class BnCameraRecordingProxyListener: public BnInterface<ICameraRecordingProxyListener>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-}; // namespace android
-
-#endif
diff --git a/camera/include/camera/VendorTagDescriptor.h b/camera/include/camera/VendorTagDescriptor.h
index b2fbf3a..b3440d5 100644
--- a/camera/include/camera/VendorTagDescriptor.h
+++ b/camera/include/camera/VendorTagDescriptor.h
@@ -249,6 +249,12 @@
*/
static void clearGlobalVendorTagCache();
+ /**
+ * Return true if given vendor id is present in the vendor tag caches, return
+ * false otherwise.
+ */
+ static bool isVendorCachePresent(metadata_vendor_id_t vendorId);
+
};
} /* namespace android */
diff --git a/camera/include/camera/camera2/OutputConfiguration.h b/camera/include/camera/camera2/OutputConfiguration.h
index 95c4f39..f80ed3a 100644
--- a/camera/include/camera/camera2/OutputConfiguration.h
+++ b/camera/include/camera/camera2/OutputConfiguration.h
@@ -47,6 +47,10 @@
bool isDeferred() const;
bool isShared() const;
String16 getPhysicalCameraId() const;
+ bool isMultiResolution() const;
+
+ // set of sensor pixel mode resolutions allowed {MAX_RESOLUTION, DEFAULT_MODE};
+ const std::vector<int32_t>& getSensorPixelModesUsed() const;
/**
* Keep impl up-to-date with OutputConfiguration.java in frameworks/base
*/
@@ -83,7 +87,9 @@
mIsDeferred == other.mIsDeferred &&
mIsShared == other.mIsShared &&
gbpsEqual(other) &&
- mPhysicalCameraId == other.mPhysicalCameraId );
+ mPhysicalCameraId == other.mPhysicalCameraId &&
+ mIsMultiResolution == other.mIsMultiResolution &&
+ sensorPixelModesUsedEqual(other));
}
bool operator != (const OutputConfiguration& other) const {
return !(*this == other);
@@ -114,13 +120,22 @@
if (mPhysicalCameraId != other.mPhysicalCameraId) {
return mPhysicalCameraId < other.mPhysicalCameraId;
}
+ if (mIsMultiResolution != other.mIsMultiResolution) {
+ return mIsMultiResolution < other.mIsMultiResolution;
+ }
+ if (!sensorPixelModesUsedEqual(other)) {
+ return sensorPixelModesUsedLessThan(other);
+ }
return gbpsLessThan(other);
}
+
bool operator > (const OutputConfiguration& other) const {
return (*this != other && !(*this < other));
}
bool gbpsEqual(const OutputConfiguration& other) const;
+ bool sensorPixelModesUsedEqual(const OutputConfiguration& other) const;
+ bool sensorPixelModesUsedLessThan(const OutputConfiguration& other) const;
bool gbpsLessThan(const OutputConfiguration& other) const;
void addGraphicProducer(sp<IGraphicBufferProducer> gbp) {mGbps.push_back(gbp);}
private:
@@ -133,6 +148,8 @@
bool mIsDeferred;
bool mIsShared;
String16 mPhysicalCameraId;
+ bool mIsMultiResolution;
+ std::vector<int32_t> mSensorPixelModesUsed;
};
} // namespace params
} // namespace camera2
diff --git a/camera/include/camera/camera2/SessionConfiguration.h b/camera/include/camera/camera2/SessionConfiguration.h
index 64288ed..29913f6 100644
--- a/camera/include/camera/camera2/SessionConfiguration.h
+++ b/camera/include/camera/camera2/SessionConfiguration.h
@@ -38,6 +38,7 @@
int getInputHeight() const { return mInputHeight; }
int getInputFormat() const { return mInputFormat; }
int getOperatingMode() const { return mOperatingMode; }
+ bool inputIsMultiResolution() const { return mInputIsMultiResolution; }
virtual status_t writeToParcel(android::Parcel* parcel) const override;
virtual status_t readFromParcel(const android::Parcel* parcel) override;
@@ -61,7 +62,8 @@
mInputWidth == other.mInputWidth &&
mInputHeight == other.mInputHeight &&
mInputFormat == other.mInputFormat &&
- mOperatingMode == other.mOperatingMode);
+ mOperatingMode == other.mOperatingMode &&
+ mInputIsMultiResolution == other.mInputIsMultiResolution);
}
bool operator != (const SessionConfiguration& other) const {
@@ -83,6 +85,10 @@
return mInputFormat < other.mInputFormat;
}
+ if (mInputIsMultiResolution != other.mInputIsMultiResolution) {
+ return mInputIsMultiResolution < other.mInputIsMultiResolution;
+ }
+
if (mOperatingMode != other.mOperatingMode) {
return mOperatingMode < other.mOperatingMode;
}
@@ -104,6 +110,7 @@
std::vector<OutputConfiguration> mOutputStreams;
int mInputWidth, mInputHeight, mInputFormat, mOperatingMode;
+ bool mInputIsMultiResolution = false;
};
} // namespace params
} // namespace camera2
diff --git a/camera/ndk/Android.bp b/camera/ndk/Android.bp
index 24eaba6..fef873b 100644
--- a/camera/ndk/Android.bp
+++ b/camera/ndk/Android.bp
@@ -93,7 +93,7 @@
],
cflags: [
"-fvisibility=hidden",
- "-DEXPORT=__attribute__ ((visibility (\"default\")))",
+ "-DEXPORT=__attribute__((visibility(\"default\")))",
"-Wall",
"-Wextra",
"-Werror",
@@ -152,9 +152,11 @@
"libcamera_metadata",
"libmediandk",
"android.frameworks.cameraservice.device@2.0",
+ "android.frameworks.cameraservice.device@2.1",
"android.frameworks.cameraservice.common@2.0",
"android.frameworks.cameraservice.service@2.0",
"android.frameworks.cameraservice.service@2.1",
+ "android.frameworks.cameraservice.service@2.2",
],
static_libs: [
"android.hardware.camera.common@1.0-helper",
diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp
index 78974ae..dd652c7 100644
--- a/camera/ndk/impl/ACameraDevice.cpp
+++ b/camera/ndk/impl/ACameraDevice.cpp
@@ -186,6 +186,7 @@
const ACaptureRequest* sessionParameters,
const ACameraCaptureSession_stateCallbacks* callbacks,
/*out*/ACameraCaptureSession** session) {
+ nsecs_t startTimeNs = systemTime();
sp<ACameraCaptureSession> currentSession = mCurrentSession.promote();
Mutex::Autolock _l(mDeviceLock);
camera_status_t ret = checkCameraClosedOrErrorLocked();
@@ -199,7 +200,7 @@
}
// Create new session
- ret = configureStreamsLocked(outputs, sessionParameters);
+ ret = configureStreamsLocked(outputs, sessionParameters, startTimeNs);
if (ret != ACAMERA_OK) {
ALOGE("Fail to create new session. cannot configure streams");
return ret;
@@ -450,7 +451,11 @@
}
// No new session, unconfigure now
- camera_status_t ret = configureStreamsLocked(nullptr, nullptr);
+ // Note: The unconfiguration of session won't be accounted for session
+ // latency because a stream configuration with 0 streams won't ever become
+ // active.
+ nsecs_t startTimeNs = systemTime();
+ camera_status_t ret = configureStreamsLocked(nullptr, nullptr, startTimeNs);
if (ret != ACAMERA_OK) {
ALOGE("Unconfigure stream failed. Device might still be configured! ret %d", ret);
}
@@ -609,7 +614,7 @@
camera_status_t
CameraDevice::configureStreamsLocked(const ACaptureSessionOutputContainer* outputs,
- const ACaptureRequest* sessionParameters) {
+ const ACaptureRequest* sessionParameters, nsecs_t startTimeNs) {
ACaptureSessionOutputContainer emptyOutput;
if (outputs == nullptr) {
outputs = &emptyOutput;
@@ -711,7 +716,8 @@
params.append(sessionParameters->settings->getInternalData());
}
std::vector<int> offlineStreamIds;
- remoteRet = mRemote->endConfigure(/*isConstrainedHighSpeed*/ false, params, &offlineStreamIds);
+ remoteRet = mRemote->endConfigure(/*isConstrainedHighSpeed*/ false, params,
+ ns2ms(startTimeNs), &offlineStreamIds);
if (remoteRet.serviceSpecificErrorCode() == hardware::ICameraService::ERROR_ILLEGAL_ARGUMENT) {
ALOGE("Camera device %s cannnot support app output configuration: %s", getId(),
remoteRet.toString8().string());
diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h
index 3073dfb..344d964 100644
--- a/camera/ndk/impl/ACameraDevice.h
+++ b/camera/ndk/impl/ACameraDevice.h
@@ -167,7 +167,7 @@
void notifySessionEndOfLifeLocked(ACameraCaptureSession* session);
camera_status_t configureStreamsLocked(const ACaptureSessionOutputContainer* outputs,
- const ACaptureRequest* sessionParameters);
+ const ACaptureRequest* sessionParameters, nsecs_t startTimeNs);
// Input message will be posted and cleared after this returns
void postSessionMsgAndCleanup(sp<AMessage>& msg);
diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp
index bfa60d9..dab2fef 100644
--- a/camera/ndk/impl/ACameraMetadata.cpp
+++ b/camera/ndk/impl/ACameraMetadata.cpp
@@ -24,6 +24,28 @@
using namespace android;
+// Formats not listed in the public API, but still available to AImageReader
+// Enum value must match corresponding enum in ui/PublicFormat.h (which is not
+// available to VNDK)
+enum AIMAGE_PRIVATE_FORMATS {
+ /**
+ * Unprocessed implementation-dependent raw
+ * depth measurements, opaque with 16 bit
+ * samples.
+ *
+ */
+
+ AIMAGE_FORMAT_RAW_DEPTH = 0x1002,
+
+ /**
+ * Device specific 10 bits depth RAW image format.
+ *
+ * <p>Unprocessed implementation-dependent raw depth measurements, opaque with 10 bit samples
+ * and device specific bit layout.</p>
+ */
+ AIMAGE_FORMAT_RAW_DEPTH10 = 0x1003,
+};
+
/**
* ACameraMetadata Implementation
*/
@@ -290,6 +312,10 @@
format = AIMAGE_FORMAT_DEPTH_POINT_CLOUD;
} else if (format == HAL_PIXEL_FORMAT_Y16) {
format = AIMAGE_FORMAT_DEPTH16;
+ } else if (format == HAL_PIXEL_FORMAT_RAW16) {
+ format = static_cast<int32_t>(AIMAGE_FORMAT_RAW_DEPTH);
+ } else if (format == HAL_PIXEL_FORMAT_RAW10) {
+ format = static_cast<int32_t>(AIMAGE_FORMAT_RAW_DEPTH10);
}
filteredDepthStreamConfigs.push_back(format);
@@ -426,6 +452,7 @@
camera_metadata_ro_entry_t entry;
int ret = get_camera_metadata_ro_entry(rawMetadata, i, &entry);
if (ret != 0) {
+ mData->unlock(rawMetadata);
ALOGE("%s: error reading metadata index %zu", __FUNCTION__, i);
return ACAMERA_ERROR_UNKNOWN;
}
@@ -527,11 +554,13 @@
case ACAMERA_LENS_OPTICAL_STABILIZATION_MODE:
case ACAMERA_NOISE_REDUCTION_MODE:
case ACAMERA_SCALER_CROP_REGION:
+ case ACAMERA_SCALER_ROTATE_AND_CROP:
case ACAMERA_SENSOR_EXPOSURE_TIME:
case ACAMERA_SENSOR_FRAME_DURATION:
case ACAMERA_SENSOR_SENSITIVITY:
case ACAMERA_SENSOR_TEST_PATTERN_DATA:
case ACAMERA_SENSOR_TEST_PATTERN_MODE:
+ case ACAMERA_SENSOR_PIXEL_MODE:
case ACAMERA_SHADING_MODE:
case ACAMERA_STATISTICS_FACE_DETECT_MODE:
case ACAMERA_STATISTICS_HOT_PIXEL_MAP_MODE:
@@ -582,6 +611,7 @@
ANDROID_SENSOR_PROFILE_HUE_SAT_MAP,
ANDROID_SENSOR_PROFILE_TONE_CURVE,
ANDROID_SENSOR_OPAQUE_RAW_SIZE,
+ ANDROID_SENSOR_OPAQUE_RAW_SIZE_MAXIMUM_RESOLUTION,
ANDROID_SHADING_STRENGTH,
ANDROID_STATISTICS_HISTOGRAM_MODE,
ANDROID_STATISTICS_SHARPNESS_MAP_MODE,
diff --git a/camera/ndk/include/camera/NdkCameraCaptureSession.h b/camera/ndk/include/camera/NdkCameraCaptureSession.h
index 6c1cf33..2b7f040 100644
--- a/camera/ndk/include/camera/NdkCameraCaptureSession.h
+++ b/camera/ndk/include/camera/NdkCameraCaptureSession.h
@@ -61,6 +61,10 @@
*/
typedef void (*ACameraCaptureSession_stateCallback)(void* context, ACameraCaptureSession *session);
+/**
+ * Capture session state callbacks used in {@link ACameraDevice_createCaptureSession} and
+ * {@link ACameraDevice_createCaptureSessionWithSessionParameters}
+ */
typedef struct ACameraCaptureSession_stateCallbacks {
/// optional application context.
void* context;
@@ -246,6 +250,10 @@
void* context, ACameraCaptureSession* session,
ACaptureRequest* request, ACameraWindowType* window, int64_t frameNumber);
+/**
+ * ACaptureCaptureSession_captureCallbacks structure used in
+ * {@link ACameraCaptureSession_capture} and {@link ACameraCaptureSession_setRepeatingRequest}.
+ */
typedef struct ACameraCaptureSession_captureCallbacks {
/// optional application context.
void* context;
@@ -413,7 +421,10 @@
*/
void ACameraCaptureSession_close(ACameraCaptureSession* session);
-struct ACameraDevice;
+/**
+ * ACameraDevice is opaque type that provides access to a camera device.
+ * A pointer can be obtained using {@link ACameraManager_openCamera} method.
+ */
typedef struct ACameraDevice ACameraDevice;
/**
@@ -591,6 +602,10 @@
camera_status_t ACameraCaptureSession_abortCaptures(ACameraCaptureSession* session)
__INTRODUCED_IN(24);
+/**
+ * Opaque object for capture session output, use {@link ACaptureSessionOutput_create} or
+ * {@link ACaptureSessionSharedOutput_create} to create an instance.
+ */
typedef struct ACaptureSessionOutput ACaptureSessionOutput;
/**
@@ -604,9 +619,9 @@
*
* <p>Native windows that get removed must not be part of any active repeating or single/burst
* request or have any pending results. Consider updating repeating requests via
- * {@link ACaptureSessionOutput_setRepeatingRequest} and then wait for the last frame number
+ * {@link ACameraCaptureSession_setRepeatingRequest} and then wait for the last frame number
* when the sequence completes
- * {@link ACameraCaptureSession_captureCallback#onCaptureSequenceCompleted}.</p>
+ * {@link ACameraCaptureSession_captureCallbacks#onCaptureSequenceCompleted}.</p>
*
* <p>Native windows that get added must not be part of any other registered ACaptureSessionOutput
* and must be compatible. Compatible windows must have matching format, rotation and
@@ -713,7 +728,15 @@
* Same as ACameraCaptureSession_captureCallbacks
*/
void* context;
+
+ /**
+ * Same as {@link ACameraCaptureSession_captureCallbacks#onCaptureStarted}.
+ */
ACameraCaptureSession_captureCallback_start onCaptureStarted;
+
+ /**
+ * Same as {@link ACameraCaptureSession_captureCallbacks#onCaptureProgressed}.
+ */
ACameraCaptureSession_captureCallback_result onCaptureProgressed;
/**
@@ -751,10 +774,18 @@
ACameraCaptureSession_logicalCamera_captureCallback_failed onLogicalCameraCaptureFailed;
/**
- * Same as ACameraCaptureSession_captureCallbacks
+ * Same as {@link ACameraCaptureSession_captureCallbacks#onCaptureSequenceCompleted}.
*/
ACameraCaptureSession_captureCallback_sequenceEnd onCaptureSequenceCompleted;
+
+ /**
+ * Same as {@link ACameraCaptureSession_captureCallbacks#onCaptureSequenceAborted}.
+ */
ACameraCaptureSession_captureCallback_sequenceAbort onCaptureSequenceAborted;
+
+ /**
+ * Same as {@link ACameraCaptureSession_captureCallbacks#onCaptureBufferLost}.
+ */
ACameraCaptureSession_captureCallback_bufferLost onCaptureBufferLost;
} ACameraCaptureSession_logicalCamera_captureCallbacks;
diff --git a/camera/ndk/include/camera/NdkCameraDevice.h b/camera/ndk/include/camera/NdkCameraDevice.h
index f72fe8d..7be4bd3 100644
--- a/camera/ndk/include/camera/NdkCameraDevice.h
+++ b/camera/ndk/include/camera/NdkCameraDevice.h
@@ -124,6 +124,10 @@
*/
typedef void (*ACameraDevice_ErrorStateCallback)(void* context, ACameraDevice* device, int error);
+/**
+ * Applications' callbacks for camera device state changes, register with
+ * {@link ACameraManager_openCamera}.
+ */
typedef struct ACameraDevice_StateCallbacks {
/// optional application context.
void* context;
@@ -198,6 +202,10 @@
*/
const char* ACameraDevice_getId(const ACameraDevice* device) __INTRODUCED_IN(24);
+/**
+ * Capture request pre-defined template types, used in {@link ACameraDevice_createCaptureRequest}
+ * and {@link ACameraDevice_createCaptureRequest_withPhysicalIds}.
+ */
typedef enum {
/**
* Create a request suitable for a camera preview window. Specifically, this
@@ -301,10 +309,12 @@
const ACameraDevice* device, ACameraDevice_request_template templateId,
/*out*/ACaptureRequest** request) __INTRODUCED_IN(24);
-
+/**
+ * Opaque object for CaptureSessionOutput container, use
+ * {@link ACaptureSessionOutputContainer_create} to create an instance.
+ */
typedef struct ACaptureSessionOutputContainer ACaptureSessionOutputContainer;
-typedef struct ACaptureSessionOutput ACaptureSessionOutput;
/**
* Create a capture session output container.
@@ -844,7 +854,7 @@
/*out*/ACaptureRequest** request) __INTRODUCED_IN(29);
/**
- * Check whether a particular {@ACaptureSessionOutputContainer} is supported by
+ * Check whether a particular {@link ACaptureSessionOutputContainer} is supported by
* the camera device.
*
* <p>This method performs a runtime check of a given {@link
@@ -875,6 +885,7 @@
* device.</li>
* <li>{@link ACAMERA_ERROR_UNSUPPORTED_OPERATION} if the query operation is not
* supported by the camera device.</li>
+ * </ul>
*/
camera_status_t ACameraDevice_isSessionConfigurationSupported(
const ACameraDevice* device,
diff --git a/camera/ndk/include/camera/NdkCameraError.h b/camera/ndk/include/camera/NdkCameraError.h
index 9d77eb4..26db7f2 100644
--- a/camera/ndk/include/camera/NdkCameraError.h
+++ b/camera/ndk/include/camera/NdkCameraError.h
@@ -40,7 +40,13 @@
__BEGIN_DECLS
+/**
+ * Camera status enum types.
+ */
typedef enum {
+ /**
+ * Camera operation has succeeded.
+ */
ACAMERA_OK = 0,
ACAMERA_ERROR_BASE = -10000,
diff --git a/camera/ndk/include/camera/NdkCameraManager.h b/camera/ndk/include/camera/NdkCameraManager.h
index be32b11..729182e 100644
--- a/camera/ndk/include/camera/NdkCameraManager.h
+++ b/camera/ndk/include/camera/NdkCameraManager.h
@@ -326,7 +326,7 @@
* @see ACameraManager_registerExtendedAvailabilityCallback
*/
typedef struct ACameraManager_ExtendedAvailabilityListener {
- ///
+ /// Called when a camera becomes available or unavailable
ACameraManager_AvailabilityCallbacks availabilityCallbacks;
/// Called when there is camera access permission change
diff --git a/camera/ndk/include/camera/NdkCameraMetadata.h b/camera/ndk/include/camera/NdkCameraMetadata.h
index 0d5e6c4..b331d50 100644
--- a/camera/ndk/include/camera/NdkCameraMetadata.h
+++ b/camera/ndk/include/camera/NdkCameraMetadata.h
@@ -256,10 +256,12 @@
/**
* Return a {@link ACameraMetadata} that references the same data as
- * {@link cameraMetadata}, which is an instance of
- * {@link android.hardware.camera2.CameraMetadata} (e.g., a
- * {@link android.hardware.camera2.CameraCharacteristics} or
- * {@link android.hardware.camera2.CaptureResult}).
+ * <a href="/reference/android/hardware/camera2/CameraMetadata">
+ * android.hardware.camera2.CameraMetadata</a> from Java API. (e.g., a
+ * <a href="/reference/android/hardware/camera2/CameraCharacteristics">
+ * android.hardware.camera2.CameraCharacteristics</a>
+ * or <a href="/reference/android/hardware/camera2/CaptureResult">
+ * android.hardware.camera2.CaptureResult</a>).
*
* <p>The returned ACameraMetadata must be freed by the application by {@link ACameraMetadata_free}
* after application is done using it.</p>
@@ -269,11 +271,13 @@
* the Java metadata is garbage collected.
*
* @param env the JNI environment.
- * @param cameraMetadata the source {@link android.hardware.camera2.CameraMetadata} from which the
+ * @param cameraMetadata the source <a href="/reference/android/hardware/camera2/CameraMetadata">
+ android.hardware.camera2.CameraMetadata </a>from which the
* returned {@link ACameraMetadata} is a view.
*
- * @return a valid ACameraMetadata pointer or NULL if {@link cameraMetadata} is null or not a valid
- * instance of {@link android.hardware.camera2.CameraMetadata}.
+ * @return a valid ACameraMetadata pointer or NULL if cameraMetadata is null or not a valid
+ * instance of <a href="android/hardware/camera2/CameraMetadata">
+ * android.hardware.camera2.CameraMetadata</a>.
*
*/
ACameraMetadata* ACameraMetadata_fromCameraMetadata(JNIEnv* env, jobject cameraMetadata)
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index bc33d6a..1609c7b 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -40,6 +40,7 @@
__BEGIN_DECLS
+
typedef enum acamera_metadata_section {
ACAMERA_COLOR_CORRECTION,
ACAMERA_CONTROL,
@@ -526,6 +527,13 @@
* 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>For camera devices with the
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * capability,
+ * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION /
+ * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION must be used as the
+ * coordinate system for requests where ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</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
@@ -535,7 +543,10 @@
* @see ACAMERA_DISTORTION_CORRECTION_MODE
* @see ACAMERA_SCALER_CROP_REGION
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
* @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+ * @see ACAMERA_SENSOR_PIXEL_MODE
*/
ACAMERA_CONTROL_AE_REGIONS = // int32[5*area_count]
ACAMERA_CONTROL_START + 4,
@@ -717,6 +728,12 @@
* 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>For camera devices with the
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * capability, ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION /
+ * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION must be used as the
+ * coordinate system for requests where ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</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
@@ -726,7 +743,10 @@
* @see ACAMERA_DISTORTION_CORRECTION_MODE
* @see ACAMERA_SCALER_CROP_REGION
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
* @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+ * @see ACAMERA_SENSOR_PIXEL_MODE
*/
ACAMERA_CONTROL_AF_REGIONS = // int32[5*area_count]
ACAMERA_CONTROL_START + 8,
@@ -814,7 +834,7 @@
* </ul></p>
*
* <p>This control is only effective if ACAMERA_CONTROL_MODE is AUTO.</p>
- * <p>When set to the ON mode, the camera device's auto-white balance
+ * <p>When set to the AUTO mode, the camera device's auto-white balance
* routine is enabled, overriding the application's selected
* ACAMERA_COLOR_CORRECTION_TRANSFORM, ACAMERA_COLOR_CORRECTION_GAINS and
* ACAMERA_COLOR_CORRECTION_MODE. Note that when ACAMERA_CONTROL_AE_MODE
@@ -903,6 +923,12 @@
* 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>For camera devices with the
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * capability, ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION /
+ * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION must be used as the
+ * coordinate system for requests where ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</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
@@ -912,7 +938,10 @@
* @see ACAMERA_DISTORTION_CORRECTION_MODE
* @see ACAMERA_SCALER_CROP_REGION
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
* @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+ * @see ACAMERA_SENSOR_PIXEL_MODE
*/
ACAMERA_CONTROL_AWB_REGIONS = // int32[5*area_count]
ACAMERA_CONTROL_START + 12,
@@ -1839,7 +1868,7 @@
* <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 BOKEH_STILL_CAPTURE mode
- * (queried via {@link ACAMERA_CONTROL_AVAILABLE_EXTENDED_SCENE_MODE_CAPABILITIES })
+ * (queried via {@link ACAMERA_CONTROL_AVAILABLE_EXTENDED_SCENE_MODE_MAX_SIZES })
* will have preview bokeh effect applied.</li>
* </ul>
* <p>When set to BOKEH_CONTINUOUS mode, configured streams dimension should not exceed this mode's
@@ -2800,6 +2829,51 @@
*/
ACAMERA_LENS_DISTORTION = // float[5]
ACAMERA_LENS_START + 13,
+ /**
+ * <p>The correction coefficients to correct for this camera device's
+ * radial and tangential lens distortion for a
+ * CaptureRequest with ACAMERA_SENSOR_PIXEL_MODE set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: float[5]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_LENS_DISTORTION, when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_LENS_DISTORTION
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_LENS_DISTORTION_MAXIMUM_RESOLUTION = // float[5]
+ ACAMERA_LENS_START + 14,
+ /**
+ * <p>The parameters for this camera device's intrinsic
+ * calibration when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: float[5]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_LENS_INTRINSIC_CALIBRATION, when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_LENS_INTRINSIC_CALIBRATION
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_LENS_INTRINSIC_CALIBRATION_MAXIMUM_RESOLUTION = // float[5]
+ ACAMERA_LENS_START + 15,
ACAMERA_LENS_END,
/**
@@ -3427,6 +3501,12 @@
* 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>For camera devices with the
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * capability, ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION /
+ * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION must be used as the
+ * coordinate system for requests where ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
* <p>The data representation is int[4], which maps to (left, top, width, height).</p>
*
* @see ACAMERA_CONTROL_AE_TARGET_FPS_RANGE
@@ -3435,7 +3515,10 @@
* @see ACAMERA_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
* @see ACAMERA_SCALER_CROPPING_TYPE
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
* @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+ * @see ACAMERA_SENSOR_PIXEL_MODE
*/
ACAMERA_SCALER_CROP_REGION = // int32[4]
ACAMERA_SCALER_START,
@@ -3495,7 +3578,7 @@
* android.scaler.availableInputOutputFormatsMap.</p>
* <p>The following table describes the minimum required output stream
* configurations based on the hardware level
- * (ACAMERA_INFO_SUPPORTED_HARDWARE_LEVEL):</p>
+ * (ACAMERA_INFO_SUPPORTED_HARDWARE_LEVEL), prior to Android 12:</p>
* <p>Format | Size | Hardware Level | Notes
* :-------------:|:--------------------------------------------:|:--------------:|:--------------:
* JPEG | ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE | Any |
@@ -3506,6 +3589,21 @@
* YUV_420_888 | all output sizes available for JPEG | FULL |
* YUV_420_888 | all output sizes available for JPEG, up to the maximum video size | LIMITED |
* IMPLEMENTATION_DEFINED | same as YUV_420_888 | Any |</p>
+ * <p>Starting from Android 12, the camera device may not support JPEG sizes smaller than the
+ * minimum of 1080p and the camera sensor active array size. The requirements for
+ * IMPLEMENTATION_DEFINED and YUV_420_888 stay the same. This new minimum required output
+ * stream configurations are illustrated by the table below:</p>
+ * <p>Format | Size | Hardware Level | Notes
+ * :-------------:|:--------------------------------------------:|:--------------:|:--------------:
+ * JPEG | ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE | Any |
+ * JPEG | 1920x1080 (1080p) | Any | if 1080p <= activeArraySize
+ * YUV_420_888 | ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE | FULL |
+ * YUV_420_888 | 1920x1080 (1080p) | FULL | if 1080p <= activeArraySize
+ * YUV_420_888 | 1280x720 (720) | FULL | if 720p <= activeArraySize
+ * YUV_420_888 | 640x480 (480p) | FULL | if 480p <= activeArraySize
+ * YUV_420_888 | 320x240 (240p) | FULL | if 240p <= activeArraySize
+ * YUV_420_888 | all output sizes available for FULL hardware level, up to the maximum video size | LIMITED |
+ * IMPLEMENTATION_DEFINED | same as YUV_420_888 | Any |</p>
* <p>Refer to ACAMERA_REQUEST_AVAILABLE_CAPABILITIES for additional
* mandatory stream configurations on a per-capability basis.</p>
* <p>Exception on 176x144 (QCIF) resolution: camera devices usually have a fixed capability for
@@ -3537,8 +3635,6 @@
* set to either OFF or FAST.</p>
* <p>When multiple streams are used in a request, the minimum frame
* duration will be max(individual stream min durations).</p>
- * <p>The minimum frame duration of a stream (of a particular format, size)
- * is the same regardless of whether the stream is input or output.</p>
* <p>See ACAMERA_SENSOR_FRAME_DURATION and
* ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS for more details about
* calculating the max frame rate.</p>
@@ -3739,6 +3835,278 @@
ACAMERA_SCALER_AVAILABLE_RECOMMENDED_INPUT_OUTPUT_FORMATS_MAP =
// int32
ACAMERA_SCALER_START + 15,
+ /**
+ * <p>List of rotate-and-crop modes for ACAMERA_SCALER_ROTATE_AND_CROP that are supported by this camera device.</p>
+ *
+ * @see ACAMERA_SCALER_ROTATE_AND_CROP
+ *
+ * <p>Type: byte[n]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>This entry lists the valid modes for ACAMERA_SCALER_ROTATE_AND_CROP for this camera device.</p>
+ * <p>Starting with API level 30, all devices will list at least <code>ROTATE_AND_CROP_NONE</code>.
+ * Devices with support for rotate-and-crop will additionally list at least
+ * <code>ROTATE_AND_CROP_AUTO</code> and <code>ROTATE_AND_CROP_90</code>.</p>
+ *
+ * @see ACAMERA_SCALER_ROTATE_AND_CROP
+ */
+ ACAMERA_SCALER_AVAILABLE_ROTATE_AND_CROP_MODES = // byte[n]
+ ACAMERA_SCALER_START + 16,
+ /**
+ * <p>Whether a rotation-and-crop operation is applied to processed
+ * outputs from the camera.</p>
+ *
+ * <p>Type: byte (acamera_metadata_enum_android_scaler_rotate_and_crop_t)</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
+ * <li>ACaptureRequest</li>
+ * </ul></p>
+ *
+ * <p>This control is primarily intended to help camera applications with no support for
+ * multi-window modes to work correctly on devices where multi-window scenarios are
+ * unavoidable, such as foldables or other devices with variable display geometry or more
+ * free-form window placement (such as laptops, which often place portrait-orientation apps
+ * in landscape with pillarboxing).</p>
+ * <p>If supported, the default value is <code>ROTATE_AND_CROP_AUTO</code>, which allows the camera API
+ * to enable backwards-compatibility support for applications that do not support resizing
+ * / multi-window modes, when the device is in fact in a multi-window mode (such as inset
+ * portrait on laptops, or on a foldable device in some fold states). In addition,
+ * <code>ROTATE_AND_CROP_NONE</code> and <code>ROTATE_AND_CROP_90</code> will always be available if this control
+ * is supported by the device. If not supported, devices API level 30 or higher will always
+ * list only <code>ROTATE_AND_CROP_NONE</code>.</p>
+ * <p>When <code>CROP_AUTO</code> is in use, and the camera API activates backward-compatibility mode,
+ * several metadata fields will also be parsed differently to ensure that coordinates are
+ * correctly handled for features like drawing face detection boxes or passing in
+ * tap-to-focus coordinates. The camera API will convert positions in the active array
+ * coordinate system to/from the cropped-and-rotated coordinate system to make the
+ * operation transparent for applications. The following controls are affected:</p>
+ * <ul>
+ * <li>ACAMERA_CONTROL_AE_REGIONS</li>
+ * <li>ACAMERA_CONTROL_AF_REGIONS</li>
+ * <li>ACAMERA_CONTROL_AWB_REGIONS</li>
+ * <li>android.statistics.faces</li>
+ * </ul>
+ * <p>Capture results will contain the actual value selected by the API;
+ * <code>ROTATE_AND_CROP_AUTO</code> will never be seen in a capture result.</p>
+ * <p>Applications can also select their preferred cropping mode, either to opt out of the
+ * backwards-compatibility treatment, or to use the cropping feature themselves as needed.
+ * In this case, no coordinate translation will be done automatically, and all controls
+ * will continue to use the normal active array coordinates.</p>
+ * <p>Cropping and rotating is done after the application of digital zoom (via either
+ * ACAMERA_SCALER_CROP_REGION or ACAMERA_CONTROL_ZOOM_RATIO), but before each individual
+ * output is further cropped and scaled. It only affects processed outputs such as
+ * YUV, PRIVATE, and JPEG. It has no effect on RAW outputs.</p>
+ * <p>When <code>CROP_90</code> or <code>CROP_270</code> are selected, there is a significant loss to the field of
+ * view. For example, with a 4:3 aspect ratio output of 1600x1200, <code>CROP_90</code> will still
+ * produce 1600x1200 output, but these buffers are cropped from a vertical 3:4 slice at the
+ * center of the 4:3 area, then rotated to be 4:3, and then upscaled to 1600x1200. Only
+ * 56.25% of the original FOV is still visible. In general, for an aspect ratio of <code>w:h</code>,
+ * the crop and rotate operation leaves <code>(h/w)^2</code> of the field of view visible. For 16:9,
+ * this is ~31.6%.</p>
+ * <p>As a visual example, the figure below shows the effect of <code>ROTATE_AND_CROP_90</code> on the
+ * outputs for the following parameters:</p>
+ * <ul>
+ * <li>Sensor active array: <code>2000x1500</code></li>
+ * <li>Crop region: top-left: <code>(500, 375)</code>, size: <code>(1000, 750)</code> (4:3 aspect ratio)</li>
+ * <li>Output streams: YUV <code>640x480</code> and YUV <code>1280x720</code></li>
+ * <li><code>ROTATE_AND_CROP_90</code></li>
+ * </ul>
+ * <p><img alt="Effect of ROTATE_AND_CROP_90" src="../images/camera2/metadata/android.scaler.rotateAndCrop/crop-region-rotate-90-43-ratio.png" /></p>
+ * <p>With these settings, the regions of the active array covered by the output streams are:</p>
+ * <ul>
+ * <li>640x480 stream crop: top-left: <code>(219, 375)</code>, size: <code>(562, 750)</code></li>
+ * <li>1280x720 stream crop: top-left: <code>(289, 375)</code>, size: <code>(422, 750)</code></li>
+ * </ul>
+ * <p>Since the buffers are rotated, the buffers as seen by the application are:</p>
+ * <ul>
+ * <li>640x480 stream: top-left: <code>(781, 375)</code> on active array, size: <code>(640, 480)</code>, downscaled 1.17x from sensor pixels</li>
+ * <li>1280x720 stream: top-left: <code>(711, 375)</code> on active array, size: <code>(1280, 720)</code>, upscaled 1.71x from sensor pixels</li>
+ * </ul>
+ *
+ * @see ACAMERA_CONTROL_AE_REGIONS
+ * @see ACAMERA_CONTROL_AF_REGIONS
+ * @see ACAMERA_CONTROL_AWB_REGIONS
+ * @see ACAMERA_CONTROL_ZOOM_RATIO
+ * @see ACAMERA_SCALER_CROP_REGION
+ */
+ ACAMERA_SCALER_ROTATE_AND_CROP = // byte (acamera_metadata_enum_android_scaler_rotate_and_crop_t)
+ ACAMERA_SCALER_START + 17,
+ /**
+ * <p>Default YUV/PRIVATE size to use for requesting secure image buffers.</p>
+ *
+ * <p>Type: int32[2]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>This entry lists the default size supported in the secure camera mode. This entry is
+ * optional on devices support the SECURE_IMAGE_DATA capability. This entry will be null
+ * if the camera device does not list SECURE_IMAGE_DATA capability.</p>
+ * <p>When the key is present, only a PRIVATE/YUV output of the specified size is guaranteed
+ * to be supported by the camera HAL in the secure camera mode. Any other format or
+ * resolutions might not be supported. Use
+ * {@link ACameraDevice_isSessionConfigurationSupported }
+ * API to query if a secure session configuration is supported if the device supports this
+ * API.</p>
+ * <p>If this key returns null on a device with SECURE_IMAGE_DATA capability, the application
+ * can assume all output sizes listed in the
+ * {@link ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS }
+ * are supported.</p>
+ */
+ ACAMERA_SCALER_DEFAULT_SECURE_IMAGE_SIZE = // int32[2]
+ ACAMERA_SCALER_START + 18,
+ /**
+ * <p>The available multi-resolution stream configurations that this
+ * physical camera device supports
+ * (i.e. format, width, height, output/input stream).</p>
+ *
+ * <p>Type: int32[n*4] (acamera_metadata_enum_android_scaler_physical_camera_multi_resolution_stream_configurations_t)</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>This list contains a subset of the parent logical camera's multi-resolution stream
+ * configurations which belong to this physical camera, and it will advertise and will only
+ * advertise the maximum supported resolutions for a particular format.</p>
+ * <p>If this camera device isn't a physical camera device constituting a logical camera,
+ * but a standalone <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * camera, this field represents the multi-resolution input/output stream configurations of
+ * default mode and max resolution modes. The sizes will be the maximum resolution of a
+ * particular format for default mode and max resolution mode.</p>
+ * <p>This field will only be advertised if the device is a physical camera of a
+ * logical multi-camera device or an ultra high resolution sensor camera. For a logical
+ * multi-camera, the camera API will derive the logical camera’s multi-resolution stream
+ * configurations from all physical cameras. For an ultra high resolution sensor camera, this
+ * is used directly as the camera’s multi-resolution stream configurations.</p>
+ */
+ ACAMERA_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS =
+ // int32[n*4] (acamera_metadata_enum_android_scaler_physical_camera_multi_resolution_stream_configurations_t)
+ ACAMERA_SCALER_START + 19,
+ /**
+ * <p>The available stream configurations that this
+ * camera device supports (i.e. format, width, height, output/input stream) for a
+ * CaptureRequest with ACAMERA_SENSOR_PIXEL_MODE set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int32[n*4] (acamera_metadata_enum_android_scaler_available_stream_configurations_maximum_resolution_t)</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, for configurations
+ * which are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ * <p>Not all output formats may be supported in a configuration with
+ * an input stream of a particular format. For more details, see
+ * android.scaler.availableInputOutputFormatsMapMaximumResolution.</p>
+ *
+ * @see ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION =
+ // int32[n*4] (acamera_metadata_enum_android_scaler_available_stream_configurations_maximum_resolution_t)
+ ACAMERA_SCALER_START + 20,
+ /**
+ * <p>This lists the minimum frame duration for each
+ * format/size combination when the camera device is sent a CaptureRequest with
+ * ACAMERA_SENSOR_PIXEL_MODE set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int64[4*n]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS, for configurations
+ * which are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ * <p>When multiple streams are used in a request (if supported, when ACAMERA_SENSOR_PIXEL_MODE
+ * is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>), the
+ * minimum frame duration will be max(individual stream min durations).</p>
+ * <p>See ACAMERA_SENSOR_FRAME_DURATION and
+ * ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION for more details about
+ * calculating the max frame rate.</p>
+ *
+ * @see ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS
+ * @see ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION
+ * @see ACAMERA_SENSOR_FRAME_DURATION
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION =
+ // int64[4*n]
+ ACAMERA_SCALER_START + 21,
+ /**
+ * <p>This lists the maximum stall duration for each
+ * output format/size combination when CaptureRequests are submitted with
+ * ACAMERA_SENSOR_PIXEL_MODE set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a></p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int64[4*n]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS, for configurations
+ * which are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION =
+ // int64[4*n]
+ ACAMERA_SCALER_START + 22,
+ /**
+ * <p>Whether the camera device supports multi-resolution input or output streams</p>
+ *
+ * <p>Type: byte (acamera_metadata_enum_android_scaler_multi_resolution_stream_supported_t)</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>A logical multi-camera or an ultra high resolution camera may support multi-resolution
+ * input or output streams. With multi-resolution output streams, the camera device is able
+ * to output different resolution images depending on the current active physical camera or
+ * pixel mode. With multi-resolution input streams, the camera device can reprocess images
+ * of different resolutions from different physical cameras or sensor pixel modes.</p>
+ * <p>When set to TRUE:
+ * * For a logical multi-camera, the camera framework derives
+ * android.scaler.multiResolutionStreamConfigurationMap by combining the
+ * ACAMERA_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS from its physical
+ * cameras.
+ * * For an ultra-high resolution sensor camera, the camera framework directly copies
+ * the value of ACAMERA_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS to
+ * android.scaler.multiResolutionStreamConfigurationMap.</p>
+ *
+ * @see ACAMERA_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS
+ */
+ ACAMERA_SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED = // byte (acamera_metadata_enum_android_scaler_multi_resolution_stream_supported_t)
+ ACAMERA_SCALER_START + 24,
ACAMERA_SCALER_END,
/**
@@ -4525,6 +4893,67 @@
*/
ACAMERA_SENSOR_DYNAMIC_WHITE_LEVEL = // int32
ACAMERA_SENSOR_START + 29,
+ /**
+ * <p>Switches sensor pixel mode between maximum resolution mode and default mode.</p>
+ *
+ * <p>Type: byte (acamera_metadata_enum_android_sensor_pixel_mode_t)</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
+ * <li>ACaptureRequest</li>
+ * </ul></p>
+ *
+ * <p>This key controls whether the camera sensor operates in
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>
+ * mode or not. By default, all camera devices operate in
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_DEFAULT">CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT</a> mode.
+ * When operating in
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_DEFAULT">CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT</a> mode, sensors
+ * with <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * capability would typically perform pixel binning in order to improve low light
+ * performance, noise reduction etc. However, in
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>
+ * mode (supported only
+ * by <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * sensors), sensors typically operate in unbinned mode allowing for a larger image size.
+ * The stream configurations supported in
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>
+ * mode are also different from those of
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_DEFAULT">CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT</a> mode.
+ * They can be queried through
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html#get">CameraCharacteristics#get</a> with
+ * <a href="https://developer.android.com/reference/CameraCharacteristics.html#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION)">CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION)</a>.
+ * Unless reported by both
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/params/StreamConfigurationMap.html">StreamConfigurationMap</a>s, the outputs from
+ * <code>android.scaler.streamConfigurationMapMaximumResolution</code> and
+ * <code>android.scaler.streamConfigurationMap</code>
+ * must not be mixed in the same CaptureRequest. In other words, these outputs are
+ * exclusive to each other.
+ * This key does not need to be set for reprocess requests.</p>
+ */
+ ACAMERA_SENSOR_PIXEL_MODE = // byte (acamera_metadata_enum_android_sensor_pixel_mode_t)
+ ACAMERA_SENSOR_START + 32,
+ /**
+ * <p>Whether <code>RAW</code> images requested have their bayer pattern as described by
+ * ACAMERA_SENSOR_INFO_BINNING_FACTOR.</p>
+ *
+ * @see ACAMERA_SENSOR_INFO_BINNING_FACTOR
+ *
+ * <p>Type: byte (acamera_metadata_enum_android_sensor_raw_binning_factor_used_t)</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
+ * </ul></p>
+ *
+ * <p>This key will only be present in devices advertisting the
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * capability which also advertise <code>REMOSAIC_REPROCESSING</code> capability. On all other devices
+ * RAW targets will have a regular bayer pattern.</p>
+ */
+ ACAMERA_SENSOR_RAW_BINNING_FACTOR_USED = // byte (acamera_metadata_enum_android_sensor_raw_binning_factor_used_t)
+ ACAMERA_SENSOR_START + 33,
ACAMERA_SENSOR_END,
/**
@@ -4782,7 +5211,7 @@
* rectangle, and cropping to the rectangle given in ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.</p>
* <p>E.g. to calculate position of a pixel, (x,y), in a processed YUV output image with the
* dimensions in ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE given the position of a pixel,
- * (x', y'), in the raw pixel array with dimensions give in
+ * (x', y'), in the raw pixel array with dimensions given in
* ACAMERA_SENSOR_INFO_PIXEL_ARRAY_SIZE:</p>
* <ol>
* <li>Choose a pixel (x', y') within the active array region of the raw buffer given in
@@ -4826,6 +5255,120 @@
*/
ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE = // int32[4]
ACAMERA_SENSOR_INFO_START + 10,
+ /**
+ * <p>The area of the image sensor which corresponds to active pixels after any geometric
+ * distortion correction has been applied, when the sensor runs in maximum resolution mode.</p>
+ *
+ * <p>Type: int32[4]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, when ACAMERA_SENSOR_PIXEL_MODE
+ * is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.
+ * Refer to ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE for details, with sensor array related keys
+ * replaced with their
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>
+ * counterparts.
+ * This key will only be present for devices which advertise the
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * capability.</p>
+ * <p>The data representation is <code>int[4]</code>, which maps to <code>(left, top, width, height)</code>.</p>
+ *
+ * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION = // int32[4]
+ ACAMERA_SENSOR_INFO_START + 11,
+ /**
+ * <p>Dimensions of the full pixel array, possibly
+ * including black calibration pixels, when the sensor runs in maximum resolution mode.
+ * Analogous to ACAMERA_SENSOR_INFO_PIXEL_ARRAY_SIZE, when ACAMERA_SENSOR_PIXEL_MODE is
+ * set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_INFO_PIXEL_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int32[2]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>The pixel count of the full pixel array of the image sensor, which covers
+ * ACAMERA_SENSOR_INFO_PHYSICAL_SIZE area. This represents the full pixel dimensions of
+ * the raw buffers produced by this sensor, when it runs in maximum resolution mode. That
+ * is, when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.
+ * This key will only be present for devices which advertise the
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * capability.</p>
+ *
+ * @see ACAMERA_SENSOR_INFO_PHYSICAL_SIZE
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_SENSOR_INFO_PIXEL_ARRAY_SIZE_MAXIMUM_RESOLUTION = // int32[2]
+ ACAMERA_SENSOR_INFO_START + 12,
+ /**
+ * <p>The area of the image sensor which corresponds to active pixels prior to the
+ * application of any geometric distortion correction, when the sensor runs in maximum
+ * resolution mode. This key must be used for crop / metering regions, only when
+ * ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int32[4]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
+ * when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.
+ * This key will only be present for devices which advertise the
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * capability.</p>
+ * <p>The data representation is <code>int[4]</code>, which maps to <code>(left, top, width, height)</code>.</p>
+ *
+ * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION =
+ // int32[4]
+ ACAMERA_SENSOR_INFO_START + 13,
+ /**
+ * <p>Dimensions of the group of pixels which are under the same color filter.
+ * This specifies the width and height (pair of integers) of the group of pixels which fall
+ * under the same color filter for ULTRA_HIGH_RESOLUTION sensors.</p>
+ *
+ * <p>Type: int32[2]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Sensors can have pixels grouped together under the same color filter in order
+ * to improve various aspects of imaging such as noise reduction, low light
+ * performance etc. These groups can be of various sizes such as 2X2 (quad bayer),
+ * 3X3 (nona-bayer). This key specifies the length and width of the pixels grouped under
+ * the same color filter.</p>
+ * <p>This key will not be present if REMOSAIC_REPROCESSING is not supported, since RAW images
+ * will have a regular bayer pattern.</p>
+ * <p>This key will not be present for sensors which don't have the
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>
+ * capability.</p>
+ */
+ ACAMERA_SENSOR_INFO_BINNING_FACTOR = // int32[2]
+ ACAMERA_SENSOR_INFO_START + 14,
ACAMERA_SENSOR_INFO_END,
/**
@@ -5226,7 +5769,7 @@
* </ul></p>
*
* <p>Since optical image stabilization generally involves motion much faster than the duration
- * of individualq image exposure, multiple OIS samples can be included for a single capture
+ * of individual image exposure, multiple OIS samples can be included for a single capture
* result. For example, if the OIS reporting operates at 200 Hz, a typical camera operating
* at 30fps may have 6-7 OIS samples per capture result. This information can be combined
* with the rolling shutter skew to account for lens motion during image exposure in
@@ -6031,6 +6574,162 @@
*/
ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS = // int64[4*n]
ACAMERA_DEPTH_START + 8,
+ /**
+ * <p>The available depth dataspace stream
+ * configurations that this camera device supports
+ * (i.e. format, width, height, output/input stream) when a CaptureRequest is submitted with
+ * ACAMERA_SENSOR_PIXEL_MODE set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int32[n*4] (acamera_metadata_enum_android_depth_available_depth_stream_configurations_maximum_resolution_t)</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, for configurations which
+ * are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION =
+ // int32[n*4] (acamera_metadata_enum_android_depth_available_depth_stream_configurations_maximum_resolution_t)
+ ACAMERA_DEPTH_START + 9,
+ /**
+ * <p>This lists the minimum frame duration for each
+ * format/size combination for depth output formats when a CaptureRequest is submitted with
+ * ACAMERA_SENSOR_PIXEL_MODE set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int64[4*n]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS, for configurations which
+ * are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ * <p>See ACAMERA_SENSOR_FRAME_DURATION and
+ * ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION for more details about
+ * calculating the max frame rate.</p>
+ *
+ * @see ACAMERA_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS
+ * @see ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION
+ * @see ACAMERA_SENSOR_FRAME_DURATION
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION =
+ // int64[4*n]
+ ACAMERA_DEPTH_START + 10,
+ /**
+ * <p>This lists the maximum stall duration for each
+ * output format/size combination for depth streams for CaptureRequests where
+ * ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int64[4*n]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS, for configurations which
+ * are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION =
+ // int64[4*n]
+ ACAMERA_DEPTH_START + 11,
+ /**
+ * <p>The available dynamic depth dataspace stream
+ * configurations that this camera device supports (i.e. format, width, height,
+ * output/input stream) for CaptureRequests where ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int32[n*4] (acamera_metadata_enum_android_depth_available_dynamic_depth_stream_configurations_maximum_resolution_t)</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS, for configurations
+ * which are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION =
+ // int32[n*4] (acamera_metadata_enum_android_depth_available_dynamic_depth_stream_configurations_maximum_resolution_t)
+ ACAMERA_DEPTH_START + 12,
+ /**
+ * <p>This lists the minimum frame duration for each
+ * format/size combination for dynamic depth output streams for CaptureRequests where
+ * ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int64[4*n]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS, for configurations
+ * which are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION =
+ // int64[4*n]
+ ACAMERA_DEPTH_START + 13,
+ /**
+ * <p>This lists the maximum stall duration for each
+ * output format/size combination for dynamic depth streams for CaptureRequests where
+ * ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int64[4*n]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Analogous to ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS, for configurations
+ * which are applicable when ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ */
+ ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION =
+ // int64[4*n]
+ ACAMERA_DEPTH_START + 14,
ACAMERA_DEPTH_END,
/**
@@ -6251,6 +6950,71 @@
*/
ACAMERA_HEIC_AVAILABLE_HEIC_STALL_DURATIONS = // int64[4*n]
ACAMERA_HEIC_START + 2,
+ /**
+ * <p>The available HEIC (ISO/IEC 23008-12) stream
+ * configurations that this camera device supports
+ * (i.e. format, width, height, output/input stream).</p>
+ *
+ * <p>Type: int32[n*4] (acamera_metadata_enum_android_heic_available_heic_stream_configurations_maximum_resolution_t)</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Refer to ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS for details.</p>
+ * <p>All the configuration tuples <code>(format, width, height, input?)</code> will contain
+ * AIMAGE_FORMAT_HEIC format as OUTPUT only.</p>
+ *
+ * @see ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS
+ */
+ ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION =
+ // int32[n*4] (acamera_metadata_enum_android_heic_available_heic_stream_configurations_maximum_resolution_t)
+ ACAMERA_HEIC_START + 3,
+ /**
+ * <p>This lists the minimum frame duration for each
+ * format/size combination for HEIC output formats for CaptureRequests where
+ * ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int64[4*n]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Refer to ACAMERA_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS for details.</p>
+ *
+ * @see ACAMERA_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS
+ */
+ ACAMERA_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION =
+ // int64[4*n]
+ ACAMERA_HEIC_START + 4,
+ /**
+ * <p>This lists the maximum stall duration for each
+ * output format/size combination for HEIC streams for CaptureRequests where
+ * ACAMERA_SENSOR_PIXEL_MODE is set to
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION</a>.</p>
+ *
+ * @see ACAMERA_SENSOR_PIXEL_MODE
+ *
+ * <p>Type: int64[4*n]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>Refer to ACAMERA_HEIC_AVAILABLE_HEIC_STALL_DURATIONS for details.</p>
+ *
+ * @see ACAMERA_HEIC_AVAILABLE_HEIC_STALL_DURATIONS
+ */
+ ACAMERA_HEIC_AVAILABLE_HEIC_STALL_DURATIONS_MAXIMUM_RESOLUTION =
+ // int64[4*n]
+ ACAMERA_HEIC_START + 5,
ACAMERA_HEIC_END,
} acamera_metadata_tag_t;
@@ -8201,6 +8965,20 @@
*/
ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA = 14,
+ /**
+ * <p>This camera device is capable of producing ultra high resolution images in
+ * addition to the image sizes described in the
+ * android.scaler.streamConfigurationMap.
+ * It can operate in 'default' mode and 'max resolution' mode. It generally does this
+ * by binning pixels in 'default' mode and not binning them in 'max resolution' mode.
+ * <code>android.scaler.streamConfigurationMap</code> describes the streams supported in 'default'
+ * mode.
+ * The stream configurations supported in 'max resolution' mode are described by
+ * <code>android.scaler.streamConfigurationMapMaximumResolution</code>.</p>
+ */
+ ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR
+ = 16,
+
} acamera_metadata_enum_android_request_available_capabilities_t;
@@ -8301,6 +9079,79 @@
} acamera_metadata_enum_android_scaler_available_recommended_stream_configurations_t;
+// ACAMERA_SCALER_ROTATE_AND_CROP
+typedef enum acamera_metadata_enum_acamera_scaler_rotate_and_crop {
+ /**
+ * <p>No rotate and crop is applied. Processed outputs are in the sensor orientation.</p>
+ */
+ ACAMERA_SCALER_ROTATE_AND_CROP_NONE = 0,
+
+ /**
+ * <p>Processed images are rotated by 90 degrees clockwise, and then cropped
+ * to the original aspect ratio.</p>
+ */
+ ACAMERA_SCALER_ROTATE_AND_CROP_90 = 1,
+
+ /**
+ * <p>Processed images are rotated by 180 degrees. Since the aspect ratio does not
+ * change, no cropping is performed.</p>
+ */
+ ACAMERA_SCALER_ROTATE_AND_CROP_180 = 2,
+
+ /**
+ * <p>Processed images are rotated by 270 degrees clockwise, and then cropped
+ * to the original aspect ratio.</p>
+ */
+ ACAMERA_SCALER_ROTATE_AND_CROP_270 = 3,
+
+ /**
+ * <p>The camera API automatically selects the best concrete value for
+ * rotate-and-crop based on the application's support for resizability and the current
+ * multi-window mode.</p>
+ * <p>If the application does not support resizing but the display mode for its main
+ * Activity is not in a typical orientation, the camera API will set <code>ROTATE_AND_CROP_90</code>
+ * or some other supported rotation value, depending on device configuration,
+ * to ensure preview and captured images are correctly shown to the user. Otherwise,
+ * <code>ROTATE_AND_CROP_NONE</code> will be selected.</p>
+ * <p>When a value other than NONE is selected, several metadata fields will also be parsed
+ * differently to ensure that coordinates are correctly handled for features like drawing
+ * face detection boxes or passing in tap-to-focus coordinates. The camera API will
+ * convert positions in the active array coordinate system to/from the cropped-and-rotated
+ * coordinate system to make the operation transparent for applications.</p>
+ * <p>No coordinate mapping will be done when the application selects a non-AUTO mode.</p>
+ */
+ ACAMERA_SCALER_ROTATE_AND_CROP_AUTO = 4,
+
+} acamera_metadata_enum_android_scaler_rotate_and_crop_t;
+
+// ACAMERA_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS
+typedef enum acamera_metadata_enum_acamera_scaler_physical_camera_multi_resolution_stream_configurations {
+ ACAMERA_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS_OUTPUT
+ = 0,
+
+ ACAMERA_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS_INPUT
+ = 1,
+
+} acamera_metadata_enum_android_scaler_physical_camera_multi_resolution_stream_configurations_t;
+
+// ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION
+typedef enum acamera_metadata_enum_acamera_scaler_available_stream_configurations_maximum_resolution {
+ ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_OUTPUT
+ = 0,
+
+ ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_INPUT
+ = 1,
+
+} acamera_metadata_enum_android_scaler_available_stream_configurations_maximum_resolution_t;
+
+// ACAMERA_SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED
+typedef enum acamera_metadata_enum_acamera_scaler_multi_resolution_stream_supported {
+ ACAMERA_SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED_FALSE = 0,
+
+ ACAMERA_SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED_TRUE = 1,
+
+} acamera_metadata_enum_android_scaler_multi_resolution_stream_supported_t;
+
// ACAMERA_SENSOR_REFERENCE_ILLUMINANT1
typedef enum acamera_metadata_enum_acamera_sensor_reference_illuminant1 {
@@ -8373,10 +9224,10 @@
* respective color channel provided in
* ACAMERA_SENSOR_TEST_PATTERN_DATA.</p>
* <p>For example:</p>
- * <pre><code>android.testPatternData = [0, 0xFFFFFFFF, 0xFFFFFFFF, 0]
+ * <pre><code>android.control.testPatternData = [0, 0xFFFFFFFF, 0xFFFFFFFF, 0]
* </code></pre>
* <p>All green pixels are 100% green. All red/blue pixels are black.</p>
- * <pre><code>android.testPatternData = [0xFFFFFFFF, 0, 0xFFFFFFFF, 0]
+ * <pre><code>android.control.testPatternData = [0xFFFFFFFF, 0, 0xFFFFFFFF, 0]
* </code></pre>
* <p>All red pixels are 100% red. Only the odd green pixels
* are 100% green. All blue pixels are 100% black.</p>
@@ -8459,6 +9310,42 @@
} acamera_metadata_enum_android_sensor_test_pattern_mode_t;
+// ACAMERA_SENSOR_PIXEL_MODE
+typedef enum acamera_metadata_enum_acamera_sensor_pixel_mode {
+ /**
+ * <p>This is the default sensor pixel mode. This is the only sensor pixel mode
+ * supported unless a camera device advertises
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>.</p>
+ */
+ ACAMERA_SENSOR_PIXEL_MODE_DEFAULT = 0,
+
+ /**
+ * <p>This sensor pixel mode is offered by devices with capability
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR">CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR</a>.
+ * In this mode, sensors typically do not bin pixels, as a result can offer larger
+ * image sizes.</p>
+ */
+ ACAMERA_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION = 1,
+
+} acamera_metadata_enum_android_sensor_pixel_mode_t;
+
+// ACAMERA_SENSOR_RAW_BINNING_FACTOR_USED
+typedef enum acamera_metadata_enum_acamera_sensor_raw_binning_factor_used {
+ /**
+ * <p>The <code>RAW</code> targets in this capture have ACAMERA_SENSOR_INFO_BINNING_FACTOR as the
+ * bayer pattern.</p>
+ *
+ * @see ACAMERA_SENSOR_INFO_BINNING_FACTOR
+ */
+ ACAMERA_SENSOR_RAW_BINNING_FACTOR_USED_TRUE = 0,
+
+ /**
+ * <p>The <code>RAW</code> targets have a regular bayer pattern in this capture.</p>
+ */
+ ACAMERA_SENSOR_RAW_BINNING_FACTOR_USED_FALSE = 1,
+
+} acamera_metadata_enum_android_sensor_raw_binning_factor_used_t;
+
// ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
typedef enum acamera_metadata_enum_acamera_sensor_info_color_filter_arrangement {
@@ -8943,6 +9830,26 @@
} acamera_metadata_enum_android_depth_available_dynamic_depth_stream_configurations_t;
+// ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION
+typedef enum acamera_metadata_enum_acamera_depth_available_depth_stream_configurations_maximum_resolution {
+ ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_OUTPUT
+ = 0,
+
+ ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_INPUT
+ = 1,
+
+} acamera_metadata_enum_android_depth_available_depth_stream_configurations_maximum_resolution_t;
+
+// ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION
+typedef enum acamera_metadata_enum_acamera_depth_available_dynamic_depth_stream_configurations_maximum_resolution {
+ ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_OUTPUT
+ = 0,
+
+ ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_INPUT
+ = 1,
+
+} acamera_metadata_enum_android_depth_available_dynamic_depth_stream_configurations_maximum_resolution_t;
+
// ACAMERA_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
typedef enum acamera_metadata_enum_acamera_logical_multi_camera_sensor_sync_type {
@@ -8994,6 +9901,17 @@
} acamera_metadata_enum_android_heic_available_heic_stream_configurations_t;
+// ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION
+typedef enum acamera_metadata_enum_acamera_heic_available_heic_stream_configurations_maximum_resolution {
+ ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_OUTPUT
+ = 0,
+
+ ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION_INPUT
+ = 1,
+
+} acamera_metadata_enum_android_heic_available_heic_stream_configurations_maximum_resolution_t;
+
+
__END_DECLS
diff --git a/camera/ndk/include/camera/NdkCameraWindowType.h b/camera/ndk/include/camera/NdkCameraWindowType.h
index 99f67e9..0838fba 100644
--- a/camera/ndk/include/camera/NdkCameraWindowType.h
+++ b/camera/ndk/include/camera/NdkCameraWindowType.h
@@ -44,10 +44,12 @@
*/
#ifdef __ANDROID_VNDK__
#include <cutils/native_handle.h>
-typedef native_handle_t ACameraWindowType;
+typedef const native_handle_t ACameraWindowType;
#else
#include <android/native_window.h>
typedef ANativeWindow ACameraWindowType;
#endif
+/** @} */
+
#endif //_NDK_CAMERA_WINDOW_TYPE_H
diff --git a/camera/ndk/include/camera/NdkCaptureRequest.h b/camera/ndk/include/camera/NdkCaptureRequest.h
index a4dc374..d83c5b3 100644
--- a/camera/ndk/include/camera/NdkCaptureRequest.h
+++ b/camera/ndk/include/camera/NdkCaptureRequest.h
@@ -44,10 +44,10 @@
__BEGIN_DECLS
-// Container for output targets
+/** Container for output targets */
typedef struct ACameraOutputTargets ACameraOutputTargets;
-// Container for a single output target
+/** Container for a single output target */
typedef struct ACameraOutputTarget ACameraOutputTarget;
/**
@@ -383,10 +383,10 @@
* Set/change a camera capture control entry with unsigned 8 bits data type for
* a physical camera backing a logical multi-camera device.
*
- * <p>Same as ACaptureRequest_setEntry_u8, except that if {@link tag} is contained
+ * <p>Same as ACaptureRequest_setEntry_u8, except that if tag is contained
* in {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, this function
* sets the entry for a particular physical sub-camera backing the logical multi-camera.
- * If {@link tag} is not contained in
+ * If tag is not contained in
* {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored
* by the camera device.</p>
*
@@ -413,10 +413,10 @@
* Set/change a camera capture control entry with signed 32 bits data type for
* a physical camera of a logical multi-camera device.
*
- * <p>Same as ACaptureRequest_setEntry_i32, except that if {@link tag} is contained
+ * <p>Same as ACaptureRequest_setEntry_i32, except that if tag is contained
* in {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, this function
* sets the entry for a particular physical sub-camera backing the logical multi-camera.
- * If {@link tag} is not contained in
+ * If tag is not contained in
* {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored
* by the camera device.</p>
*
@@ -443,10 +443,10 @@
* Set/change a camera capture control entry with float data type for
* a physical camera of a logical multi-camera device.
*
- * <p>Same as ACaptureRequest_setEntry_float, except that if {@link tag} is contained
+ * <p>Same as ACaptureRequest_setEntry_float, except that if tag is contained
* in {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, this function
* sets the entry for a particular physical sub-camera backing the logical multi-camera.
- * If {@link tag} is not contained in
+ * If tag is not contained in
* {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored
* by the camera device.</p>
*
@@ -473,10 +473,10 @@
* Set/change a camera capture control entry with signed 64 bits data type for
* a physical camera of a logical multi-camera device.
*
- * <p>Same as ACaptureRequest_setEntry_i64, except that if {@link tag} is contained
+ * <p>Same as ACaptureRequest_setEntry_i64, except that if tag is contained
* in {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, this function
* sets the entry for a particular physical sub-camera backing the logical multi-camera.
- * If {@link tag} is not contained in
+ * If tag is not contained in
* {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored
* by the camera device.</p>
*
@@ -503,10 +503,10 @@
* Set/change a camera capture control entry with double data type for
* a physical camera of a logical multi-camera device.
*
- * <p>Same as ACaptureRequest_setEntry_double, except that if {@link tag} is contained
+ * <p>Same as ACaptureRequest_setEntry_double, except that if tag is contained
* in {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, this function
* sets the entry for a particular physical sub-camera backing the logical multi-camera.
- * If {@link tag} is not contained in
+ * If tag is not contained in
* {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored
* by the camera device.</p>
*
@@ -533,10 +533,10 @@
* Set/change a camera capture control entry with rational data type for
* a physical camera of a logical multi-camera device.
*
- * <p>Same as ACaptureRequest_setEntry_rational, except that if {@link tag} is contained
+ * <p>Same as ACaptureRequest_setEntry_rational, except that if tag is contained
* in {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, this function
* sets the entry for a particular physical sub-camera backing the logical multi-camera.
- * If {@link tag} is not contained in
+ * If tag is not contained in
* {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored
* by the camera device.</p>
*
diff --git a/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h b/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h
index e1af8c1..5a1af79 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h
+++ b/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h
@@ -18,7 +18,7 @@
#include "utils.h"
struct ACaptureSessionOutput {
- explicit ACaptureSessionOutput(native_handle_t* window, bool isShared = false,
+ explicit ACaptureSessionOutput(const native_handle_t* window, bool isShared = false,
const char* physicalCameraId = "") :
mWindow(window), mIsShared(isShared), mPhysicalCameraId(physicalCameraId) {};
diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
index e511a3f..9f63099 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
+++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
@@ -180,6 +180,7 @@
const ACaptureRequest* sessionParameters,
const ACameraCaptureSession_stateCallbacks* callbacks,
/*out*/ACameraCaptureSession** session) {
+ nsecs_t startTimeNs = systemTime();
sp<ACameraCaptureSession> currentSession = mCurrentSession.promote();
Mutex::Autolock _l(mDeviceLock);
camera_status_t ret = checkCameraClosedOrErrorLocked();
@@ -193,7 +194,7 @@
}
// Create new session
- ret = configureStreamsLocked(outputs, sessionParameters);
+ ret = configureStreamsLocked(outputs, sessionParameters, startTimeNs);
if (ret != ACAMERA_OK) {
ALOGE("Fail to create new session. cannot configure streams");
return ret;
@@ -355,7 +356,7 @@
std::vector<int32_t> requestStreamIdxList;
std::vector<int32_t> requestSurfaceIdxList;
for (auto outputTarget : request->targets->mOutputs) {
- native_handle_t* anw = outputTarget.mWindow;
+ const native_handle_t* anw = outputTarget.mWindow;
bool found = false;
req->mSurfaceList.push_back(anw);
// lookup stream/surface ID
@@ -434,7 +435,7 @@
}
pRequest->targets = new ACameraOutputTargets();
for (size_t i = 0; i < req->mSurfaceList.size(); i++) {
- native_handle_t* anw = req->mSurfaceList[i];
+ const native_handle_t* anw = req->mSurfaceList[i];
ACameraOutputTarget outputTarget(anw);
pRequest->targets->mOutputs.insert(outputTarget);
}
@@ -472,7 +473,11 @@
}
// No new session, unconfigure now
- camera_status_t ret = configureStreamsLocked(nullptr, nullptr);
+ // Note: The unconfiguration of session won't be accounted for session
+ // latency because a stream configuration with 0 streams won't ever become
+ // active.
+ nsecs_t startTimeNs = systemTime();
+ camera_status_t ret = configureStreamsLocked(nullptr, nullptr, startTimeNs);
if (ret != ACAMERA_OK) {
ALOGE("Unconfigure stream failed. Device might still be configured! ret %d", ret);
}
@@ -598,7 +603,7 @@
camera_status_t
CameraDevice::configureStreamsLocked(const ACaptureSessionOutputContainer* outputs,
- const ACaptureRequest* sessionParameters) {
+ const ACaptureRequest* sessionParameters, nsecs_t startTimeNs) {
ACaptureSessionOutputContainer emptyOutput;
if (outputs == nullptr) {
outputs = &emptyOutput;
@@ -611,7 +616,7 @@
std::set<std::pair<native_handle_ptr_wrapper, OutputConfigurationWrapper>> outputSet;
for (auto outConfig : outputs->mOutputs) {
- native_handle_t* anw = outConfig.mWindow;
+ const native_handle_t* anw = outConfig.mWindow;
OutputConfigurationWrapper outConfigInsertW;
OutputConfiguration &outConfigInsert = outConfigInsertW.mOutputConfiguration;
outConfigInsert.rotation = utils::convertToHidl(outConfig.mRotation);
@@ -697,7 +702,8 @@
utils::convertToHidl(params_metadata, &hidlParams);
params.unlock(params_metadata);
}
- remoteRet = mRemote->endConfigure(StreamConfigurationMode::NORMAL_MODE, hidlParams);
+ remoteRet = mRemote->endConfigure_2_1(StreamConfigurationMode::NORMAL_MODE,
+ hidlParams, startTimeNs);
CHECK_TRANSACTION_AND_RET(remoteRet, remoteRet, "endConfigure()")
return ACAMERA_OK;
}
@@ -846,8 +852,7 @@
for (auto streamAndWindowId : request->mCaptureRequest.streamAndWindowIds) {
int32_t windowId = streamAndWindowId.windowId;
if (utils::isWindowNativeHandleEqual(windowHandles[windowId],outHandle)) {
- native_handle_t* anw =
- const_cast<native_handle_t *>(windowHandles[windowId].getNativeHandle());
+ const native_handle_t* anw = windowHandles[windowId].getNativeHandle();
ALOGV("Camera %s Lost output buffer for ANW %p frame %" PRId64,
getId(), anw, frameNumber);
@@ -1244,7 +1249,7 @@
return;
}
- native_handle_t* anw;
+ const native_handle_t* anw;
found = msg->findPointer(kAnwKey, (void**) &anw);
if (!found) {
ALOGE("%s: Cannot find native_handle_t!", __FUNCTION__);
diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.h b/camera/ndk/ndk_vendor/impl/ACameraDevice.h
index 7fc699e..0b6c7c8 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraDevice.h
+++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.h
@@ -26,7 +26,7 @@
#include <utils/Mutex.h>
#include <utils/List.h>
#include <utils/Vector.h>
-#include <android/frameworks/cameraservice/device/2.0/ICameraDeviceUser.h>
+#include <android/frameworks/cameraservice/device/2.1/ICameraDeviceUser.h>
#include <android/frameworks/cameraservice/device/2.0/ICameraDeviceCallback.h>
#include <android/frameworks/cameraservice/device/2.0/types.h>
#include <fmq/MessageQueue.h>
@@ -44,7 +44,8 @@
namespace acam {
using ICameraDeviceCallback = frameworks::cameraservice::device::V2_0::ICameraDeviceCallback;
-using ICameraDeviceUser = frameworks::cameraservice::device::V2_0::ICameraDeviceUser;
+using ICameraDeviceUser_2_0 = frameworks::cameraservice::device::V2_0::ICameraDeviceUser;
+using ICameraDeviceUser = frameworks::cameraservice::device::V2_1::ICameraDeviceUser;
using CaptureResultExtras = frameworks::cameraservice::device::V2_0::CaptureResultExtras;
using PhysicalCaptureResultInfo = frameworks::cameraservice::device::V2_0::PhysicalCaptureResultInfo;
using PhysicalCameraSettings = frameworks::cameraservice::device::V2_0::PhysicalCameraSettings;
@@ -201,7 +202,7 @@
void notifySessionEndOfLifeLocked(ACameraCaptureSession* session);
camera_status_t configureStreamsLocked(const ACaptureSessionOutputContainer* outputs,
- const ACaptureRequest* sessionParameters);
+ const ACaptureRequest* sessionParameters, nsecs_t startTimeNs);
// Input message will be posted and cleared after this returns
void postSessionMsgAndCleanup(sp<AMessage>& msg);
diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp
index 5aa9c46..77c934a 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp
+++ b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp
@@ -764,15 +764,15 @@
}
sp<ICameraDeviceCallback> callbacks = device->getServiceCallback();
- sp<ICameraDeviceUser> deviceRemote;
+ sp<ICameraDeviceUser_2_0> deviceRemote_2_0;
// No way to get package name from native.
// Send a zero length package name and let camera service figure it out from UID
Status status = Status::NO_ERROR;
auto serviceRet = cs->connectDevice(
- callbacks, cameraId, [&status, &deviceRemote](auto s, auto &device) {
+ callbacks, cameraId, [&status, &deviceRemote_2_0](auto s, auto &device) {
status = s;
- deviceRemote = device;
+ deviceRemote_2_0 = device;
});
if (!serviceRet.isOk() || status != Status::NO_ERROR) {
@@ -780,11 +780,18 @@
delete device;
return utils::convertFromHidl(status);
}
- if (deviceRemote == nullptr) {
+ if (deviceRemote_2_0 == nullptr) {
ALOGE("%s: connect camera device failed! remote device is null", __FUNCTION__);
delete device;
return ACAMERA_ERROR_CAMERA_DISCONNECTED;
}
+ auto castResult = ICameraDeviceUser::castFrom(deviceRemote_2_0);
+ if (!castResult.isOk()) {
+ ALOGE("%s: failed to cast remote device to version 2.1", __FUNCTION__);
+ delete device;
+ return ACAMERA_ERROR_CAMERA_DISCONNECTED;
+ }
+ sp<ICameraDeviceUser> deviceRemote = castResult;
device->setRemoteDevice(deviceRemote);
device->setDeviceMetadataQueues();
*outDevice = device;
diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.h b/camera/ndk/ndk_vendor/impl/ACameraManager.h
index 85da3e9..8359bb1 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraManager.h
+++ b/camera/ndk/ndk_vendor/impl/ACameraManager.h
@@ -22,6 +22,7 @@
#include <android-base/parseint.h>
#include <android/frameworks/cameraservice/service/2.0/ICameraService.h>
#include <android/frameworks/cameraservice/service/2.1/ICameraService.h>
+#include <android/frameworks/cameraservice/service/2.2/ICameraService.h>
#include <android/frameworks/cameraservice/service/2.1/ICameraServiceListener.h>
#include <CameraMetadata.h>
@@ -38,7 +39,7 @@
namespace android {
namespace acam {
-using ICameraService = frameworks::cameraservice::service::V2_1::ICameraService;
+using ICameraService = frameworks::cameraservice::service::V2_2::ICameraService;
using CameraDeviceStatus = frameworks::cameraservice::service::V2_0::CameraDeviceStatus;
using ICameraServiceListener = frameworks::cameraservice::service::V2_1::ICameraServiceListener;
using PhysicalCameraStatusAndId = frameworks::cameraservice::service::V2_1::PhysicalCameraStatusAndId;
diff --git a/camera/ndk/ndk_vendor/impl/ACaptureRequestVendor.h b/camera/ndk/ndk_vendor/impl/ACaptureRequestVendor.h
index ed67615..5715d77 100644
--- a/camera/ndk/ndk_vendor/impl/ACaptureRequestVendor.h
+++ b/camera/ndk/ndk_vendor/impl/ACaptureRequestVendor.h
@@ -17,7 +17,7 @@
#include "utils.h"
struct ACameraOutputTarget {
- explicit ACameraOutputTarget(native_handle_t* window) : mWindow(window) {};
+ explicit ACameraOutputTarget(const native_handle_t* window) : mWindow(window) {};
bool operator == (const ACameraOutputTarget& other) const {
return mWindow == other.mWindow;
diff --git a/camera/ndk/ndk_vendor/impl/utils.h b/camera/ndk/ndk_vendor/impl/utils.h
index f389f03..6f5820e 100644
--- a/camera/ndk/ndk_vendor/impl/utils.h
+++ b/camera/ndk/ndk_vendor/impl/utils.h
@@ -42,7 +42,7 @@
// Utility class so that CaptureRequest can be stored by sp<>
struct CaptureRequest : public RefBase {
frameworks::cameraservice::device::V2_0::CaptureRequest mCaptureRequest;
- std::vector<native_handle_t *> mSurfaceList;
+ std::vector<const native_handle_t *> mSurfaceList;
//Physical camera settings metadata is stored here, since the capture request
//might not contain it. That's since, fmq might have consumed it.
hidl_vec<PhysicalCameraSettings> mPhysicalCameraSettings;
@@ -62,13 +62,13 @@
// Utility class so the native_handle_t can be compared with its contents instead
// of just raw pointer comparisons.
struct native_handle_ptr_wrapper {
- native_handle_t *mWindow = nullptr;
+ const native_handle_t *mWindow = nullptr;
- native_handle_ptr_wrapper(native_handle_t *nh) : mWindow(nh) { }
+ native_handle_ptr_wrapper(const native_handle_t *nh) : mWindow(nh) { }
native_handle_ptr_wrapper() = default;
- operator native_handle_t *() const { return mWindow; }
+ operator const native_handle_t *() const { return mWindow; }
bool operator ==(const native_handle_ptr_wrapper other) const {
return isWindowNativeHandleEqual(mWindow, other.mWindow);
diff --git a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp
index 938b5f5..ba14c5c 100644
--- a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp
+++ b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp
@@ -50,7 +50,7 @@
static constexpr int kTestImageFormat = AIMAGE_FORMAT_YUV_420_888;
using android::hardware::camera::common::V1_0::helper::VendorTagDescriptorCache;
-using ConfiguredWindows = std::set<native_handle_t *>;
+using ConfiguredWindows = std::set<const native_handle_t *>;
class CameraHelper {
public:
@@ -60,11 +60,11 @@
struct PhysicalImgReaderInfo {
const char* physicalCameraId;
- native_handle_t* anw;
+ const native_handle_t* anw;
};
// Retaining the error code in case the caller needs to analyze it.
- std::variant<int, ConfiguredWindows> initCamera(native_handle_t* imgReaderAnw,
+ std::variant<int, ConfiguredWindows> initCamera(const native_handle_t* imgReaderAnw,
const std::vector<PhysicalImgReaderInfo>& physicalImgReaders,
bool usePhysicalSettings) {
ConfiguredWindows configuredWindows;
@@ -257,7 +257,7 @@
ACameraDevice_StateCallbacks mDeviceCb{this, nullptr, nullptr};
ACameraCaptureSession_stateCallbacks mSessionCb{ this, nullptr, nullptr, nullptr};
- native_handle_t* mImgReaderAnw = nullptr; // not owned by us.
+ const native_handle_t* mImgReaderAnw = nullptr; // not owned by us.
// Camera device
ACameraDevice* mDevice = nullptr;
@@ -396,7 +396,7 @@
return 0;
}
- native_handle_t* getNativeWindow() { return mImgReaderAnw; }
+ const native_handle_t* getNativeWindow() { return mImgReaderAnw; }
int getAcquiredImageCount() {
std::lock_guard<std::mutex> lock(mMutex);
diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp
index eee05ff..0cf390f 100644
--- a/camera/tests/CameraBinderTests.cpp
+++ b/camera/tests/CameraBinderTests.cpp
@@ -517,7 +517,7 @@
CameraMetadata sessionParams;
std::vector<int> offlineStreamIds;
res = device->endConfigure(/*isConstrainedHighSpeed*/ false, sessionParams,
- &offlineStreamIds);
+ ns2ms(systemTime()), &offlineStreamIds);
EXPECT_TRUE(res.isOk()) << res;
EXPECT_FALSE(callbacks->hadError());
@@ -629,7 +629,7 @@
res = device->deleteStream(streamId);
EXPECT_TRUE(res.isOk()) << res;
res = device->endConfigure(/*isConstrainedHighSpeed*/ false, sessionParams,
- &offlineStreamIds);
+ ns2ms(systemTime()), &offlineStreamIds);
EXPECT_TRUE(res.isOk()) << res;
sleep(/*second*/1); // allow some time for errors to show up, if any
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index f4fb626..e6e3473 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -57,7 +57,7 @@
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/AMessage.h>
#include <mediadrm/ICrypto.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
#include <ui/DisplayState.h>
#include "screenrecord.h"
@@ -68,7 +68,7 @@
using android::ALooper;
using android::AMessage;
using android::AString;
-using android::DisplayConfig;
+using android::ui::DisplayMode;
using android::FrameOutput;
using android::IBinder;
using android::IGraphicBufferProducer;
@@ -273,14 +273,11 @@
SurfaceComposerClient::Transaction& t,
const sp<IBinder>& dpy,
const ui::DisplayState& displayState) {
- const ui::Size& viewport = displayState.viewport;
-
- // Set the region of the layer stack we're interested in, which in our
- // case is "all of it".
- Rect layerStackRect(viewport);
+ // Set the region of the layer stack we're interested in, which in our case is "all of it".
+ Rect layerStackRect(displayState.layerStackSpaceRect);
// We need to preserve the aspect ratio of the display.
- float displayAspect = viewport.getHeight() / static_cast<float>(viewport.getWidth());
+ float displayAspect = layerStackRect.getHeight() / static_cast<float>(layerStackRect.getWidth());
// Set the way we map the output onto the display surface (which will
@@ -692,27 +689,28 @@
return err;
}
- DisplayConfig displayConfig;
- err = SurfaceComposerClient::getActiveDisplayConfig(display, &displayConfig);
+ DisplayMode displayMode;
+ err = SurfaceComposerClient::getActiveDisplayMode(display, &displayMode);
if (err != NO_ERROR) {
fprintf(stderr, "ERROR: unable to get display config\n");
return err;
}
- const ui::Size& viewport = displayState.viewport;
+ const ui::Size& layerStackSpaceRect = displayState.layerStackSpaceRect;
if (gVerbose) {
printf("Display is %dx%d @%.2ffps (orientation=%s), layerStack=%u\n",
- viewport.getWidth(), viewport.getHeight(), displayConfig.refreshRate,
- toCString(displayState.orientation), displayState.layerStack);
+ layerStackSpaceRect.getWidth(), layerStackSpaceRect.getHeight(),
+ displayMode.refreshRate, toCString(displayState.orientation),
+ displayState.layerStack);
fflush(stdout);
}
// Encoder can't take odd number as config
if (gVideoWidth == 0) {
- gVideoWidth = floorToEven(viewport.getWidth());
+ gVideoWidth = floorToEven(layerStackSpaceRect.getWidth());
}
if (gVideoHeight == 0) {
- gVideoHeight = floorToEven(viewport.getHeight());
+ gVideoHeight = floorToEven(layerStackSpaceRect.getHeight());
}
// Configure and start the encoder.
@@ -720,7 +718,7 @@
sp<FrameOutput> frameOutput;
sp<IGraphicBufferProducer> encoderInputSurface;
if (gOutputFormat != FORMAT_FRAMES && gOutputFormat != FORMAT_RAW_FRAMES) {
- err = prepareEncoder(displayConfig.refreshRate, &encoder, &encoderInputSurface);
+ err = prepareEncoder(displayMode.refreshRate, &encoder, &encoderInputSurface);
if (err != NO_ERROR && !gSizeSpecified) {
// fallback is defined for landscape; swap if we're in portrait
@@ -733,7 +731,7 @@
gVideoWidth, gVideoHeight, newWidth, newHeight);
gVideoWidth = newWidth;
gVideoHeight = newHeight;
- err = prepareEncoder(displayConfig.refreshRate, &encoder, &encoderInputSurface);
+ err = prepareEncoder(displayMode.refreshRate, &encoder, &encoderInputSurface);
}
}
if (err != NO_ERROR) return err;
@@ -1170,14 +1168,14 @@
}
break;
case 'd':
- gPhysicalDisplayId = atoll(optarg);
- if (gPhysicalDisplayId == 0) {
+ gPhysicalDisplayId = PhysicalDisplayId(atoll(optarg));
+ if (gPhysicalDisplayId.value == 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);
+ fprintf(stderr, "Invalid physical display id: %s\n",
+ to_string(gPhysicalDisplayId).c_str());
return 2;
}
break;
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index ef4c568..03a8dc9 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -16,6 +16,9 @@
libstagefright_foundation libjpeg libui libgui libcutils liblog \
libhidlbase libdatasource libaudioclient \
android.hardware.media.omx@1.0 \
+ media_permission-aidl-cpp
+
+LOCAL_STATIC_LIBRARIES := media_permission-aidl-cpp
LOCAL_C_INCLUDES:= \
frameworks/av/media/libstagefright \
@@ -48,7 +51,8 @@
LOCAL_SHARED_LIBRARIES := \
libstagefright libmedia liblog libutils libbinder \
- libstagefright_foundation libdatasource libaudioclient
+ libstagefright_foundation libdatasource libaudioclient \
+ media_permission-aidl-cpp
LOCAL_C_INCLUDES:= \
frameworks/av/camera/include \
@@ -85,7 +89,8 @@
LOCAL_C_INCLUDES:= \
frameworks/av/media/libstagefright \
frameworks/native/include/media/openmax \
- frameworks/native/include/media/hardware
+ frameworks/native/include/media/hardware \
+ media_permission-aidl-cpp
LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
@@ -113,7 +118,8 @@
LOCAL_SHARED_LIBRARIES := \
libstagefright libmedia liblog libutils libbinder \
- libstagefright_foundation libaudioclient
+ libstagefright_foundation libaudioclient \
+ media_permission-aidl-cpp
LOCAL_C_INCLUDES:= \
frameworks/av/media/libstagefright \
diff --git a/cmds/stagefright/audioloop.cpp b/cmds/stagefright/audioloop.cpp
index 84a6d6b..c86a611 100644
--- a/cmds/stagefright/audioloop.cpp
+++ b/cmds/stagefright/audioloop.cpp
@@ -24,6 +24,7 @@
#include <utils/String16.h>
+#include <android/media/permission/Identity.h>
#include <binder/ProcessState.h>
#include <media/mediarecorder.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -38,6 +39,8 @@
using namespace android;
+using media::permission::Identity;
+
static void usage(const char* name)
{
fprintf(stderr, "Usage: %s [-d du.ration] [-m] [-w] [-N name] [<output-file>]\n", name);
@@ -110,9 +113,10 @@
audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
attr.source = AUDIO_SOURCE_MIC;
+ // TODO b/182392769: use identity util
source = new AudioSource(
&attr,
- String16(),
+ Identity(),
sampleRate,
channels);
} else {
diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp
index 33c4663..beeab54 100644
--- a/cmds/stagefright/codec.cpp
+++ b/cmds/stagefright/codec.cpp
@@ -39,7 +39,7 @@
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/Surface.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
static void usage(const char *me) {
fprintf(stderr, "usage: %s [-a] use audio\n"
@@ -414,10 +414,10 @@
const sp<IBinder> display = SurfaceComposerClient::getInternalDisplayToken();
CHECK(display != nullptr);
- DisplayConfig config;
- CHECK_EQ(SurfaceComposerClient::getActiveDisplayConfig(display, &config), NO_ERROR);
+ ui::DisplayMode mode;
+ CHECK_EQ(SurfaceComposerClient::getActiveDisplayMode(display, &mode), NO_ERROR);
- const ui::Size& resolution = config.resolution;
+ const ui::Size& resolution = mode.resolution;
const ssize_t displayWidth = resolution.getWidth();
const ssize_t displayHeight = resolution.getHeight();
diff --git a/cmds/stagefright/mediafilter.cpp b/cmds/stagefright/mediafilter.cpp
index ca058ab..67c68e6 100644
--- a/cmds/stagefright/mediafilter.cpp
+++ b/cmds/stagefright/mediafilter.cpp
@@ -34,7 +34,7 @@
#include <media/stagefright/NuMediaExtractor.h>
#include <media/stagefright/RenderScriptWrapper.h>
#include <OMX_IVCommon.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
#include "RenderScript.h"
#include "ScriptC_argbtorgba.h"
@@ -752,10 +752,10 @@
const android::sp<IBinder> display = SurfaceComposerClient::getInternalDisplayToken();
CHECK(display != nullptr);
- DisplayConfig config;
- CHECK_EQ(SurfaceComposerClient::getActiveDisplayConfig(display, &config), NO_ERROR);
+ ui::DisplayMode mode;
+ CHECK_EQ(SurfaceComposerClient::getActiveDisplayMode(display, &mode), NO_ERROR);
- const ui::Size& resolution = config.resolution;
+ const ui::Size& resolution = mode.resolution;
const ssize_t displayWidth = resolution.getWidth();
const ssize_t displayHeight = resolution.getHeight();
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
index 37091c4..098c278 100644
--- a/cmds/stagefright/record.cpp
+++ b/cmds/stagefright/record.cpp
@@ -259,31 +259,6 @@
printf("$\n");
#endif
-#if 0
- CameraSource *source = CameraSource::Create(
- String16(argv[0], strlen(argv[0])));
- source->start();
-
- printf("source = %p\n", source);
-
- for (int i = 0; i < 100; ++i) {
- MediaBuffer *buffer;
- status_t err = source->read(&buffer);
- CHECK_EQ(err, (status_t)OK);
-
- printf("got a frame, data=%p, size=%d\n",
- buffer->data(), buffer->range_length());
-
- buffer->release();
- buffer = NULL;
- }
-
- err = source->stop();
-
- delete source;
- source = NULL;
-#endif
-
if (err != OK && err != ERROR_END_OF_STREAM) {
fprintf(stderr, "record failed: %d\n", err);
return 1;
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index 250d26b..40b2392 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -42,7 +42,7 @@
#include <gui/Surface.h>
#include <fcntl.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
using namespace android;
@@ -321,10 +321,10 @@
const sp<IBinder> display = SurfaceComposerClient::getInternalDisplayToken();
CHECK(display != nullptr);
- DisplayConfig config;
- CHECK_EQ(SurfaceComposerClient::getActiveDisplayConfig(display, &config), NO_ERROR);
+ ui::DisplayMode mode;
+ CHECK_EQ(SurfaceComposerClient::getActiveDisplayMode(display, &mode), NO_ERROR);
- const ui::Size& resolution = config.resolution;
+ const ui::Size& resolution = mode.resolution;
const ssize_t displayWidth = resolution.getWidth();
const ssize_t displayHeight = resolution.getHeight();
diff --git a/drm/TEST_MAPPING b/drm/TEST_MAPPING
index 2595e3e..3642898 100644
--- a/drm/TEST_MAPPING
+++ b/drm/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "presubmit": [
+ "presubmit-large": [
// The following tests validate codec and drm path.
{
"name": "GtsMediaTestCases",
@@ -9,17 +9,9 @@
},
{
"include-filter": "com.google.android.media.gts.WidevineGenericOpsTests"
- }
- ]
- },
- {
- "name": "GtsExoPlayerTestCases",
- "options" : [
- {
- "include-annotation": "android.platform.test.annotations.SocPresubmit"
},
{
- "include-filter": "com.google.android.exoplayer.gts.DashTest#testWidevine23FpsH264Fixed"
+ "include-filter": "com.google.android.media.gts.WidevineH264PlaybackTests"
}
]
}
diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp
index a82a61d..0ffe626 100644
--- a/drm/libmediadrm/Android.bp
+++ b/drm/libmediadrm/Android.bp
@@ -60,6 +60,7 @@
"android.hardware.drm@1.1",
"android.hardware.drm@1.2",
"android.hardware.drm@1.3",
+ "android.hardware.drm@1.4",
"libhidlallocatorutils",
"libhidlbase",
],
@@ -72,6 +73,7 @@
"android.hardware.drm@1.0",
"android.hardware.drm@1.1",
"android.hardware.drm@1.2",
+ "android.hardware.drm@1.4",
],
cflags: [
diff --git a/drm/libmediadrm/CryptoHal.cpp b/drm/libmediadrm/CryptoHal.cpp
index 18772e0..e0db1c4 100644
--- a/drm/libmediadrm/CryptoHal.cpp
+++ b/drm/libmediadrm/CryptoHal.cpp
@@ -28,6 +28,7 @@
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/MediaErrors.h>
#include <mediadrm/CryptoHal.h>
+#include <mediadrm/DrmUtils.h>
using drm::V1_0::BufferType;
using drm::V1_0::DestinationBuffer;
@@ -39,6 +40,7 @@
using drm::V1_0::Status;
using drm::V1_0::SubSample;
+using ::android::DrmUtils::toStatusT;
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_handle;
using ::android::hardware::hidl_memory;
@@ -53,42 +55,6 @@
namespace android {
-static status_t toStatusT(Status status) {
- switch (status) {
- case Status::OK:
- return OK;
- case Status::ERROR_DRM_NO_LICENSE:
- return ERROR_DRM_NO_LICENSE;
- case Status::ERROR_DRM_LICENSE_EXPIRED:
- return ERROR_DRM_LICENSE_EXPIRED;
- case Status::ERROR_DRM_RESOURCE_BUSY:
- return ERROR_DRM_RESOURCE_BUSY;
- case Status::ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION:
- return ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION;
- case Status::ERROR_DRM_SESSION_NOT_OPENED:
- return ERROR_DRM_SESSION_NOT_OPENED;
- case Status::ERROR_DRM_CANNOT_HANDLE:
- return ERROR_DRM_CANNOT_HANDLE;
- case Status::ERROR_DRM_DECRYPT:
- return ERROR_DRM_DECRYPT;
- default:
- return UNKNOWN_ERROR;
- }
-}
-
-static status_t toStatusT_1_2(Status_V1_2 status) {
- switch (status) {
- case Status_V1_2::ERROR_DRM_SESSION_LOST_STATE:
- return ERROR_DRM_SESSION_LOST_STATE;;
- case Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE:
- return ERROR_DRM_FRAME_TOO_LARGE;
- case Status_V1_2::ERROR_DRM_INSUFFICIENT_SECURITY:
- return ERROR_DRM_INSUFFICIENT_SECURITY;
- default:
- return toStatusT(static_cast<Status>(status));
- }
-}
-
static hidl_vec<uint8_t> toHidlVec(const Vector<uint8_t> &vector) {
hidl_vec<uint8_t> vec;
vec.setToExternal(const_cast<uint8_t *>(vector.array()), vector.size());
@@ -180,6 +146,9 @@
plugin = hPlugin;
}
);
+ if (!hResult.isOk()) {
+ mInitCheck = DEAD_OBJECT;
+ }
return plugin;
}
@@ -213,10 +182,8 @@
}
}
- if (mPlugin == NULL) {
- mInitCheck = ERROR_UNSUPPORTED;
- } else {
- mInitCheck = OK;
+ if (mInitCheck == NO_INIT) {
+ mInitCheck = mPlugin == NULL ? ERROR_UNSUPPORTED : OK;
}
return mInitCheck;
@@ -376,6 +343,7 @@
Return<void> hResult;
+ mLock.unlock();
if (mPluginV1_2 != NULL) {
hResult = mPluginV1_2->decrypt_1_2(secure, toHidlArray16(keyId), toHidlArray16(iv),
hMode, hPattern, hSubSamples, hSource, offset, hDestination,
@@ -384,7 +352,7 @@
bytesWritten = hBytesWritten;
*errorDetailMsg = toString8(hDetailedError);
}
- err = toStatusT_1_2(status);
+ err = toStatusT(status);
}
);
} else {
@@ -414,7 +382,8 @@
return;
}
- mPlugin->notifyResolution(width, height);
+ auto hResult = mPlugin->notifyResolution(width, height);
+ ALOGE_IF(!hResult.isOk(), "notifyResolution txn failed %s", hResult.description().c_str());
}
status_t CryptoHal::setMediaDrmSession(const Vector<uint8_t> &sessionId) {
@@ -424,7 +393,12 @@
return mInitCheck;
}
- return toStatusT(mPlugin->setMediaDrmSession(toHidlVec(sessionId)));
+ auto err = mPlugin->setMediaDrmSession(toHidlVec(sessionId));
+ return err.isOk() ? toStatusT(err) : DEAD_OBJECT;
}
+status_t CryptoHal::getLogMessages(Vector<drm::V1_4::LogMessage> &logs) const {
+ Mutex::Autolock autoLock(mLock);
+ return DrmUtils::GetLogMessages<drm::V1_4::ICryptoPlugin>(mPlugin, logs);
+}
} // namespace android
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index f218041..40d1e0c 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -16,13 +16,9 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "DrmHal"
-#include <iomanip>
-
-#include <utils/Log.h>
-
-#include <android/binder_manager.h>
#include <aidl/android/media/BnResourceManagerClient.h>
+#include <android/binder_manager.h>
#include <android/hardware/drm/1.2/types.h>
#include <android/hidl/manager/1.2/IServiceManager.h>
#include <hidl/ServiceManagement.h>
@@ -40,7 +36,9 @@
#include <mediadrm/DrmSessionManager.h>
#include <mediadrm/IDrmMetricsConsumer.h>
#include <mediadrm/DrmUtils.h>
+#include <utils/Log.h>
+#include <iomanip>
#include <vector>
using drm::V1_0::KeyedVector;
@@ -55,6 +53,7 @@
using drm::V1_1::SecurityLevel;
using drm::V1_2::KeySetId;
using drm::V1_2::KeyStatusType;
+using ::android::DrmUtils::toStatusT;
using ::android::hardware::drm::V1_1::DrmMetricGroup;
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_string;
@@ -235,58 +234,6 @@
return keySetIds;
}
-static status_t toStatusT(Status status) {
- switch (status) {
- case Status::OK:
- return OK;
- break;
- case Status::ERROR_DRM_NO_LICENSE:
- return ERROR_DRM_NO_LICENSE;
- break;
- case Status::ERROR_DRM_LICENSE_EXPIRED:
- return ERROR_DRM_LICENSE_EXPIRED;
- break;
- case Status::ERROR_DRM_SESSION_NOT_OPENED:
- return ERROR_DRM_SESSION_NOT_OPENED;
- break;
- case Status::ERROR_DRM_CANNOT_HANDLE:
- return ERROR_DRM_CANNOT_HANDLE;
- break;
- case Status::ERROR_DRM_INVALID_STATE:
- return ERROR_DRM_INVALID_STATE;
- break;
- case Status::BAD_VALUE:
- return BAD_VALUE;
- break;
- case Status::ERROR_DRM_NOT_PROVISIONED:
- return ERROR_DRM_NOT_PROVISIONED;
- break;
- case Status::ERROR_DRM_RESOURCE_BUSY:
- return ERROR_DRM_RESOURCE_BUSY;
- break;
- case Status::ERROR_DRM_DEVICE_REVOKED:
- return ERROR_DRM_DEVICE_REVOKED;
- break;
- case Status::ERROR_DRM_UNKNOWN:
- default:
- return ERROR_DRM_UNKNOWN;
- break;
- }
-}
-
-static status_t toStatusT_1_2(Status_V1_2 status) {
- switch (status) {
- case Status_V1_2::ERROR_DRM_RESOURCE_CONTENTION:
- return ERROR_DRM_RESOURCE_CONTENTION;
- case Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE:
- return ERROR_DRM_FRAME_TOO_LARGE;
- case Status_V1_2::ERROR_DRM_INSUFFICIENT_SECURITY:
- return ERROR_DRM_INSUFFICIENT_SECURITY;
- default:
- return toStatusT(static_cast<Status>(status));
- }
-}
-
Mutex DrmHal::mLock;
struct DrmHal::DrmSessionClient : public aidl::android::media::BnResourceManagerClient {
@@ -370,8 +317,7 @@
closeOpenSessions();
Mutex::Autolock autoLock(mLock);
- reportPluginMetrics();
- reportFrameworkMetrics();
+ reportFrameworkMetrics(reportPluginMetrics());
setListener(NULL);
mInitCheck = NO_INIT;
@@ -387,18 +333,19 @@
mPlugin.clear();
mPluginV1_1.clear();
mPluginV1_2.clear();
+ mPluginV1_4.clear();
}
std::vector<sp<IDrmFactory>> DrmHal::makeDrmFactories() {
- std::vector<sp<IDrmFactory>> factories(DrmUtils::MakeDrmFactories());
+ static 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();
if (passthrough != NULL) {
- ALOGI("makeDrmFactories: using default passthrough drm instance");
+ DrmUtils::LOG2BI("makeDrmFactories: using default passthrough drm instance");
factories.push_back(passthrough);
} else {
- ALOGE("Failed to find any drm factories");
+ DrmUtils::LOG2BE("Failed to find any drm factories");
}
}
return factories;
@@ -414,7 +361,7 @@
Return<void> hResult = factory->createPlugin(uuid, appPackageName.string(),
[&](Status status, const sp<IDrmPlugin>& hPlugin) {
if (status != Status::OK) {
- ALOGE("Failed to make drm plugin");
+ DrmUtils::LOG2BE(uuid, "Failed to make drm plugin: %d", status);
return;
}
plugin = hPlugin;
@@ -422,7 +369,8 @@
);
if (!hResult.isOk()) {
- ALOGE("createPlugin remote call failed");
+ DrmUtils::LOG2BE(uuid, "createPlugin remote call failed: %s",
+ hResult.description().c_str());
}
return plugin;
@@ -616,18 +564,21 @@
Mutex::Autolock autoLock(mLock);
for (ssize_t i = mFactories.size() - 1; i >= 0; i--) {
- if (mFactories[i]->isCryptoSchemeSupported(uuid)) {
+ auto hResult = mFactories[i]->isCryptoSchemeSupported(uuid);
+ if (hResult.isOk() && hResult) {
auto plugin = makeDrmPlugin(mFactories[i], uuid, appPackageName);
if (plugin != NULL) {
mPlugin = plugin;
mPluginV1_1 = drm::V1_1::IDrmPlugin::castFrom(mPlugin);
mPluginV1_2 = drm::V1_2::IDrmPlugin::castFrom(mPlugin);
+ mPluginV1_4 = drm::V1_4::IDrmPlugin::castFrom(mPlugin);
break;
}
}
}
if (mPlugin == NULL) {
+ DrmUtils::LOG2BE(uuid, "No supported hal instance found");
mInitCheck = ERROR_UNSUPPORTED;
} else {
mInitCheck = OK;
@@ -642,6 +593,7 @@
mPlugin.clear();
mPluginV1_1.clear();
mPluginV1_2.clear();
+ mPluginV1_4.clear();
}
}
@@ -822,7 +774,7 @@
defaultUrl = toString8(hDefaultUrl);
*keyRequestType = toKeyRequestType_1_1(hKeyRequestType);
}
- err = toStatusT_1_2(status);
+ err = toStatusT(status);
});
} else if (mPluginV1_1 != NULL) {
hResult = mPluginV1_1->getKeyRequest_1_1(
@@ -936,7 +888,7 @@
Return<void> hResult;
if (mPluginV1_2 != NULL) {
- Return<void> hResult = mPluginV1_2->getProvisionRequest_1_2(
+ hResult = mPluginV1_2->getProvisionRequest_1_2(
toHidlString(certType), toHidlString(certAuthority),
[&](Status_V1_2 status, const hidl_vec<uint8_t>& hRequest,
const hidl_string& hDefaultUrl) {
@@ -944,11 +896,11 @@
request = toVector(hRequest);
defaultUrl = toString8(hDefaultUrl);
}
- err = toStatusT_1_2(status);
+ err = toStatusT(status);
}
);
} else {
- Return<void> hResult = mPlugin->getProvisionRequest(
+ hResult = mPlugin->getProvisionRequest(
toHidlString(certType), toHidlString(certAuthority),
[&](Status status, const hidl_vec<uint8_t>& hRequest,
const hidl_string& hDefaultUrl) {
@@ -1116,7 +1068,7 @@
*connected = toHdcpLevel(hConnected);
*max = toHdcpLevel(hMax);
}
- err = toStatusT_1_2(status);
+ err = toStatusT(status);
});
} else if (mPluginV1_1 != NULL) {
hResult = mPluginV1_1->getHdcpLevels(
@@ -1511,7 +1463,7 @@
return hResult.isOk() ? err : DEAD_OBJECT;
}
-void DrmHal::reportFrameworkMetrics() const
+std::string DrmHal::reportFrameworkMetrics(const std::string& pluginMetrics) const
{
mediametrics_handle_t item(mediametrics_create("mediadrm"));
mediametrics_setUid(item, mMetrics.GetAppUid());
@@ -1540,21 +1492,26 @@
if (!b64EncodedMetrics.empty()) {
mediametrics_setCString(item, "serialized_metrics", b64EncodedMetrics.c_str());
}
+ if (!pluginMetrics.empty()) {
+ mediametrics_setCString(item, "plugin_metrics", pluginMetrics.c_str());
+ }
if (!mediametrics_selfRecord(item)) {
ALOGE("Failed to self record framework metrics");
}
mediametrics_delete(item);
+ return serializedMetrics;
}
-void DrmHal::reportPluginMetrics() const
+std::string DrmHal::reportPluginMetrics() const
{
Vector<uint8_t> metricsVector;
String8 vendor;
String8 description;
+ std::string metricsString;
if (getPropertyStringInternal(String8("vendor"), vendor) == OK &&
getPropertyStringInternal(String8("description"), description) == OK &&
getPropertyByteArrayInternal(String8("metrics"), metricsVector) == OK) {
- std::string metricsString = toBase64StringNoPad(metricsVector.array(),
+ metricsString = toBase64StringNoPad(metricsVector.array(),
metricsVector.size());
status_t res = android::reportDrmPluginMetrics(metricsString, vendor,
description, mMetrics.GetAppUid());
@@ -1562,6 +1519,55 @@
ALOGE("Metrics were retrieved but could not be reported: %d", res);
}
}
+ return metricsString;
+}
+
+status_t DrmHal::requiresSecureDecoder(const char *mime, bool *required) const {
+ Mutex::Autolock autoLock(mLock);
+ if (mPluginV1_4 == NULL) {
+ return false;
+ }
+ auto hResult = mPluginV1_4->requiresSecureDecoderDefault(hidl_string(mime));
+ if (!hResult.isOk()) {
+ DrmUtils::LOG2BE("requiresSecureDecoder txn failed: %s", hResult.description().c_str());
+ return DEAD_OBJECT;
+ }
+ if (required) {
+ *required = hResult;
+ }
+ return OK;
+}
+
+status_t DrmHal::requiresSecureDecoder(const char *mime, DrmPlugin::SecurityLevel securityLevel,
+ bool *required) const {
+ Mutex::Autolock autoLock(mLock);
+ if (mPluginV1_4 == NULL) {
+ return false;
+ }
+ auto hLevel = toHidlSecurityLevel(securityLevel);
+ auto hResult = mPluginV1_4->requiresSecureDecoder(hidl_string(mime), hLevel);
+ if (!hResult.isOk()) {
+ DrmUtils::LOG2BE("requiresSecureDecoder txn failed: %s", hResult.description().c_str());
+ return DEAD_OBJECT;
+ }
+ if (required) {
+ *required = hResult;
+ }
+ return OK;
+}
+
+status_t DrmHal::setPlaybackId(Vector<uint8_t> const &sessionId, const char *playbackId) {
+ Mutex::Autolock autoLock(mLock);
+ if (mPluginV1_4 == NULL) {
+ return ERROR_UNSUPPORTED;
+ }
+ auto err = mPluginV1_4->setPlaybackId(toHidlVec(sessionId), hidl_string(playbackId));
+ return err.isOk() ? toStatusT(err) : DEAD_OBJECT;
+}
+
+status_t DrmHal::getLogMessages(Vector<drm::V1_4::LogMessage> &logs) const {
+ Mutex::Autolock autoLock(mLock);
+ return DrmUtils::GetLogMessages<drm::V1_4::IDrmPlugin>(mPlugin, logs);
}
} // namespace android
diff --git a/drm/libmediadrm/DrmUtils.cpp b/drm/libmediadrm/DrmUtils.cpp
index d85fa61..0b117a3 100644
--- a/drm/libmediadrm/DrmUtils.cpp
+++ b/drm/libmediadrm/DrmUtils.cpp
@@ -27,6 +27,8 @@
#include <android/hardware/drm/1.2/IDrmFactory.h>
#include <android/hardware/drm/1.3/ICryptoFactory.h>
#include <android/hardware/drm/1.3/IDrmFactory.h>
+#include <android/hardware/drm/1.4/ICryptoFactory.h>
+#include <android/hardware/drm/1.4/IDrmFactory.h>
#include <android/hidl/manager/1.2/IServiceManager.h>
#include <hidl/HidlSupport.h>
@@ -41,6 +43,9 @@
#include <mediadrm/ICrypto.h>
#include <mediadrm/IDrm.h>
+#include <map>
+#include <string>
+
using HServiceManager = ::android::hidl::manager::V1_2::IServiceManager;
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_string;
@@ -64,20 +69,30 @@
return obj;
}
-template <typename Hal, typename V>
-void MakeHidlFactories(const uint8_t uuid[16], V &factories) {
+template <typename Hal, typename V, typename M>
+void MakeHidlFactories(const uint8_t uuid[16], V &factories, M& instances) {
sp<HServiceManager> serviceManager = HServiceManager::getService();
if (serviceManager == nullptr) {
- ALOGE("Failed to get service manager");
- exit(-1);
+ LOG2BE("Failed to get service manager");
+ return;
}
serviceManager->listManifestByInterface(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)) {
+ instances[instance.c_str()] = Hal::descriptor;
+ if (!uuid) {
+ factories.push_back(factory);
+ continue;
+ }
+ auto supported = factory->isCryptoSchemeSupported(uuid);
+ if (!supported.isOk()) {
+ LOG2BE(uuid, "isCryptoSchemeSupported txn failed: %s",
+ supported.description().c_str());
+ continue;
+ }
+ if (supported) {
factories.push_back(factory);
}
}
@@ -85,6 +100,12 @@
});
}
+template <typename Hal, typename V>
+void MakeHidlFactories(const uint8_t uuid[16], V &factories) {
+ std::map<std::string, std::string> instances;
+ MakeHidlFactories<Hal>(uuid, factories, instances);
+}
+
hidl_vec<uint8_t> toHidlVec(const void *ptr, size_t size) {
hidl_vec<uint8_t> vec(size);
if (ptr != nullptr) {
@@ -103,28 +124,42 @@
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;
+ auto err = factory->createPlugin(
+ toHidlArray16(uuid), hidl_string(appPackageName),
+ [&](::V1_0::Status status, const sp<::V1_0::IDrmPlugin> &hPlugin) {
+ if (status != ::V1_0::Status::OK) {
+ LOG2BE(uuid, "MakeDrmPlugin failed: %d", status);
+ return;
+ }
+ plugin = hPlugin;
+ });
+ if (err.isOk()) {
+ return plugin;
+ } else {
+ LOG2BE(uuid, "MakeDrmPlugin txn failed: %s", err.description().c_str());
+ return nullptr;
+ }
}
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;
+ auto err = factory->createPlugin(
+ toHidlArray16(uuid), toHidlVec(initData, initDataSize),
+ [&](::V1_0::Status status, const sp<::V1_0::ICryptoPlugin> &hPlugin) {
+ if (status != ::V1_0::Status::OK) {
+ LOG2BE(uuid, "MakeCryptoPlugin failed: %d", status);
+ return;
+ }
+ plugin = hPlugin;
+ });
+ if (err.isOk()) {
+ return plugin;
+ } else {
+ LOG2BE(uuid, "MakeCryptoPlugin txn failed: %s", err.description().c_str());
+ return nullptr;
+ }
}
} // namespace
@@ -143,10 +178,15 @@
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);
- MakeHidlFactories<::V1_3::IDrmFactory>(uuid, drmFactories);
+ std::map<std::string, std::string> instances;
+ MakeHidlFactories<::V1_0::IDrmFactory>(uuid, drmFactories, instances);
+ MakeHidlFactories<::V1_1::IDrmFactory>(uuid, drmFactories, instances);
+ MakeHidlFactories<::V1_2::IDrmFactory>(uuid, drmFactories, instances);
+ MakeHidlFactories<::V1_3::IDrmFactory>(uuid, drmFactories, instances);
+ MakeHidlFactories<::V1_4::IDrmFactory>(uuid, drmFactories, instances);
+ for (auto const& entry : instances) {
+ LOG2BI("found instance=%s version=%s", entry.first.c_str(), entry.second.c_str());
+ }
return drmFactories;
}
@@ -165,6 +205,7 @@
MakeHidlFactories<::V1_1::ICryptoFactory>(uuid, cryptoFactories);
MakeHidlFactories<::V1_2::ICryptoFactory>(uuid, cryptoFactories);
MakeHidlFactories<::V1_3::ICryptoFactory>(uuid, cryptoFactories);
+ MakeHidlFactories<::V1_4::ICryptoFactory>(uuid, cryptoFactories);
return cryptoFactories;
}
@@ -177,5 +218,160 @@
return plugins;
}
+status_t toStatusT_1_4(::V1_4::Status status) {
+ switch (status) {
+ case ::V1_4::Status::OK:
+ return OK;
+ case ::V1_4::Status::BAD_VALUE:
+ return BAD_VALUE;
+ case ::V1_4::Status::ERROR_DRM_CANNOT_HANDLE:
+ return ERROR_DRM_CANNOT_HANDLE;
+ case ::V1_4::Status::ERROR_DRM_DECRYPT:
+ return ERROR_DRM_DECRYPT;
+ case ::V1_4::Status::ERROR_DRM_DEVICE_REVOKED:
+ return ERROR_DRM_DEVICE_REVOKED;
+ case ::V1_4::Status::ERROR_DRM_FRAME_TOO_LARGE:
+ return ERROR_DRM_FRAME_TOO_LARGE;
+ case ::V1_4::Status::ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION:
+ return ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION;
+ case ::V1_4::Status::ERROR_DRM_INSUFFICIENT_SECURITY:
+ return ERROR_DRM_INSUFFICIENT_SECURITY;
+ case ::V1_4::Status::ERROR_DRM_INVALID_STATE:
+ return ERROR_DRM_INVALID_STATE;
+ case ::V1_4::Status::ERROR_DRM_LICENSE_EXPIRED:
+ return ERROR_DRM_LICENSE_EXPIRED;
+ case ::V1_4::Status::ERROR_DRM_NO_LICENSE:
+ return ERROR_DRM_NO_LICENSE;
+ case ::V1_4::Status::ERROR_DRM_NOT_PROVISIONED:
+ return ERROR_DRM_NOT_PROVISIONED;
+ case ::V1_4::Status::ERROR_DRM_RESOURCE_BUSY:
+ return ERROR_DRM_RESOURCE_BUSY;
+ case ::V1_4::Status::ERROR_DRM_RESOURCE_CONTENTION:
+ return ERROR_DRM_RESOURCE_CONTENTION;
+ case ::V1_4::Status::ERROR_DRM_SESSION_LOST_STATE:
+ return ERROR_DRM_SESSION_LOST_STATE;
+ case ::V1_4::Status::ERROR_DRM_SESSION_NOT_OPENED:
+ return ERROR_DRM_SESSION_NOT_OPENED;
+
+ // New in S / drm@1.4:
+ case ::V1_4::Status::CANNOT_DECRYPT_ZERO_SUBSAMPLES:
+ return ERROR_DRM_ZERO_SUBSAMPLES;
+ case ::V1_4::Status::CRYPTO_LIBRARY_ERROR:
+ return ERROR_DRM_CRYPTO_LIBRARY;
+ case ::V1_4::Status::GENERAL_OEM_ERROR:
+ return ERROR_DRM_GENERIC_OEM;
+ case ::V1_4::Status::GENERAL_PLUGIN_ERROR:
+ return ERROR_DRM_GENERIC_PLUGIN;
+ case ::V1_4::Status::INIT_DATA_INVALID:
+ return ERROR_DRM_INIT_DATA;
+ case ::V1_4::Status::KEY_NOT_LOADED:
+ return ERROR_DRM_KEY_NOT_LOADED;
+ case ::V1_4::Status::LICENSE_PARSE_ERROR:
+ return ERROR_DRM_LICENSE_PARSE;
+ case ::V1_4::Status::LICENSE_POLICY_ERROR:
+ return ERROR_DRM_LICENSE_POLICY;
+ case ::V1_4::Status::LICENSE_RELEASE_ERROR:
+ return ERROR_DRM_LICENSE_RELEASE;
+ case ::V1_4::Status::LICENSE_REQUEST_REJECTED:
+ return ERROR_DRM_LICENSE_REQUEST_REJECTED;
+ case ::V1_4::Status::LICENSE_RESTORE_ERROR:
+ return ERROR_DRM_LICENSE_RESTORE;
+ case ::V1_4::Status::LICENSE_STATE_ERROR:
+ return ERROR_DRM_LICENSE_STATE;
+ case ::V1_4::Status::MALFORMED_CERTIFICATE:
+ return ERROR_DRM_CERTIFICATE_MALFORMED;
+ case ::V1_4::Status::MEDIA_FRAMEWORK_ERROR:
+ return ERROR_DRM_MEDIA_FRAMEWORK;
+ case ::V1_4::Status::MISSING_CERTIFICATE:
+ return ERROR_DRM_CERTIFICATE_MISSING;
+ case ::V1_4::Status::PROVISIONING_CERTIFICATE_ERROR:
+ return ERROR_DRM_PROVISIONING_CERTIFICATE;
+ case ::V1_4::Status::PROVISIONING_CONFIGURATION_ERROR:
+ return ERROR_DRM_PROVISIONING_CONFIG;
+ case ::V1_4::Status::PROVISIONING_PARSE_ERROR:
+ return ERROR_DRM_PROVISIONING_PARSE;
+ case ::V1_4::Status::PROVISIONING_REQUEST_REJECTED:
+ return ERROR_DRM_PROVISIONING_REQUEST_REJECTED;
+ case ::V1_4::Status::RETRYABLE_PROVISIONING_ERROR:
+ return ERROR_DRM_PROVISIONING_RETRY;
+ case ::V1_4::Status::SECURE_STOP_RELEASE_ERROR:
+ return ERROR_DRM_SECURE_STOP_RELEASE;
+ case ::V1_4::Status::STORAGE_READ_FAILURE:
+ return ERROR_DRM_STORAGE_READ;
+ case ::V1_4::Status::STORAGE_WRITE_FAILURE:
+ return ERROR_DRM_STORAGE_WRITE;
+
+ case ::V1_4::Status::ERROR_DRM_UNKNOWN:
+ default:
+ return ERROR_DRM_UNKNOWN;
+ }
+ return ERROR_DRM_UNKNOWN;
+}
+
+namespace {
+char logPriorityToChar(::V1_4::LogPriority priority) {
+ char p = 'U';
+ switch (priority) {
+ case ::V1_4::LogPriority::VERBOSE: p = 'V'; break;
+ case ::V1_4::LogPriority::DEBUG: p = 'D'; break;
+ case ::V1_4::LogPriority::INFO: p = 'I'; break;
+ case ::V1_4::LogPriority::WARN: p = 'W'; break;
+ case ::V1_4::LogPriority::ERROR: p = 'E'; break;
+ case ::V1_4::LogPriority::FATAL: p = 'F'; break;
+ default: p = 'U'; break;
+ }
+ return p;
+}
+} // namespace
+
+std::string GetExceptionMessage(status_t err, const char *msg,
+ const Vector<::V1_4::LogMessage> &logs) {
+ std::string ruler("==============================");
+ std::string header("Beginning of DRM Plugin Log");
+ std::string footer("End of DRM Plugin Log");
+ String8 msg8;
+ if (msg) {
+ msg8 += msg;
+ msg8 += ": ";
+ }
+ auto errStr = StrCryptoError(err);
+ msg8 += errStr.c_str();
+ msg8 += String8::format("\n%s %s %s", ruler.c_str(), header.c_str(), ruler.c_str());
+
+ for (auto log : logs) {
+ time_t seconds = log.timeMs / 1000;
+ int ms = log.timeMs % 1000;
+ char buf[64] = {0};
+ std::string timeStr = "00-00 00:00:00";
+ if (strftime(buf, sizeof buf, "%m-%d %H:%M:%S", std::localtime(&seconds))) {
+ timeStr = buf;
+ }
+
+ char p = logPriorityToChar(log.priority);
+ msg8 += String8::format("\n %s.%03d %c %s", timeStr.c_str(), ms, p, log.message.c_str());
+ }
+
+ msg8 += String8::format("\n%s %s %s", ruler.c_str(), footer.c_str(), ruler.c_str());
+ return msg8.c_str();
+}
+
+void LogBuffer::addLog(const ::V1_4::LogMessage &log) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mBuffer.push_back(log);
+ while (mBuffer.size() > MAX_CAPACITY) {
+ mBuffer.pop_front();
+ }
+}
+
+Vector<::V1_4::LogMessage> LogBuffer::getLogs() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ Vector<::V1_4::LogMessage> logs;
+ for (auto log : mBuffer) {
+ logs.push_back(log);
+ }
+ return logs;
+}
+
+LogBuffer gLogBuf;
} // namespace DrmUtils
} // namespace android
diff --git a/drm/libmediadrm/fuzzer/Android.bp b/drm/libmediadrm/fuzzer/Android.bp
index 2b04702..7281066 100644
--- a/drm/libmediadrm/fuzzer/Android.bp
+++ b/drm/libmediadrm/fuzzer/Android.bp
@@ -56,6 +56,7 @@
"android.hardware.drm@1.1",
"android.hardware.drm@1.2",
"android.hardware.drm@1.3",
+ "android.hardware.drm@1.4",
"libhidlallocatorutils",
"libhidlbase",
],
diff --git a/drm/libmediadrm/include/mediadrm/CryptoHal.h b/drm/libmediadrm/include/mediadrm/CryptoHal.h
index c9fda67..5fd39e6 100644
--- a/drm/libmediadrm/include/mediadrm/CryptoHal.h
+++ b/drm/libmediadrm/include/mediadrm/CryptoHal.h
@@ -22,6 +22,7 @@
#include <android/hardware/drm/1.0/ICryptoPlugin.h>
#include <android/hardware/drm/1.1/ICryptoFactory.h>
#include <android/hardware/drm/1.2/ICryptoPlugin.h>
+#include <android/hardware/drm/1.4/ICryptoPlugin.h>
#include <mediadrm/ICrypto.h>
#include <utils/KeyedVector.h>
@@ -71,6 +72,8 @@
}
virtual void unsetHeap(int32_t seqNum) { clearHeapBase(seqNum); }
+ virtual status_t getLogMessages(Vector<drm::V1_4::LogMessage> &logs) const;
+
private:
mutable Mutex mLock;
diff --git a/drm/libmediadrm/include/mediadrm/DrmHal.h b/drm/libmediadrm/include/mediadrm/DrmHal.h
index 3b4639b..7eb1dec 100644
--- a/drm/libmediadrm/include/mediadrm/DrmHal.h
+++ b/drm/libmediadrm/include/mediadrm/DrmHal.h
@@ -25,7 +25,10 @@
#include <android/hardware/drm/1.2/IDrmFactory.h>
#include <android/hardware/drm/1.2/IDrmPlugin.h>
#include <android/hardware/drm/1.2/IDrmPluginListener.h>
+#include <android/hardware/drm/1.4/IDrmPlugin.h>
+#include <android/hardware/drm/1.4/types.h>
+#include <media/drm/DrmAPI.h>
#include <mediadrm/DrmMetrics.h>
#include <mediadrm/DrmSessionManager.h>
#include <mediadrm/IDrm.h>
@@ -176,6 +179,17 @@
virtual status_t setListener(const sp<IDrmClient>& listener);
+ virtual status_t requiresSecureDecoder(const char *mime, bool *required) const;
+
+ virtual status_t requiresSecureDecoder(const char *mime, DrmPlugin::SecurityLevel securityLevel,
+ bool *required) const;
+
+ virtual status_t setPlaybackId(
+ Vector<uint8_t> const &sessionId,
+ const char *playbackId);
+
+ virtual status_t getLogMessages(Vector<drm::V1_4::LogMessage> &logs) const;
+
// Methods of IDrmPluginListener
Return<void> sendEvent(EventType eventType,
const hidl_vec<uint8_t>& sessionId, const hidl_vec<uint8_t>& data);
@@ -202,6 +216,7 @@
sp<IDrmPlugin> mPlugin;
sp<drm::V1_1::IDrmPlugin> mPluginV1_1;
sp<drm::V1_2::IDrmPlugin> mPluginV1_2;
+ sp<drm::V1_4::IDrmPlugin> mPluginV1_4;
String8 mAppPackageName;
// Mutable to allow modification within GetPropertyByteArray.
@@ -225,8 +240,8 @@
void writeByteArray(Parcel &obj, const hidl_vec<uint8_t>& array);
- void reportPluginMetrics() const;
- void reportFrameworkMetrics() const;
+ std::string reportPluginMetrics() const;
+ std::string reportFrameworkMetrics(const std::string& pluginMetrics) const;
status_t getPropertyStringInternal(String8 const &name, String8 &value) const;
status_t getPropertyByteArrayInternal(String8 const &name,
Vector<uint8_t> &value) const;
diff --git a/drm/libmediadrm/include/mediadrm/IDrm.h b/drm/libmediadrm/include/mediadrm/IDrm.h
index 0177c24..a88784d 100644
--- a/drm/libmediadrm/include/mediadrm/IDrm.h
+++ b/drm/libmediadrm/include/mediadrm/IDrm.h
@@ -24,6 +24,15 @@
#define ANDROID_IDRM_H_
namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_4 {
+struct LogMessage;
+} // namespace V1_4
+} // namespace drm
+} // namespace hardware
+
+namespace drm = ::android::hardware::drm;
struct AString;
@@ -145,6 +154,17 @@
virtual status_t setListener(const sp<IDrmClient>& listener) = 0;
+ virtual status_t requiresSecureDecoder(const char *mime, bool *required) const = 0;
+
+ virtual status_t requiresSecureDecoder(const char *mime, DrmPlugin::SecurityLevel securityLevel,
+ bool *required) const = 0;
+
+ virtual status_t setPlaybackId(
+ Vector<uint8_t> const &sessionId,
+ const char *playbackId) = 0;
+
+ virtual status_t getLogMessages(Vector<drm::V1_4::LogMessage> &logs) const = 0;
+
protected:
IDrm() {}
diff --git a/drm/libmediadrm/interface/mediadrm/DrmUtils.h b/drm/libmediadrm/interface/mediadrm/DrmUtils.h
index 20b3fe9..ec0b878 100644
--- a/drm/libmediadrm/interface/mediadrm/DrmUtils.h
+++ b/drm/libmediadrm/interface/mediadrm/DrmUtils.h
@@ -19,11 +19,30 @@
#include <android/hardware/drm/1.0/ICryptoFactory.h>
#include <android/hardware/drm/1.0/IDrmFactory.h>
+#include <android/hardware/drm/1.4/IDrmPlugin.h>
+#include <android/hardware/drm/1.4/types.h>
+#include <media/stagefright/MediaErrors.h>
#include <utils/Errors.h> // for status_t
+#include <utils/Log.h>
+#include <utils/String8.h>
#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
+#include <algorithm>
+#include <chrono>
+#include <cstddef>
+#include <cstdint>
+#include <ctime>
+#include <deque>
+#include <endian.h>
+#include <iterator>
+#include <mutex>
+#include <string>
#include <vector>
+
using namespace ::android::hardware::drm;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
namespace android {
@@ -32,6 +51,61 @@
namespace DrmUtils {
+// Log APIs
+class LogBuffer {
+ public:
+ static const int MAX_CAPACITY = 100;
+ void addLog(const ::V1_4::LogMessage &log);
+ Vector<::V1_4::LogMessage> getLogs();
+
+ private:
+ std::deque<::V1_4::LogMessage> mBuffer;
+ std::mutex mMutex;
+};
+
+extern LogBuffer gLogBuf;
+
+static inline int formatBuffer(char *buf, size_t size, const char *msg) {
+ return snprintf(buf, size, "%s", msg);
+}
+
+template <typename First, typename... Args>
+static inline int formatBuffer(char *buf, size_t size, const char *fmt, First first, Args... args) {
+ return snprintf(buf, size, fmt, first, args...);
+}
+
+template <typename... Args>
+void LogToBuffer(android_LogPriority level, const char *fmt, Args... args) {
+ const int LOG_BUF_SIZE = 256;
+ char buf[LOG_BUF_SIZE];
+ int len = formatBuffer(buf, LOG_BUF_SIZE, fmt, args...);
+ if (len <= 0) {
+ return;
+ }
+ __android_log_write(level, LOG_TAG, buf);
+ if (level >= ANDROID_LOG_INFO) {
+ int64_t epochTimeMs =
+ std::chrono::system_clock::now().time_since_epoch() / std::chrono::milliseconds(1);
+ gLogBuf.addLog({epochTimeMs, static_cast<::V1_4::LogPriority>(level), buf});
+ }
+}
+
+template <typename... Args>
+void LogToBuffer(android_LogPriority level, const uint8_t uuid[16], const char *fmt, Args... args) {
+ const uint64_t* uuid2 = reinterpret_cast<const uint64_t*>(uuid);
+ std::string uuidFmt("uuid=[%lx %lx] ");
+ uuidFmt += fmt;
+ LogToBuffer(level, uuidFmt.c_str(), htobe64(uuid2[0]), htobe64(uuid2[1]), args...);
+}
+
+#ifndef LOG2BE
+#define LOG2BE(...) LogToBuffer(ANDROID_LOG_ERROR, __VA_ARGS__)
+#define LOG2BW(...) LogToBuffer(ANDROID_LOG_WARN, __VA_ARGS__)
+#define LOG2BI(...) LogToBuffer(ANDROID_LOG_INFO, __VA_ARGS__)
+#define LOG2BD(...) LogToBuffer(ANDROID_LOG_DEBUG, __VA_ARGS__)
+#define LOG2BV(...) LogToBuffer(ANDROID_LOG_VERBOSE, __VA_ARGS__)
+#endif
+
bool UseDrmService();
sp<IDrm> MakeDrm(status_t *pstatus = nullptr);
@@ -91,8 +165,74 @@
std::vector<sp<::V1_0::ICryptoPlugin>> MakeCryptoPlugins(const uint8_t uuid[16],
const void *initData, size_t initDataSize);
+status_t toStatusT_1_4(::V1_4::Status status);
+
+template<typename S>
+inline status_t toStatusT(S status) {
+ auto err = static_cast<::V1_4::Status>(status);
+ return toStatusT_1_4(err);
+}
+
+template<typename T>
+inline status_t toStatusT(const android::hardware::Return<T> &status) {
+ auto t = static_cast<T>(status);
+ auto err = static_cast<::V1_4::Status>(t);
+ return toStatusT_1_4(err);
+}
+
+template<typename T, typename U>
+status_t GetLogMessages(const sp<U> &obj, Vector<::V1_4::LogMessage> &logs) {
+ sp<T> plugin = T::castFrom(obj);
+ if (obj == NULL) {
+ LOG2BW("%s obj is null", U::descriptor);
+ } else if (plugin == NULL) {
+ LOG2BW("Cannot cast %s obj to %s plugin", U::descriptor, T::descriptor);
+ }
+
+ ::V1_4::Status err{};
+ std::vector<::V1_4::LogMessage> pluginLogs;
+ ::V1_4::IDrmPlugin::getLogMessages_cb cb = [&](
+ ::V1_4::Status status,
+ hidl_vec<::V1_4::LogMessage> hLogs) {
+ if (::V1_4::Status::OK != status) {
+ err = status;
+ return;
+ }
+ pluginLogs.assign(hLogs.data(), hLogs.data() + hLogs.size());
+ };
+
+ Return<void> hResult;
+ if (plugin != NULL) {
+ hResult = plugin->getLogMessages(cb);
+ }
+ if (!hResult.isOk()) {
+ LOG2BW("%s::getLogMessages remote call failed %s",
+ T::descriptor, hResult.description().c_str());
+ }
+
+ auto allLogs(gLogBuf.getLogs());
+ LOG2BD("framework logs size %zu; plugin logs size %zu",
+ allLogs.size(), pluginLogs.size());
+ std::copy(pluginLogs.begin(), pluginLogs.end(), std::back_inserter(allLogs));
+ std::sort(allLogs.begin(), allLogs.end(),
+ [](const ::V1_4::LogMessage &a, const ::V1_4::LogMessage &b) {
+ return a.timeMs < b.timeMs;
+ });
+
+ logs.appendVector(allLogs);
+ return OK;
+}
+
+std::string GetExceptionMessage(status_t err, const char *msg,
+ const Vector<::V1_4::LogMessage> &logs);
+
+template<typename T>
+std::string GetExceptionMessage(status_t err, const char *msg, const sp<T> &iface) {
+ Vector<::V1_4::LogMessage> logs;
+ iface->getLogMessages(logs);
+ return GetExceptionMessage(err, msg, logs);
+}
+
} // 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 df980ae..2c4df60 100644
--- a/drm/libmediadrm/interface/mediadrm/ICrypto.h
+++ b/drm/libmediadrm/interface/mediadrm/ICrypto.h
@@ -32,6 +32,10 @@
struct SharedBuffer;
struct DestinationBuffer;
} // namespace V1_0
+
+namespace V1_4 {
+struct LogMessage;
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
@@ -83,6 +87,8 @@
virtual int32_t setHeap(const sp<hardware::HidlMemory>& heap) = 0;
virtual void unsetHeap(int32_t seqNum) = 0;
+ virtual status_t getLogMessages(Vector<drm::V1_4::LogMessage> &logs) const = 0;
+
protected:
ICrypto() {}
diff --git a/drm/mediadrm/plugins/clearkey/default/JsonWebKey.cpp b/drm/mediadrm/plugins/clearkey/default/JsonWebKey.cpp
index 53ffae4..a2d506d 100644
--- a/drm/mediadrm/plugins/clearkey/default/JsonWebKey.cpp
+++ b/drm/mediadrm/plugins/clearkey/default/JsonWebKey.cpp
@@ -61,7 +61,7 @@
// all the base64 encoded keys. Each key is also stored separately as
// a JSON object in mJsonObjects[1..n] where n is the total
// number of keys in the set.
- if (!isJsonWebKeySet(mJsonObjects[0])) {
+ if (mJsonObjects.size() == 0 || !isJsonWebKeySet(mJsonObjects[0])) {
return false;
}
diff --git a/drm/mediadrm/plugins/clearkey/hidl/AesCtrDecryptor.cpp b/drm/mediadrm/plugins/clearkey/hidl/AesCtrDecryptor.cpp
index 0ac879c..e03a896 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/AesCtrDecryptor.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/AesCtrDecryptor.cpp
@@ -26,7 +26,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
using ::android::hardware::drm::V1_0::SubSample;
@@ -79,7 +79,7 @@
}
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/Android.bp b/drm/mediadrm/plugins/clearkey/hidl/Android.bp
index c49d5fe..6c68532 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/Android.bp
+++ b/drm/mediadrm/plugins/clearkey/hidl/Android.bp
@@ -57,6 +57,7 @@
"android.hardware.drm@1.1",
"android.hardware.drm@1.2",
"android.hardware.drm@1.3",
+ "android.hardware.drm@1.4",
"libbase",
"libbinder",
"libcrypto",
@@ -110,18 +111,18 @@
}
cc_binary {
- name: "android.hardware.drm@1.3-service.clearkey",
+ name: "android.hardware.drm@1.4-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"],
+ init_rc: ["android.hardware.drm@1.4-service.clearkey.rc"],
+ vintf_fragments: ["manifest_android.hardware.drm@1.4-service.clearkey.xml"],
}
cc_binary {
- name: "android.hardware.drm@1.3-service-lazy.clearkey",
- overrides: ["android.hardware.drm@1.3-service.clearkey"],
+ name: "android.hardware.drm@1.4-service-lazy.clearkey",
+ overrides: ["android.hardware.drm@1.4-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"],
+ init_rc: ["android.hardware.drm@1.4-service-lazy.clearkey.rc"],
+ vintf_fragments: ["manifest_android.hardware.drm@1.4-service.clearkey.xml"],
}
diff --git a/drm/mediadrm/plugins/clearkey/hidl/Base64.cpp b/drm/mediadrm/plugins/clearkey/hidl/Base64.cpp
index 657a42f..d81f875 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/Base64.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/Base64.cpp
@@ -21,7 +21,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
sp<Buffer> decodeBase64(const std::string &s) {
@@ -169,7 +169,7 @@
}
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/Buffer.cpp b/drm/mediadrm/plugins/clearkey/hidl/Buffer.cpp
index 75f8395..dcb76f4 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/Buffer.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/Buffer.cpp
@@ -21,7 +21,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
Buffer::Buffer(size_t capacity)
@@ -47,7 +47,7 @@
}
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp b/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp
index bfb0e05..4ab33d3 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_3 {
+namespace V1_4 {
namespace clearkey {
extern "C" {
@@ -38,7 +38,7 @@
} // extern "C"
} // namespace clearkey
-} // namespace V1_3
+} // namespace V1_4
} // 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 a6ed3bd..0bebc3b 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp
@@ -27,11 +27,11 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_3 {
+namespace V1_4 {
namespace clearkey {
using ::android::hardware::drm::V1_0::Status;
-using ::android::hardware::drm::V1_2::clearkey::CryptoPlugin;
+using ::android::hardware::drm::V1_4::clearkey::CryptoPlugin;
Return<bool> CryptoFactory::isCryptoSchemeSupported(
const hidl_array<uint8_t, 16> &uuid)
@@ -63,7 +63,7 @@
}
} // namespace clearkey
-} // namespace V1_3
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
index 302dd39..b92f236 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
@@ -27,7 +27,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
using ::android::hardware::drm::V1_0::BufferType;
@@ -236,7 +236,7 @@
}
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4.
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DeviceFiles.cpp b/drm/mediadrm/plugins/clearkey/hidl/DeviceFiles.cpp
index 2415b6f..0385d8f 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DeviceFiles.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DeviceFiles.cpp
@@ -38,7 +38,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
bool DeviceFiles::StoreLicense(
@@ -246,7 +246,7 @@
}
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // 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 1ce8269..14cb5c1 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp
@@ -31,13 +31,13 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_3 {
+namespace V1_4 {
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::drm::V1_4::clearkey::DrmPlugin;
+using ::android::hardware::drm::V1_4::clearkey::SessionLibrary;
using ::android::hardware::Void;
Return<bool> DrmFactory::isCryptoSchemeSupported(
@@ -105,7 +105,7 @@
}
} // namespace clearkey
-} // namespace V1_3
+} // namespace V1_4
} // 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 6f69110..4318af4 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "hidl_ClearKeyPlugin"
#include <utils/Log.h>
+#include <chrono>
#include <stdio.h>
#include <inttypes.h>
@@ -58,7 +59,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
KeyRequestType toKeyRequestType_V1_0(KeyRequestType_V1_1 keyRequestType) {
@@ -635,6 +636,68 @@
return Void();
}
+Return<void> DrmPlugin::getLogMessages(
+ getLogMessages_cb _hidl_cb) {
+ using std::chrono::duration_cast;
+ using std::chrono::milliseconds;
+ using std::chrono::system_clock;
+
+ auto timeMillis = duration_cast<milliseconds>(
+ system_clock::now().time_since_epoch()).count();
+
+ //TODO(b/182525516) Stub out for now
+ std::vector<LogMessage> logs = {
+ { timeMillis, LogPriority::DEFAULT, std::string() }};
+ _hidl_cb(drm::V1_4::Status::OK, toHidlVec(logs));
+ return Void();
+}
+
+Return<bool> DrmPlugin::requiresSecureDecoder(
+ const hidl_string& mime, SecurityLevel level) {
+ if (!strncasecmp(mime.c_str(), "video/", 6)) {
+ // Type is video, so check level to see if we require a secure decoder.
+ return level == SecurityLevel::HW_SECURE_DECODE;
+ } else {
+ // Type is not video, so never require a secure decoder.
+ return false;
+ }
+}
+
+Return<bool> DrmPlugin::requiresSecureDecoderDefault(const hidl_string& mime) {
+ UNUSED(mime);
+ // Clearkey only supports SW_SECURE_CRYPTO, so we always returns false
+ // regardless of mime type.
+ return false;
+}
+
+Return<Status> DrmPlugin::setPlaybackId(
+ const hidl_vec<uint8_t>& sessionId,
+ const hidl_string& playbackId) {
+ if (sessionId.size() == 0) {
+ ALOGE("Invalid empty session id");
+ return Status::BAD_VALUE;
+ }
+
+ std::vector<uint8_t> sid = toVector(sessionId);
+ sp<Session> session = mSessionLibrary->findSession(sid);
+ if (!session.get()) {
+ return Status::ERROR_DRM_SESSION_NOT_OPENED;
+ }
+
+ std::map<std::vector<uint8_t>, std::string>::iterator itr =
+ mPlaybackId.find(sid);
+ if (itr != mPlaybackId.end()) {
+ mPlaybackId[sid] = playbackId;
+ } else {
+ if (!mPlaybackId.insert(
+ std::pair<std::vector<uint8_t>, std::string>(sid, playbackId)).second) {
+ ALOGE("Failed to set playback Id");
+ return Status::ERROR_DRM_UNKNOWN;
+ }
+ }
+ return Status::OK;
+}
+
Return<Status> DrmPlugin::setSecurityLevel(const hidl_vec<uint8_t>& sessionId,
SecurityLevel level) {
if (sessionId.size() == 0) {
@@ -702,8 +765,21 @@
"close_session", { closeSessionNotOpenedAttribute }, { closeSessionNotOpenedMetricValue }
};
- DrmMetricGroup metrics = { { openSessionMetric, closeSessionMetric,
- closeSessionNotOpenedMetric } };
+ // Set the setPlaybackId metric.
+ DrmMetricGroup::Attribute setPlaybackIdOKAttribute = {
+ "status", DrmMetricGroup::ValueType::INT64_TYPE,
+ (int64_t) Status::OK, 0.0, ""
+ };
+ std::string playbackId = mPlaybackId.begin()->second;
+ DrmMetricGroup::Value setPlaybackIdMetricValue = {
+ "value", DrmMetricGroup::ValueType::STRING_TYPE, 0, 0, playbackId.c_str()
+ };
+ DrmMetricGroup::Metric setPlaybackIdMetric = {
+ "set_playback_id", { setPlaybackIdOKAttribute }, { setPlaybackIdMetricValue }
+ };
+
+ DrmMetricGroup metrics = {{ openSessionMetric, closeSessionMetric,
+ closeSessionNotOpenedMetric, setPlaybackIdMetric }};
_hidl_cb(Status::OK, hidl_vec<DrmMetricGroup>({metrics}));
return Void();
@@ -907,7 +983,7 @@
}
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp b/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp
index 8513434..eccc843 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp
@@ -31,7 +31,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
namespace {
@@ -180,7 +180,7 @@
}
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp b/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp
index d93777d..45cc775 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp
@@ -36,7 +36,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
JsonWebKey::JsonWebKey() {
@@ -65,7 +65,7 @@
// all the base64 encoded keys. Each key is also stored separately as
// a JSON object in mJsonObjects[1..n] where n is the total
// number of keys in the set.
- if (!isJsonWebKeySet(mJsonObjects[0])) {
+ if (mJsonObjects.size() == 0 || !isJsonWebKeySet(mJsonObjects[0])) {
return false;
}
@@ -271,7 +271,7 @@
}
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp b/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp
index 32cf2dc..e61db3f 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp
@@ -11,7 +11,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
std::string MemoryFileSystem::GetFileName(const std::string& path) {
@@ -93,7 +93,7 @@
}
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/Session.cpp b/drm/mediadrm/plugins/clearkey/hidl/Session.cpp
index a9d7016..cf668d4 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/Session.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/Session.cpp
@@ -28,7 +28,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
using ::android::hardware::drm::V1_0::KeyValue;
@@ -95,7 +95,7 @@
}
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/SessionLibrary.cpp b/drm/mediadrm/plugins/clearkey/hidl/SessionLibrary.cpp
index 99fb30f..88afcc4 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/SessionLibrary.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/SessionLibrary.cpp
@@ -24,7 +24,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
using ::android::hardware::hidl_string;
@@ -86,7 +86,7 @@
}
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.4-service-lazy.clearkey.rc b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.4-service-lazy.clearkey.rc
new file mode 100644
index 0000000..46aba88
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.4-service-lazy.clearkey.rc
@@ -0,0 +1,18 @@
+service vendor.drm-clearkey-hal-1-4 /vendor/bin/hw/android.hardware.drm@1.4-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
+ interface android.hardware.drm@1.4::ICryptoFactory clearkey
+ interface android.hardware.drm@1.4::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.4-service.clearkey.rc b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.4-service.clearkey.rc
new file mode 100644
index 0000000..8186933
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.4-service.clearkey.rc
@@ -0,0 +1,16 @@
+service vendor.drm-clearkey-hal-1-4 /vendor/bin/hw/android.hardware.drm@1.4-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
+ interface android.hardware.drm@1.4::ICryptoFactory clearkey
+ interface android.hardware.drm@1.4::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/AesCtrDecryptor.h b/drm/mediadrm/plugins/clearkey/hidl/include/AesCtrDecryptor.h
index 721f4c0..97794f7 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/AesCtrDecryptor.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/AesCtrDecryptor.h
@@ -22,7 +22,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
using ::android::hardware::drm::V1_0::Status;
@@ -42,7 +42,7 @@
};
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/Base64.h b/drm/mediadrm/plugins/clearkey/hidl/include/Base64.h
index ec30cc1..2349f23 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/Base64.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/Base64.h
@@ -25,7 +25,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
using ::android::sp;
@@ -38,7 +38,7 @@
void encodeBase64Url(const void *data, size_t size, std::string *out);
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/Buffer.h b/drm/mediadrm/plugins/clearkey/hidl/include/Buffer.h
index c497e37..66aaa73 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/Buffer.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/Buffer.h
@@ -25,7 +25,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
using ::android::sp;
@@ -54,7 +54,7 @@
};
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h
index b83ce69..8e47c45 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h
@@ -22,7 +22,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
static const std::string kVendorKey("vendor");
@@ -55,7 +55,7 @@
static const uint8_t kMetricsData[] = { 0 };
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h
index 03c434e..cd18029 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h
@@ -24,7 +24,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
using ::android::hardware::drm::V1_0::KeyValue;
@@ -48,7 +48,7 @@
void operator=(const TypeName&) = delete;
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h b/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h
index c1c188e..d4a8a17 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.3/ICryptoFactory.h>
-#include <android/hardware/drm/1.3/IDrmFactory.h>
+#include <android/hardware/drm/1.4/ICryptoFactory.h>
+#include <android/hardware/drm/1.4/IDrmFactory.h>
namespace android {
namespace hardware {
namespace drm {
-namespace V1_3 {
+namespace V1_4 {
namespace clearkey {
-using ::android::hardware::drm::V1_3::ICryptoFactory;
-using ::android::hardware::drm::V1_3::IDrmFactory;
+using ::android::hardware::drm::V1_4::ICryptoFactory;
+using ::android::hardware::drm::V1_4::IDrmFactory;
extern "C" {
IDrmFactory* createDrmFactory();
@@ -35,7 +35,7 @@
}
} // namespace clearkey
-} // namespace V1_3
+} // namespace V1_4
} // 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 cb4811b..e6b541f 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.3/ICryptoFactory.h>
+#include <android/hardware/drm/1.4/ICryptoFactory.h>
#include "ClearKeyTypes.h"
namespace android {
namespace hardware {
namespace drm {
-namespace V1_3 {
+namespace V1_4 {
namespace clearkey {
-using ::android::hardware::drm::V1_3::ICryptoFactory;
+using ::android::hardware::drm::V1_4::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_3
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h
index 23a64fa..a7b2427 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h
@@ -34,7 +34,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
namespace drm = ::android::hardware::drm;
@@ -114,7 +114,7 @@
};
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DeviceFiles.h b/drm/mediadrm/plugins/clearkey/hidl/include/DeviceFiles.h
index 554ae59..6466ac3 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/DeviceFiles.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/DeviceFiles.h
@@ -20,9 +20,11 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
+using ::android::hardware::drm::V1_2::clearkey::OfflineFile;
+
class DeviceFiles {
public:
typedef enum {
@@ -63,7 +65,7 @@
};
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // 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 63234cf..fea1ec8 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h
@@ -17,15 +17,15 @@
#ifndef CLEARKEY_DRM_FACTORY_H_
#define CLEARKEY_DRM_FACTORY_H_
-#include <android/hardware/drm/1.2/IDrmPlugin.h>
-#include <android/hardware/drm/1.3/IDrmFactory.h>
+#include <android/hardware/drm/1.4/IDrmPlugin.h>
+#include <android/hardware/drm/1.4/IDrmFactory.h>
#include "ClearKeyTypes.h"
namespace android {
namespace hardware {
namespace drm {
-namespace V1_3 {
+namespace V1_4 {
namespace clearkey {
using ::android::hardware::drm::V1_1::SecurityLevel;
@@ -63,7 +63,7 @@
};
} // namespace clearkey
-} // namespace V1_3
+} // namespace V1_4
} // 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 894985b..5d6e3da 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
@@ -17,7 +17,7 @@
#ifndef CLEARKEY_DRM_PLUGIN_H_
#define CLEARKEY_DRM_PLUGIN_H_
-#include <android/hardware/drm/1.2/IDrmPlugin.h>
+#include <android/hardware/drm/1.4/IDrmPlugin.h>
#include <android/hardware/drm/1.2/IDrmPluginListener.h>
#include <map>
@@ -32,7 +32,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
namespace drm = ::android::hardware::drm;
@@ -50,9 +50,12 @@
using drm::V1_1::HdcpLevel;
using drm::V1_1::SecureStopRelease;
using drm::V1_1::SecurityLevel;
-using drm::V1_2::IDrmPlugin;
using drm::V1_2::KeySetId;
using drm::V1_2::OfflineLicenseState;
+using drm::V1_4::clearkey::DeviceFiles;
+using drm::V1_4::clearkey::Session;
+using drm::V1_4::clearkey::SessionLibrary;
+using drm::V1_4::IDrmPlugin;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
@@ -199,6 +202,18 @@
Return<Status> setPropertyByteArray(
const hidl_string& name, const hidl_vec<uint8_t>& value) override;
+ Return<void> getLogMessages(
+ getLogMessages_cb _hidl_cb) override;
+
+ Return<Status> setPlaybackId(
+ const hidl_vec<uint8_t>& sessionId,
+ const hidl_string& playbackId) override;
+
+ Return<bool> requiresSecureDecoder(
+ const hidl_string& mime, SecurityLevel level) override;
+
+ Return<bool> requiresSecureDecoderDefault(const hidl_string& mime) override;
+
Return<Status> setCipherAlgorithm(
const hidl_vec<uint8_t>& sessionId, const hidl_string& algorithm) {
if (sessionId.size() == 0 || algorithm.size() == 0) {
@@ -398,6 +413,7 @@
std::map<std::string, std::string> mStringProperties;
std::map<std::string, std::vector<uint8_t> > mByteArrayProperties;
std::map<std::string, std::vector<uint8_t> > mReleaseKeysMap;
+ std::map<std::vector<uint8_t>, std::string> mPlaybackId;
std::map<std::vector<uint8_t>, SecurityLevel> mSecurityLevel;
sp<IDrmPluginListener> mListener;
sp<IDrmPluginListener_V1_2> mListenerV1_2;
@@ -424,7 +440,7 @@
};
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/InitDataParser.h b/drm/mediadrm/plugins/clearkey/hidl/include/InitDataParser.h
index 889f511..59338c9 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/InitDataParser.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/InitDataParser.h
@@ -24,7 +24,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
using ::android::hardware::drm::V1_0::Status;
@@ -49,7 +49,7 @@
};
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/JsonWebKey.h b/drm/mediadrm/plugins/clearkey/hidl/include/JsonWebKey.h
index e57470c..40a2d74 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/JsonWebKey.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/JsonWebKey.h
@@ -23,7 +23,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
class JsonWebKey {
@@ -54,7 +54,7 @@
};
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h b/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h
index 6ac0e2c..a90d818 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h
@@ -15,7 +15,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
// Using android file system requires clearkey plugin to update
@@ -64,7 +64,7 @@
};
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/Session.h b/drm/mediadrm/plugins/clearkey/hidl/include/Session.h
index a159e5a..05cb8c8 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/Session.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/Session.h
@@ -27,7 +27,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
namespace drm = ::android::hardware::drm;
@@ -73,7 +73,7 @@
};
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/SessionLibrary.h b/drm/mediadrm/plugins/clearkey/hidl/include/SessionLibrary.h
index 1e567b8..5e77438 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/SessionLibrary.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/SessionLibrary.h
@@ -26,7 +26,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
using ::android::sp;
@@ -58,7 +58,7 @@
};
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h b/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h
index b0f8607..22eeccd 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef CLEARKEY_ANDROID_HARDWARE_DRM_V1_1_TYPECONVERT
-#define CLEARKEY_ANDROID_HARDWARE_DRM_V1_1_TYPECONVERT
+#ifndef CLEARKEY_ANDROID_HARDWARE_DRM_V1_4_TYPECONVERT
+#define CLEARKEY_ANDROID_HARDWARE_DRM_V1_4_TYPECONVERT
#include <vector>
@@ -24,7 +24,7 @@
namespace android {
namespace hardware {
namespace drm {
-namespace V1_2 {
+namespace V1_4 {
namespace clearkey {
using ::android::hardware::hidl_array;
@@ -80,9 +80,9 @@
}
} // namespace clearkey
-} // namespace V1_2
+} // namespace V1_4
} // namespace drm
} // namespace hardware
} // namespace android
-#endif // CLEARKEY_ANDROID_HARDWARE_DRM_V1_1_TYPECONVERT
+#endif // CLEARKEY_ANDROID_HARDWARE_DRM_V1_4_TYPECONVERT
diff --git a/drm/mediadrm/plugins/clearkey/hidl/manifest_android.hardware.drm@1.4-service.clearkey.xml b/drm/mediadrm/plugins/clearkey/hidl/manifest_android.hardware.drm@1.4-service.clearkey.xml
new file mode 100644
index 0000000..31ddb5f
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/hidl/manifest_android.hardware.drm@1.4-service.clearkey.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.drm</name>
+ <transport>hwbinder</transport>
+ <fqname>@1.4::ICryptoFactory/clearkey</fqname>
+ <fqname>@1.4::IDrmFactory/clearkey</fqname>
+ </hal>
+</manifest>
diff --git a/drm/mediadrm/plugins/clearkey/hidl/service.cpp b/drm/mediadrm/plugins/clearkey/hidl/service.cpp
index b62baae..d3d6905 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_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::drm::V1_4::ICryptoFactory;
+using android::hardware::drm::V1_4::IDrmFactory;
+using android::hardware::drm::V1_4::clearkey::CryptoFactory;
+using android::hardware::drm::V1_4::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 b3e0179..358b5cc 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_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::drm::V1_4::ICryptoFactory;
+using android::hardware::drm::V1_4::IDrmFactory;
+using android::hardware::drm::V1_4::clearkey::CryptoFactory;
+using android::hardware::drm::V1_4::clearkey::DrmFactory;
using android::hardware::LazyServiceRegistrar;
int main(int /* argc */, char** /* argv */) {
diff --git a/include/drm/TEST_MAPPING b/include/drm/TEST_MAPPING
index 28e432e..74fa50d 100644
--- a/include/drm/TEST_MAPPING
+++ b/include/drm/TEST_MAPPING
@@ -8,17 +8,9 @@
},
{
"include-filter": "com.google.android.media.gts.WidevineGenericOpsTests"
- }
- ]
- },
- {
- "name": "GtsExoPlayerTestCases",
- "options" : [
- {
- "include-annotation": "android.platform.test.annotations.SocPresubmit"
},
{
- "include-filter": "com.google.android.exoplayer.gts.DashTest#testWidevine23FpsH264Fixed"
+ "include-filter": "com.google.android.media.gts.WidevineH264PlaybackTests"
}
]
}
diff --git a/include/media/Interpolator.h b/include/media/Interpolator.h
index 703cf77..71e7604 100644
--- a/include/media/Interpolator.h
+++ b/include/media/Interpolator.h
@@ -21,6 +21,7 @@
#include <sstream>
#include <unordered_map>
+#include <android/media/InterpolatorConfig.h>
#include <binder/Parcel.h>
#include <utils/RefBase.h>
@@ -39,17 +40,10 @@
class Interpolator : public std::map<S, T> {
public:
// Polynomial spline interpolators
- // Extend only at the end of enum, as this must match order in VolumeShapers.java.
- enum InterpolatorType : int32_t {
- INTERPOLATOR_TYPE_STEP, // Not continuous
- INTERPOLATOR_TYPE_LINEAR, // C0
- INTERPOLATOR_TYPE_CUBIC, // C1
- INTERPOLATOR_TYPE_CUBIC_MONOTONIC, // C1 (to provide locally monotonic curves)
- // INTERPOLATOR_TYPE_CUBIC_C2, // TODO - requires global computation / cache
- };
+ using InterpolatorType = media::InterpolatorType;
explicit Interpolator(
- InterpolatorType interpolatorType = INTERPOLATOR_TYPE_LINEAR,
+ InterpolatorType interpolatorType = InterpolatorType::CUBIC,
bool cache = true)
: mCache(cache)
, mFirstSlope(0)
@@ -82,13 +76,13 @@
// now that we have two adjacent points:
switch (mInterpolatorType) {
- case INTERPOLATOR_TYPE_STEP:
+ case InterpolatorType::STEP:
return high->first == x ? high->second : low->second;
- case INTERPOLATOR_TYPE_LINEAR:
+ case InterpolatorType::LINEAR:
return ((high->first - x) * low->second + (x - low->first) * high->second)
/ (high->first - low->first);
- case INTERPOLATOR_TYPE_CUBIC:
- case INTERPOLATOR_TYPE_CUBIC_MONOTONIC:
+ case InterpolatorType::CUBIC:
+ case InterpolatorType::CUBIC_MONOTONIC:
default: {
// See https://en.wikipedia.org/wiki/Cubic_Hermite_spline
@@ -116,7 +110,7 @@
// non catmullRom (finite difference) with regular cubic;
// the choices here minimize computation.
bool monotonic, catmullRom;
- if (mInterpolatorType == INTERPOLATOR_TYPE_CUBIC_MONOTONIC) {
+ if (mInterpolatorType == InterpolatorType::CUBIC_MONOTONIC) {
monotonic = true;
catmullRom = false;
} else {
@@ -202,11 +196,11 @@
status_t setInterpolatorType(InterpolatorType interpolatorType) {
switch (interpolatorType) {
- case INTERPOLATOR_TYPE_STEP: // Not continuous
- case INTERPOLATOR_TYPE_LINEAR: // C0
- case INTERPOLATOR_TYPE_CUBIC: // C1
- case INTERPOLATOR_TYPE_CUBIC_MONOTONIC: // C1 + other constraints
- // case INTERPOLATOR_TYPE_CUBIC_C2:
+ case InterpolatorType::STEP: // Not continuous
+ case InterpolatorType::LINEAR: // C0
+ case InterpolatorType::CUBIC: // C1
+ case InterpolatorType::CUBIC_MONOTONIC: // C1 + other constraints
+ // case InterpolatorType::CUBIC_C2:
mInterpolatorType = interpolatorType;
return NO_ERROR;
default:
@@ -235,49 +229,50 @@
mMemo.clear();
}
+ // TODO(ytai): remove this method once it is not used.
status_t writeToParcel(Parcel *parcel) const {
- if (parcel == nullptr) {
- return BAD_VALUE;
- }
- status_t res = parcel->writeInt32(mInterpolatorType)
- ?: parcel->writeFloat(mFirstSlope)
- ?: parcel->writeFloat(mLastSlope)
- ?: parcel->writeUint32((uint32_t)this->size()); // silent truncation
- if (res != NO_ERROR) {
- return res;
- }
- for (const auto &pt : *this) {
- res = parcel->writeFloat(pt.first)
- ?: parcel->writeFloat(pt.second);
- if (res != NO_ERROR) {
- return res;
- }
- }
- return NO_ERROR;
+ media::InterpolatorConfig config;
+ writeToConfig(&config);
+ return config.writeToParcel(parcel);
}
+ void writeToConfig(media::InterpolatorConfig *config) const {
+ config->type = mInterpolatorType;
+ config->firstSlope = mFirstSlope;
+ config->lastSlope = mLastSlope;
+ for (const auto &pt : *this) {
+ config->xy.push_back(pt.first);
+ config->xy.push_back(pt.second);
+ }
+ }
+
+ // TODO(ytai): remove this method once it is not used.
status_t readFromParcel(const Parcel &parcel) {
- this->clear();
- int32_t type;
- uint32_t size;
- status_t res = parcel.readInt32(&type)
- ?: parcel.readFloat(&mFirstSlope)
- ?: parcel.readFloat(&mLastSlope)
- ?: parcel.readUint32(&size)
- ?: setInterpolatorType((InterpolatorType)type);
+ media::InterpolatorConfig config;
+ status_t res = config.readFromParcel(&parcel);
if (res != NO_ERROR) {
return res;
}
+ return readFromConfig(config);
+ }
+
+ status_t readFromConfig(const media::InterpolatorConfig &config) {
+ this->clear();
+ setInterpolatorType(config.type);
+ if ((config.xy.size() & 1) != 0) {
+ // xy size must be even.
+ return BAD_VALUE;
+ }
+ uint32_t size = config.xy.size() / 2;
+ mFirstSlope = config.firstSlope;
+ mLastSlope = config.lastSlope;
+
// Note: We don't need to check size is within some bounds as
// the Parcel read will fail if size is incorrectly specified too large.
float lastx;
for (uint32_t i = 0; i < size; ++i) {
- float x, y;
- res = parcel.readFloat(&x)
- ?: parcel.readFloat(&y);
- if (res != NO_ERROR) {
- return res;
- }
+ float x = config.xy[i * 2];
+ float y = config.xy[i * 2 + 1];
if ((i > 0 && !(x > lastx)) /* handle nan */
|| y != y /* handle nan */) {
// This is a std::map object which imposes sorted order
diff --git a/include/media/MicrophoneInfo.h b/include/media/MicrophoneInfo.h
index 0a24b02..a5045b9 100644
--- a/include/media/MicrophoneInfo.h
+++ b/include/media/MicrophoneInfo.h
@@ -17,33 +17,24 @@
#ifndef ANDROID_MICROPHONE_INFO_H
#define ANDROID_MICROPHONE_INFO_H
+#include <android/media/MicrophoneInfoData.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
+#include <media/AidlConversionUtil.h>
#include <system/audio.h>
-#include <utils/String16.h>
-#include <utils/Vector.h>
namespace android {
namespace media {
-#define RETURN_IF_FAILED(calledOnce) \
- { \
- status_t returnStatus = calledOnce; \
- if (returnStatus) { \
- ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
- return returnStatus; \
- } \
- }
-
class MicrophoneInfo : public Parcelable {
public:
MicrophoneInfo() = default;
MicrophoneInfo(const MicrophoneInfo& microphoneInfo) = default;
MicrophoneInfo(audio_microphone_characteristic_t& characteristic) {
- mDeviceId = String16(&characteristic.device_id[0]);
+ mDeviceId = std::string(&characteristic.device_id[0]);
mPortId = characteristic.id;
mType = characteristic.device;
- mAddress = String16(&characteristic.address[0]);
+ mAddress = std::string(&characteristic.address[0]);
mDeviceLocation = characteristic.location;
mDeviceGroup = characteristic.group;
mIndexInTheGroup = characteristic.index_in_the_group;
@@ -53,8 +44,8 @@
mOrientation.push_back(characteristic.orientation.x);
mOrientation.push_back(characteristic.orientation.y);
mOrientation.push_back(characteristic.orientation.z);
- Vector<float> frequencies;
- Vector<float> responses;
+ std::vector<float> frequencies;
+ std::vector<float> responses;
for (size_t i = 0; i < characteristic.num_frequency_responses; i++) {
frequencies.push_back(characteristic.frequency_responses[0][i]);
responses.push_back(characteristic.frequency_responses[1][i]);
@@ -73,76 +64,73 @@
virtual ~MicrophoneInfo() = default;
virtual status_t writeToParcel(Parcel* parcel) const {
- RETURN_IF_FAILED(parcel->writeString16(mDeviceId));
- RETURN_IF_FAILED(parcel->writeInt32(mPortId));
- RETURN_IF_FAILED(parcel->writeUint32(mType));
- RETURN_IF_FAILED(parcel->writeString16(mAddress));
- RETURN_IF_FAILED(parcel->writeInt32(mDeviceLocation));
- RETURN_IF_FAILED(parcel->writeInt32(mDeviceGroup));
- RETURN_IF_FAILED(parcel->writeInt32(mIndexInTheGroup));
- RETURN_IF_FAILED(writeFloatVector(parcel, mGeometricLocation));
- RETURN_IF_FAILED(writeFloatVector(parcel, mOrientation));
+ MicrophoneInfoData parcelable;
+ return writeToParcelable(&parcelable)
+ ?: parcelable.writeToParcel(parcel);
+ }
+
+ virtual status_t writeToParcelable(MicrophoneInfoData* parcelable) const {
+ parcelable->deviceId = mDeviceId;
+ parcelable->portId = mPortId;
+ parcelable->type = VALUE_OR_RETURN_STATUS(convertReinterpret<int32_t>(mType));
+ parcelable->address = mAddress;
+ parcelable->deviceGroup = mDeviceGroup;
+ parcelable->indexInTheGroup = mIndexInTheGroup;
+ parcelable->geometricLocation = mGeometricLocation;
+ parcelable->orientation = mOrientation;
if (mFrequencyResponses.size() != 2) {
return BAD_VALUE;
}
- for (size_t i = 0; i < mFrequencyResponses.size(); i++) {
- RETURN_IF_FAILED(parcel->writeInt32(mFrequencyResponses[i].size()));
- RETURN_IF_FAILED(writeFloatVector(parcel, mFrequencyResponses[i]));
- }
- std::vector<int> channelMapping;
- for (size_t i = 0; i < mChannelMapping.size(); ++i) {
- channelMapping.push_back(mChannelMapping[i]);
- }
- RETURN_IF_FAILED(parcel->writeInt32Vector(channelMapping));
- RETURN_IF_FAILED(parcel->writeFloat(mSensitivity));
- RETURN_IF_FAILED(parcel->writeFloat(mMaxSpl));
- RETURN_IF_FAILED(parcel->writeFloat(mMinSpl));
- RETURN_IF_FAILED(parcel->writeInt32(mDirectionality));
+ parcelable->frequencies = mFrequencyResponses[0];
+ parcelable->frequencyResponses = mFrequencyResponses[1];
+ parcelable->channelMapping = mChannelMapping;
+ parcelable->sensitivity = mSensitivity;
+ parcelable->maxSpl = mMaxSpl;
+ parcelable->minSpl = mMinSpl;
+ parcelable->directionality = mDirectionality;
return OK;
}
virtual status_t readFromParcel(const Parcel* parcel) {
- RETURN_IF_FAILED(parcel->readString16(&mDeviceId));
- RETURN_IF_FAILED(parcel->readInt32(&mPortId));
- RETURN_IF_FAILED(parcel->readUint32(&mType));
- RETURN_IF_FAILED(parcel->readString16(&mAddress));
- RETURN_IF_FAILED(parcel->readInt32(&mDeviceLocation));
- RETURN_IF_FAILED(parcel->readInt32(&mDeviceGroup));
- RETURN_IF_FAILED(parcel->readInt32(&mIndexInTheGroup));
- RETURN_IF_FAILED(readFloatVector(parcel, &mGeometricLocation, 3));
- RETURN_IF_FAILED(readFloatVector(parcel, &mOrientation, 3));
- int32_t frequenciesNum;
- RETURN_IF_FAILED(parcel->readInt32(&frequenciesNum));
- Vector<float> frequencies;
- RETURN_IF_FAILED(readFloatVector(parcel, &frequencies, frequenciesNum));
- int32_t responsesNum;
- RETURN_IF_FAILED(parcel->readInt32(&responsesNum));
- Vector<float> responses;
- RETURN_IF_FAILED(readFloatVector(parcel, &responses, responsesNum));
- if (frequencies.size() != responses.size()) {
+ MicrophoneInfoData data;
+ return data.readFromParcel(parcel)
+ ?: readFromParcelable(data);
+ }
+
+ virtual status_t readFromParcelable(const MicrophoneInfoData& parcelable) {
+ mDeviceId = parcelable.deviceId;
+ mPortId = parcelable.portId;
+ mType = VALUE_OR_RETURN_STATUS(convertReinterpret<uint32_t>(parcelable.type));
+ mAddress = parcelable.address;
+ mDeviceLocation = parcelable.deviceLocation;
+ mDeviceGroup = parcelable.deviceGroup;
+ mIndexInTheGroup = parcelable.indexInTheGroup;
+ if (parcelable.geometricLocation.size() != 3) {
return BAD_VALUE;
}
- mFrequencyResponses.push_back(frequencies);
- mFrequencyResponses.push_back(responses);
- std::vector<int> channelMapping;
- status_t result = parcel->readInt32Vector(&channelMapping);
- if (result != OK) {
- return result;
- }
- if (channelMapping.size() != AUDIO_CHANNEL_COUNT_MAX) {
+ mGeometricLocation = parcelable.geometricLocation;
+ if (parcelable.orientation.size() != 3) {
return BAD_VALUE;
}
- for (size_t i = 0; i < channelMapping.size(); i++) {
- mChannelMapping.push_back(channelMapping[i]);
+ mOrientation = parcelable.orientation;
+ if (parcelable.frequencies.size() != parcelable.frequencyResponses.size()) {
+ return BAD_VALUE;
}
- RETURN_IF_FAILED(parcel->readFloat(&mSensitivity));
- RETURN_IF_FAILED(parcel->readFloat(&mMaxSpl));
- RETURN_IF_FAILED(parcel->readFloat(&mMinSpl));
- RETURN_IF_FAILED(parcel->readInt32(&mDirectionality));
+
+ mFrequencyResponses.push_back(parcelable.frequencies);
+ mFrequencyResponses.push_back(parcelable.frequencyResponses);
+ if (parcelable.channelMapping.size() != AUDIO_CHANNEL_COUNT_MAX) {
+ return BAD_VALUE;
+ }
+ mChannelMapping = parcelable.channelMapping;
+ mSensitivity = parcelable.sensitivity;
+ mMaxSpl = parcelable.maxSpl;
+ mMinSpl = parcelable.minSpl;
+ mDirectionality = parcelable.directionality;
return OK;
}
- String16 getDeviceId() const {
+ std::string getDeviceId() const {
return mDeviceId;
}
@@ -154,7 +142,7 @@
return mType;
}
- String16 getAddress() const {
+ std::string getAddress() const {
return mAddress;
}
@@ -170,19 +158,19 @@
return mIndexInTheGroup;
}
- const Vector<float>& getGeometricLocation() const {
+ const std::vector<float>& getGeometricLocation() const {
return mGeometricLocation;
}
- const Vector<float>& getOrientation() const {
+ const std::vector<float>& getOrientation() const {
return mOrientation;
}
- const Vector<Vector<float>>& getFrequencyResponses() const {
+ const std::vector<std::vector<float>>& getFrequencyResponses() const {
return mFrequencyResponses;
}
- const Vector<int>& getChannelMapping() const {
+ const std::vector<int>& getChannelMapping() const {
return mChannelMapping;
}
@@ -203,46 +191,38 @@
}
private:
- status_t readFloatVector(
- const Parcel* parcel, Vector<float> *vectorPtr, size_t defaultLength) {
- std::optional<std::vector<float>> v;
- status_t result = parcel->readFloatVector(&v);
- if (result != OK) return result;
- vectorPtr->clear();
- if (v) {
- for (const auto& iter : *v) {
- vectorPtr->push_back(iter);
- }
- } else {
- vectorPtr->resize(defaultLength);
- }
- return OK;
- }
- status_t writeFloatVector(Parcel* parcel, const Vector<float>& vector) const {
- std::vector<float> v;
- for (size_t i = 0; i < vector.size(); i++) {
- v.push_back(vector[i]);
- }
- return parcel->writeFloatVector(v);
- }
-
- String16 mDeviceId;
+ std::string mDeviceId;
int32_t mPortId;
uint32_t mType;
- String16 mAddress;
+ std::string mAddress;
int32_t mDeviceLocation;
int32_t mDeviceGroup;
int32_t mIndexInTheGroup;
- Vector<float> mGeometricLocation;
- Vector<float> mOrientation;
- Vector<Vector<float>> mFrequencyResponses;
- Vector<int> mChannelMapping;
+ std::vector<float> mGeometricLocation;
+ std::vector<float> mOrientation;
+ std::vector<std::vector<float>> mFrequencyResponses;
+ std::vector<int> mChannelMapping;
float mSensitivity;
float mMaxSpl;
float mMinSpl;
int32_t mDirectionality;
};
+// Conversion routines, according to AidlConversion.h conventions.
+inline ConversionResult<MicrophoneInfo>
+aidl2legacy_MicrophoneInfo(const media::MicrophoneInfoData& aidl) {
+ MicrophoneInfo legacy;
+ RETURN_IF_ERROR(legacy.readFromParcelable(aidl));
+ return legacy;
+}
+
+inline ConversionResult<media::MicrophoneInfoData>
+legacy2aidl_MicrophoneInfo(const MicrophoneInfo& legacy) {
+ media::MicrophoneInfoData aidl;
+ RETURN_IF_ERROR(legacy.writeToParcelable(&aidl));
+ return aidl;
+}
+
} // namespace media
} // namespace android
diff --git a/include/media/MmapStreamInterface.h b/include/media/MmapStreamInterface.h
index b3bf16d..61de987 100644
--- a/include/media/MmapStreamInterface.h
+++ b/include/media/MmapStreamInterface.h
@@ -22,6 +22,8 @@
#include <utils/Errors.h>
#include <utils/RefBase.h>
+#include <time.h>
+
namespace android {
class MmapStreamCallback;
@@ -103,6 +105,19 @@
virtual status_t getMmapPosition(struct audio_mmap_position *position) = 0;
/**
+ * Get a recent count of the number of audio frames presented/received to/from an
+ * external observer.
+ *
+ * \param[out] position count of presented audio frames
+ * \param[out] timeNanos associated clock time
+ *
+ * \return OK if the external position is set correctly.
+ * NO_INIT in case of initialization error
+ * INVALID_OPERATION if the interface is not implemented
+ */
+ virtual status_t getExternalPosition(uint64_t* position, int64_t* timeNanos) = 0;
+
+ /**
* Start a stream operating in mmap mode.
* createMmapBuffer() must be called before calling start()
*
diff --git a/include/media/VolumeShaper.h b/include/media/VolumeShaper.h
index fe519bb..5271e10 100644
--- a/include/media/VolumeShaper.h
+++ b/include/media/VolumeShaper.h
@@ -22,6 +22,11 @@
#include <math.h>
#include <sstream>
+#include <android/media/VolumeShaperConfiguration.h>
+#include <android/media/VolumeShaperConfigurationOptionFlag.h>
+#include <android/media/VolumeShaperOperation.h>
+#include <android/media/VolumeShaperOperationFlag.h>
+#include <android/media/VolumeShaperState.h>
#include <binder/Parcel.h>
#include <media/Interpolator.h>
#include <utils/Mutex.h>
@@ -284,30 +289,41 @@
clampVolume();
}
- // The parcel layout must match VolumeShaper.java
status_t writeToParcel(Parcel *parcel) const override {
- if (parcel == nullptr) return BAD_VALUE;
- return parcel->writeInt32((int32_t)mType)
- ?: parcel->writeInt32(mId)
- ?: mType == TYPE_ID
- ? NO_ERROR
- : parcel->writeInt32((int32_t)mOptionFlags)
- ?: parcel->writeDouble(mDurationMs)
- ?: Interpolator<S, T>::writeToParcel(parcel);
+ VolumeShaperConfiguration parcelable;
+ writeToParcelable(&parcelable);
+ return parcelable.writeToParcel(parcel);
}
- status_t readFromParcel(const Parcel *parcel) override {
- int32_t type, optionFlags;
- return parcel->readInt32(&type)
- ?: setType((Type)type)
- ?: parcel->readInt32(&mId)
- ?: mType == TYPE_ID
- ? NO_ERROR
- : parcel->readInt32(&optionFlags)
- ?: setOptionFlags((OptionFlag)optionFlags)
- ?: parcel->readDouble(&mDurationMs)
- ?: Interpolator<S, T>::readFromParcel(*parcel)
- ?: checkCurve();
+ void writeToParcelable(VolumeShaperConfiguration *parcelable) const {
+ parcelable->id = getId();
+ parcelable->type = getTypeAsAidl();
+ parcelable->optionFlags = 0;
+ if (mType != TYPE_ID) {
+ parcelable->optionFlags = getOptionFlagsAsAidl();
+ parcelable->durationMs = getDurationMs();
+ parcelable->interpolatorConfig.emplace(); // create value in std::optional
+ Interpolator<S, T>::writeToConfig(&*parcelable->interpolatorConfig);
+ }
+ }
+
+ status_t readFromParcel(const Parcel* parcel) override {
+ VolumeShaperConfiguration data;
+ return data.readFromParcel(parcel)
+ ?: readFromParcelable(data);
+ }
+
+ status_t readFromParcelable(const VolumeShaperConfiguration& parcelable) {
+ setId(parcelable.id);
+ return setTypeFromAidl(parcelable.type)
+ ?: mType == TYPE_ID
+ ? NO_ERROR
+ : setOptionFlagsFromAidl(parcelable.optionFlags)
+ ?: setDurationMs(parcelable.durationMs)
+ ?: !parcelable.interpolatorConfig // check std::optional for value
+ ? BAD_VALUE // must be nonnull.
+ : Interpolator<S, T>::readFromConfig(*parcelable.interpolatorConfig)
+ ?: checkCurve();
}
// Returns a string for debug printing.
@@ -329,6 +345,51 @@
int32_t mId; // A valid id is >= 0.
OptionFlag mOptionFlags; // option flags for the configuration.
double mDurationMs; // duration, must be > 0; default is 1000 ms.
+
+ int32_t getOptionFlagsAsAidl() const {
+ int32_t result = 0;
+ if (getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) {
+ result |=
+ 1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::VOLUME_IN_DBFS);
+ }
+ if (getOptionFlags() & OPTION_FLAG_CLOCK_TIME) {
+ result |= 1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::CLOCK_TIME);
+ }
+ return result;
+ }
+
+ status_t setOptionFlagsFromAidl(int32_t aidl) {
+ std::underlying_type_t<OptionFlag> options = 0;
+ if (aidl & (1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::VOLUME_IN_DBFS))) {
+ options |= OPTION_FLAG_VOLUME_IN_DBFS;
+ }
+ if (aidl & (1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::CLOCK_TIME))) {
+ options |= OPTION_FLAG_CLOCK_TIME;
+ }
+ return setOptionFlags(static_cast<OptionFlag>(options));
+ }
+
+ status_t setTypeFromAidl(VolumeShaperConfigurationType aidl) {
+ switch (aidl) {
+ case VolumeShaperConfigurationType::ID:
+ return setType(TYPE_ID);
+ case VolumeShaperConfigurationType::SCALE:
+ return setType(TYPE_SCALE);
+ default:
+ return BAD_VALUE;
+ }
+ }
+
+ VolumeShaperConfigurationType getTypeAsAidl() const {
+ switch (getType()) {
+ case TYPE_ID:
+ return VolumeShaperConfigurationType::ID;
+ case TYPE_SCALE:
+ return VolumeShaperConfigurationType::SCALE;
+ default:
+ LOG_ALWAYS_FATAL("Shouldn't get here");
+ }
+ }
}; // Configuration
/* VolumeShaper::Operation expresses an operation to perform on the
@@ -420,19 +481,29 @@
return NO_ERROR;
}
- status_t writeToParcel(Parcel *parcel) const override {
+ status_t writeToParcel(Parcel* parcel) const override {
if (parcel == nullptr) return BAD_VALUE;
- return parcel->writeInt32((int32_t)mFlags)
- ?: parcel->writeInt32(mReplaceId)
- ?: parcel->writeFloat(mXOffset);
+ VolumeShaperOperation op;
+ writeToParcelable(&op);
+ return op.writeToParcel(parcel);
}
- status_t readFromParcel(const Parcel *parcel) override {
- int32_t flags;
- return parcel->readInt32(&flags)
- ?: parcel->readInt32(&mReplaceId)
- ?: parcel->readFloat(&mXOffset)
- ?: setFlags((Flag)flags);
+ void writeToParcelable(VolumeShaperOperation* op) const {
+ op->flags = getFlagsAsAidl();
+ op->replaceId = mReplaceId;
+ op->xOffset = mXOffset;
+ }
+
+ status_t readFromParcel(const Parcel* parcel) override {
+ VolumeShaperOperation op;
+ return op.readFromParcel(parcel)
+ ?: readFromParcelable(op);
+ }
+
+ status_t readFromParcelable(const VolumeShaperOperation& op) {
+ mReplaceId = op.replaceId;
+ mXOffset = op.xOffset;
+ return setFlagsFromAidl(op.flags);
}
std::string toString() const {
@@ -445,6 +516,48 @@
}
private:
+ status_t setFlagsFromAidl(int32_t aidl) {
+ std::underlying_type_t<Flag> flags = 0;
+ if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::REVERSE))) {
+ flags |= FLAG_REVERSE;
+ }
+ if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::TERMINATE))) {
+ flags |= FLAG_TERMINATE;
+ }
+ if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::JOIN))) {
+ flags |= FLAG_JOIN;
+ }
+ if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::DELAY))) {
+ flags |= FLAG_DELAY;
+ }
+ if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::CREATE_IF_NECESSARY))) {
+ flags |= FLAG_CREATE_IF_NECESSARY;
+ }
+ return setFlags(static_cast<Flag>(flags));
+ }
+
+ int32_t getFlagsAsAidl() const {
+ int32_t aidl = 0;
+ std::underlying_type_t<Flag> flags = getFlags();
+ if (flags & FLAG_REVERSE) {
+ aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::REVERSE));
+ }
+ if (flags & FLAG_TERMINATE) {
+ aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::TERMINATE));
+ }
+ if (flags & FLAG_JOIN) {
+ aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::JOIN));
+ }
+ if (flags & FLAG_DELAY) {
+ aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::DELAY));
+ }
+ if (flags & FLAG_CREATE_IF_NECESSARY) {
+ aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::CREATE_IF_NECESSARY));
+ }
+ return aidl;
+ }
+
+ private:
Flag mFlags; // operation to do
int32_t mReplaceId; // if >= 0 the id to remove in a replace operation.
S mXOffset; // position in the curve to set if a valid number (not nan)
@@ -483,15 +596,28 @@
mXOffset = xOffset;
}
- status_t writeToParcel(Parcel *parcel) const override {
+ status_t writeToParcel(Parcel* parcel) const override {
if (parcel == nullptr) return BAD_VALUE;
- return parcel->writeFloat(mVolume)
- ?: parcel->writeFloat(mXOffset);
+ VolumeShaperState state;
+ writeToParcelable(&state);
+ return state.writeToParcel(parcel);
}
- status_t readFromParcel(const Parcel *parcel) override {
- return parcel->readFloat(&mVolume)
- ?: parcel->readFloat(&mXOffset);
+ void writeToParcelable(VolumeShaperState* parcelable) const {
+ parcelable->volume = mVolume;
+ parcelable->xOffset = mXOffset;
+ }
+
+ status_t readFromParcel(const Parcel* parcel) override {
+ VolumeShaperState state;
+ return state.readFromParcel(parcel)
+ ?: readFromParcelable(state);
+ }
+
+ status_t readFromParcelable(const VolumeShaperState& parcelable) {
+ mVolume = parcelable.volume;
+ mXOffset = parcelable.xOffset;
+ return OK;
}
std::string toString() const {
diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING
index a6dfb21..5bc7262 100644
--- a/media/TEST_MAPPING
+++ b/media/TEST_MAPPING
@@ -1,32 +1,65 @@
+// for frameworks/av/media
{
- "presubmit": [
- {
- "name": "GtsMediaTestCases",
- "options" : [
+ "presubmit-large": [
+ // runs whenever we change something in this tree
{
- "include-annotation": "android.platform.test.annotations.Presubmit"
+ "name": "CtsMediaTestCases",
+ "options": [
+ {
+ "include-filter": "android.media.cts.EncodeDecodeTest"
+ }
+ ]
},
{
- "include-filter": "com.google.android.media.gts.WidevineGenericOpsTests"
+ "name": "CtsMediaTestCases",
+ "options": [
+ {
+ "include-filter": "android.media.cts.DecodeEditEncodeTest"
+ }
+ ]
}
- ]
- },
- {
- "name": "GtsExoPlayerTestCases",
- "options" : [
+ ],
+ "presubmit": [
{
- "include-annotation": "android.platform.test.annotations.SocPresubmit"
- },
- {
- "include-filter": "com.google.android.exoplayer.gts.DashTest#testWidevine23FpsH264Fixed"
+ "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.WidevineH264PlaybackTests"
+ }
+ ]
}
- ]
- }
- ],
- "imports": [
- {
- "path": "frameworks/av/drm/mediadrm/plugins"
- }
- ]
-}
+ ],
+ "imports": [
+ {
+ "path": "frameworks/av/drm/mediadrm/plugins"
+ }
+ ],
+
+ "platinum-postsubmit": [
+ // runs regularly, independent of changes in this tree.
+ // signals if changes elsewhere break media functionality
+ {
+ "name": "CtsMediaTestCases",
+ "options": [
+ {
+ "include-filter": "android.media.cts.EncodeDecodeTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsMediaTestCases",
+ "options": [
+ {
+ "include-filter": "android.media.cts.DecodeEditEncodeTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/media/audioserver/Android.bp b/media/audioserver/Android.bp
new file mode 100644
index 0000000..be25ffb
--- /dev/null
+++ b/media/audioserver/Android.bp
@@ -0,0 +1,67 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_binary {
+ name: "audioserver",
+
+ srcs: [
+ "main_audioserver.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ header_libs: [
+ "libaudiohal_headers",
+ "libmediametrics_headers",
+ ],
+
+ shared_libs: [
+ "libaaudioservice",
+ "libaudioflinger",
+ "libaudiopolicyservice",
+ "libaudioprocessing",
+ "libbinder",
+ "libcutils",
+ "libhidlbase",
+ "liblog",
+ "libmedia",
+ "libmedialogservice",
+ "libmediautils",
+ "libnbaio",
+ "libnblog",
+ "libpowermanager",
+ "libutils",
+ "libvibrator",
+
+ ],
+
+ // TODO check if we still need all of these include directories
+ include_dirs: [
+ "external/sonic",
+ "frameworks/av/media/libaaudio/include",
+ "frameworks/av/media/libaaudio/src",
+ "frameworks/av/media/libaaudio/src/binding",
+ "frameworks/av/media/libmedia/include",
+ "frameworks/av/services/audioflinger",
+ "frameworks/av/services/audiopolicy",
+ "frameworks/av/services/audiopolicy/common/include",
+ "frameworks/av/services/audiopolicy/common/managerdefinitions/include",
+ "frameworks/av/services/audiopolicy/engine/interface",
+ "frameworks/av/services/audiopolicy/service",
+ "frameworks/av/services/medialog",
+
+ // TODO oboeservice is the old folder name for aaudioservice. It will be changed.
+ "frameworks/av/services/oboeservice",
+ ],
+
+ init_rc: ["audioserver.rc"],
+}
diff --git a/media/audioserver/Android.mk b/media/audioserver/Android.mk
deleted file mode 100644
index acc1b82..0000000
--- a/media/audioserver/Android.mk
+++ /dev/null
@@ -1,54 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- main_audioserver.cpp \
-
-LOCAL_SHARED_LIBRARIES := \
- libaaudioservice \
- libaudioflinger \
- libaudiopolicyservice \
- libaudioprocessing \
- libbinder \
- libcutils \
- liblog \
- libhidlbase \
- libmedia \
- libmedialogservice \
- libmediautils \
- libnbaio \
- libnblog \
- libutils \
- libvibrator
-
-LOCAL_HEADER_LIBRARIES := \
- libaudiohal_headers \
- libmediametrics_headers \
-
-# TODO oboeservice is the old folder name for aaudioservice. It will be changed.
-LOCAL_C_INCLUDES := \
- frameworks/av/services/audioflinger \
- frameworks/av/services/audiopolicy \
- frameworks/av/services/audiopolicy/common/managerdefinitions/include \
- frameworks/av/services/audiopolicy/common/include \
- frameworks/av/services/audiopolicy/engine/interface \
- frameworks/av/services/audiopolicy/service \
- frameworks/av/services/medialog \
- frameworks/av/services/oboeservice \
- frameworks/av/media/libaaudio/include \
- frameworks/av/media/libaaudio/src \
- frameworks/av/media/libaaudio/src/binding \
- frameworks/av/media/libmedia/include \
- external/sonic \
-
-LOCAL_MODULE := audioserver
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-
-LOCAL_INIT_RC := audioserver.rc
-
-LOCAL_CFLAGS := -Werror -Wall
-
-include $(BUILD_EXECUTABLE)
diff --git a/media/audioserver/audioserver.rc b/media/audioserver/audioserver.rc
index f75e4c7..c4a6601 100644
--- a/media/audioserver/audioserver.rc
+++ b/media/audioserver/audioserver.rc
@@ -8,6 +8,7 @@
task_profiles ProcessCapacityHigh HighPerformance
onrestart restart vendor.audio-hal
onrestart restart vendor.audio-hal-4-0-msd
+ onrestart restart audio_proxy_service
# Keep the original service names for backward compatibility
onrestart restart vendor.audio-hal-2-0
onrestart restart audio-hal-2-0
@@ -20,6 +21,7 @@
on property:init.svc.audioserver=stopped
stop vendor.audio-hal
stop vendor.audio-hal-4-0-msd
+ stop audio_proxy_service
# Keep the original service names for backward compatibility
stop vendor.audio-hal-2-0
stop audio-hal-2-0
@@ -28,6 +30,7 @@
# audioserver bringing it back into running state.
start vendor.audio-hal
start vendor.audio-hal-4-0-msd
+ start audio_proxy_service
# Keep the original service names for backward compatibility
start vendor.audio-hal-2-0
start audio-hal-2-0
@@ -35,6 +38,7 @@
on property:init.svc.audioserver=running
start vendor.audio-hal
start vendor.audio-hal-4-0-msd
+ start audio_proxy_service
# Keep the original service names for backward compatibility
start vendor.audio-hal-2-0
start audio-hal-2-0
@@ -44,10 +48,12 @@
# Keep the original service names for backward compatibility
stop vendor.audio-hal
stop vendor.audio-hal-4-0-msd
+ stop audio_proxy_service
stop vendor.audio-hal-2-0
stop audio-hal-2-0
start vendor.audio-hal
start vendor.audio-hal-4-0-msd
+ start audio_proxy_service
start vendor.audio-hal-2-0
start audio-hal-2-0
# reset the property
diff --git a/media/audioserver/main_audioserver.cpp b/media/audioserver/main_audioserver.cpp
index 17309dd..8ee1efb 100644
--- a/media/audioserver/main_audioserver.cpp
+++ b/media/audioserver/main_audioserver.cpp
@@ -29,8 +29,8 @@
#include <mediautils/LimitProcessMemory.h>
#include <utils/Log.h>
-// from LOCAL_C_INCLUDES
-#include "aaudio/AAudioTesting.h"
+// from include_dirs
+#include "aaudio/AAudioTesting.h" // aaudio_policy_t, AAUDIO_PROP_MMAP_POLICY, AAUDIO_POLICY_*
#include "AudioFlinger.h"
#include "AudioPolicyService.h"
#include "AAudioService.h"
diff --git a/media/bufferpool/1.0/TEST_MAPPING b/media/bufferpool/1.0/TEST_MAPPING
new file mode 100644
index 0000000..a1e6a58
--- /dev/null
+++ b/media/bufferpool/1.0/TEST_MAPPING
@@ -0,0 +1,8 @@
+// mappings for frameworks/av/media/bufferpool/1.0
+{
+ "presubmit": [
+
+ { "name": "VtsVndkHidlBufferpoolV1_0TargetSingleTest" },
+ { "name": "VtsVndkHidlBufferpoolV1_0TargetMultiTest"}
+ ]
+}
diff --git a/media/bufferpool/1.0/vts/Android.bp b/media/bufferpool/1.0/vts/Android.bp
index b39871e..0977ba5 100644
--- a/media/bufferpool/1.0/vts/Android.bp
+++ b/media/bufferpool/1.0/vts/Android.bp
@@ -25,6 +25,7 @@
cc_test {
name: "VtsVndkHidlBufferpoolV1_0TargetSingleTest",
+ test_suites: ["device-tests"],
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
"allocator.cpp",
@@ -43,6 +44,7 @@
cc_test {
name: "VtsVndkHidlBufferpoolV1_0TargetMultiTest",
+ test_suites: ["device-tests"],
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
"allocator.cpp",
diff --git a/media/bufferpool/2.0/TEST_MAPPING b/media/bufferpool/2.0/TEST_MAPPING
new file mode 100644
index 0000000..65dee2c
--- /dev/null
+++ b/media/bufferpool/2.0/TEST_MAPPING
@@ -0,0 +1,7 @@
+// mappings for frameworks/av/media/bufferpool/2.0
+{
+ "presubmit": [
+ { "name": "VtsVndkHidlBufferpoolV2_0TargetSingleTest"},
+ { "name": "VtsVndkHidlBufferpoolV2_0TargetMultiTest"}
+ ]
+}
diff --git a/media/bufferpool/2.0/tests/Android.bp b/media/bufferpool/2.0/tests/Android.bp
index d6db202..803a813 100644
--- a/media/bufferpool/2.0/tests/Android.bp
+++ b/media/bufferpool/2.0/tests/Android.bp
@@ -25,6 +25,7 @@
cc_test {
name: "VtsVndkHidlBufferpoolV2_0TargetSingleTest",
+ test_suites: ["device-tests"],
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
"allocator.cpp",
@@ -33,7 +34,7 @@
static_libs: [
"android.hardware.media.bufferpool@2.0",
"libcutils",
- "libstagefright_bufferpool@2.0",
+ "libstagefright_bufferpool@2.0.1",
],
shared_libs: [
"libfmq",
@@ -43,6 +44,7 @@
cc_test {
name: "VtsVndkHidlBufferpoolV2_0TargetMultiTest",
+ test_suites: ["device-tests"],
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
"allocator.cpp",
@@ -51,10 +53,30 @@
static_libs: [
"android.hardware.media.bufferpool@2.0",
"libcutils",
- "libstagefright_bufferpool@2.0",
+ "libstagefright_bufferpool@2.0.1",
],
shared_libs: [
"libfmq",
],
compile_multilib: "both",
}
+
+cc_test {
+ name: "VtsVndkHidlBufferpoolV2_0TargetCondTest",
+ test_suites: ["device-tests"],
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
+ "allocator.cpp",
+ "cond.cpp",
+ ],
+ static_libs: [
+ "android.hardware.media.bufferpool@2.0",
+ "libcutils",
+ "libstagefright_bufferpool@2.0.1",
+ ],
+ shared_libs: [
+ "libhidlbase",
+ "libfmq",
+ ],
+ compile_multilib: "both",
+}
diff --git a/media/bufferpool/2.0/tests/allocator.cpp b/media/bufferpool/2.0/tests/allocator.cpp
index 843f7ea..25b08ef 100644
--- a/media/bufferpool/2.0/tests/allocator.cpp
+++ b/media/bufferpool/2.0/tests/allocator.cpp
@@ -120,6 +120,24 @@
}
+void IpcMutex::init() {
+ pthread_mutexattr_t mattr;
+ pthread_mutexattr_init(&mattr);
+ pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
+ pthread_mutex_init(&lock, &mattr);
+ pthread_mutexattr_destroy(&mattr);
+
+ pthread_condattr_t cattr;
+ pthread_condattr_init(&cattr);
+ pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
+ pthread_cond_init(&cond, &cattr);
+ pthread_condattr_destroy(&cattr);
+}
+
+IpcMutex *IpcMutex::Import(void *pMutex) {
+ return reinterpret_cast<IpcMutex *>(pMutex);
+}
+
ResultStatus TestBufferPoolAllocator::allocate(
const std::vector<uint8_t> ¶ms,
@@ -201,9 +219,33 @@
return false;
}
+bool TestBufferPoolAllocator::MapMemoryForMutex(const native_handle_t *handle, void **mem) {
+ if (!HandleAshmem::isValid(handle)) {
+ return false;
+ }
+ const HandleAshmem *o = static_cast<const HandleAshmem*>(handle);
+ *mem = mmap(
+ NULL, o->size(), PROT_READ|PROT_WRITE, MAP_SHARED, o->ashmemFd(), 0);
+ if (*mem == MAP_FAILED || *mem == nullptr) {
+ return false;
+ }
+ return true;
+}
+
+bool TestBufferPoolAllocator::UnmapMemoryForMutex(void *mem) {
+ munmap(mem, sizeof(IpcMutex));
+ return true;
+}
+
void getTestAllocatorParams(std::vector<uint8_t> *params) {
constexpr static int kAllocationSize = 1024 * 10;
Params ashmemParams(kAllocationSize);
params->assign(ashmemParams.array, ashmemParams.array + sizeof(ashmemParams));
}
+
+void getIpcMutexParams(std::vector<uint8_t> *params) {
+ Params ashmemParams(sizeof(IpcMutex));
+
+ params->assign(ashmemParams.array, ashmemParams.array + sizeof(ashmemParams));
+}
diff --git a/media/bufferpool/2.0/tests/allocator.h b/media/bufferpool/2.0/tests/allocator.h
index 5281dc3..862d1a5 100644
--- a/media/bufferpool/2.0/tests/allocator.h
+++ b/media/bufferpool/2.0/tests/allocator.h
@@ -17,6 +17,7 @@
#ifndef VNDK_HIDL_BUFFERPOOL_V2_0_ALLOCATOR_H
#define VNDK_HIDL_BUFFERPOOL_V2_0_ALLOCATOR_H
+#include <pthread.h>
#include <bufferpool/BufferPoolTypes.h>
using android::hardware::media::bufferpool::V2_0::ResultStatus;
@@ -25,6 +26,17 @@
using android::hardware::media::bufferpool::V2_0::implementation::
BufferPoolAllocator;
+struct IpcMutex {
+ pthread_mutex_t lock;
+ pthread_cond_t cond;
+ int counter = 0;
+ bool signalled = false;
+
+ void init();
+
+ static IpcMutex *Import(void *mem);
+};
+
// buffer allocator for the tests
class TestBufferPoolAllocator : public BufferPoolAllocator {
public:
@@ -43,9 +55,14 @@
static bool Verify(const native_handle_t *handle, const unsigned char val);
+ static bool MapMemoryForMutex(const native_handle_t *handle, void **mem);
+
+ static bool UnmapMemoryForMutex(void *mem);
};
// retrieve buffer allocator paramters
void getTestAllocatorParams(std::vector<uint8_t> *params);
+void getIpcMutexParams(std::vector<uint8_t> *params);
+
#endif // VNDK_HIDL_BUFFERPOOL_V2_0_ALLOCATOR_H
diff --git a/media/bufferpool/2.0/tests/cond.cpp b/media/bufferpool/2.0/tests/cond.cpp
new file mode 100644
index 0000000..21beea8
--- /dev/null
+++ b/media/bufferpool/2.0/tests/cond.cpp
@@ -0,0 +1,269 @@
+/*
+ * 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 "buffferpool_unit_test"
+
+#include <gtest/gtest.h>
+
+#include <android-base/logging.h>
+#include <binder/ProcessState.h>
+#include <bufferpool/ClientManager.h>
+#include <errno.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/LegacySupport.h>
+#include <hidl/Status.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <iostream>
+#include <memory>
+#include <vector>
+#include "allocator.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::hidl_handle;
+using android::hardware::media::bufferpool::V2_0::IClientManager;
+using android::hardware::media::bufferpool::V2_0::ResultStatus;
+using android::hardware::media::bufferpool::V2_0::implementation::BufferId;
+using android::hardware::media::bufferpool::V2_0::implementation::ClientManager;
+using android::hardware::media::bufferpool::V2_0::implementation::ConnectionId;
+using android::hardware::media::bufferpool::V2_0::implementation::TransactionId;
+using android::hardware::media::bufferpool::BufferPoolData;
+
+namespace {
+
+// communication message types between processes.
+enum PipeCommand : int32_t {
+ INIT_OK = 0,
+ INIT_ERROR,
+ SEND,
+ RECEIVE_OK,
+ RECEIVE_ERROR,
+};
+
+// communication message between processes.
+union PipeMessage {
+ struct {
+ int32_t command;
+ BufferId bufferId;
+ ConnectionId connectionId;
+ TransactionId transactionId;
+ int64_t timestampUs;
+ } data;
+ char array[0];
+};
+
+constexpr int kSignalInt = 200;
+
+// media.bufferpool test setup
+class BufferpoolMultiTest : public ::testing::Test {
+ public:
+ virtual void SetUp() override {
+ ResultStatus status;
+ mReceiverPid = -1;
+ mConnectionValid = false;
+
+ ASSERT_TRUE(pipe(mCommandPipeFds) == 0);
+ ASSERT_TRUE(pipe(mResultPipeFds) == 0);
+
+ mReceiverPid = fork();
+ ASSERT_TRUE(mReceiverPid >= 0);
+
+ if (mReceiverPid == 0) {
+ doReceiver();
+ // In order to ignore gtest behaviour, wait for being killed from
+ // tearDown
+ pause();
+ }
+
+ mManager = ClientManager::getInstance();
+ ASSERT_NE(mManager, nullptr);
+
+ mAllocator = std::make_shared<TestBufferPoolAllocator>();
+ ASSERT_TRUE((bool)mAllocator);
+
+ status = mManager->create(mAllocator, &mConnectionId);
+ ASSERT_TRUE(status == ResultStatus::OK);
+ mConnectionValid = true;
+ }
+
+ virtual void TearDown() override {
+ if (mReceiverPid > 0) {
+ kill(mReceiverPid, SIGKILL);
+ int wstatus;
+ wait(&wstatus);
+ }
+
+ if (mConnectionValid) {
+ mManager->close(mConnectionId);
+ }
+ }
+
+ protected:
+ static void description(const std::string& description) {
+ RecordProperty("description", description);
+ }
+
+ android::sp<ClientManager> mManager;
+ std::shared_ptr<BufferPoolAllocator> mAllocator;
+ bool mConnectionValid;
+ ConnectionId mConnectionId;
+ pid_t mReceiverPid;
+ int mCommandPipeFds[2];
+ int mResultPipeFds[2];
+
+ bool sendMessage(int *pipes, const PipeMessage &message) {
+ int ret = write(pipes[1], message.array, sizeof(PipeMessage));
+ return ret == sizeof(PipeMessage);
+ }
+
+ bool receiveMessage(int *pipes, PipeMessage *message) {
+ int ret = read(pipes[0], message->array, sizeof(PipeMessage));
+ return ret == sizeof(PipeMessage);
+ }
+
+ void doReceiver() {
+ configureRpcThreadpool(1, false);
+ PipeMessage message;
+ mManager = ClientManager::getInstance();
+ if (!mManager) {
+ message.data.command = PipeCommand::INIT_ERROR;
+ sendMessage(mResultPipeFds, message);
+ return;
+ }
+ android::status_t status = mManager->registerAsService();
+ if (status != android::OK) {
+ message.data.command = PipeCommand::INIT_ERROR;
+ sendMessage(mResultPipeFds, message);
+ return;
+ }
+ message.data.command = PipeCommand::INIT_OK;
+ sendMessage(mResultPipeFds, message);
+
+ int val = 0;
+ receiveMessage(mCommandPipeFds, &message);
+ {
+ native_handle_t *rhandle = nullptr;
+ std::shared_ptr<BufferPoolData> rbuffer;
+ void *mem = nullptr;
+ IpcMutex *mutex = nullptr;
+ ResultStatus status = mManager->receive(
+ message.data.connectionId, message.data.transactionId,
+ message.data.bufferId, message.data.timestampUs, &rhandle, &rbuffer);
+ mManager->close(message.data.connectionId);
+ if (status != ResultStatus::OK) {
+ message.data.command = PipeCommand::RECEIVE_ERROR;
+ sendMessage(mResultPipeFds, message);
+ return;
+ }
+ if (!TestBufferPoolAllocator::MapMemoryForMutex(rhandle, &mem)) {
+ message.data.command = PipeCommand::RECEIVE_ERROR;
+ sendMessage(mResultPipeFds, message);
+ return;
+ }
+ mutex = IpcMutex::Import(mem);
+ pthread_mutex_lock(&(mutex->lock));
+ while (mutex->signalled != true) {
+ pthread_cond_wait(&(mutex->cond), &(mutex->lock));
+ }
+ val = mutex->counter;
+ pthread_mutex_unlock(&(mutex->lock));
+
+ (void)TestBufferPoolAllocator::UnmapMemoryForMutex(mem);
+ if (rhandle) {
+ native_handle_close(rhandle);
+ native_handle_delete(rhandle);
+ }
+ }
+ if (val == kSignalInt) {
+ message.data.command = PipeCommand::RECEIVE_OK;
+ } else {
+ message.data.command = PipeCommand::RECEIVE_ERROR;
+ }
+ sendMessage(mResultPipeFds, message);
+ }
+};
+
+// Buffer transfer test between processes.
+TEST_F(BufferpoolMultiTest, TransferBuffer) {
+ ResultStatus status;
+ PipeMessage message;
+
+ ASSERT_TRUE(receiveMessage(mResultPipeFds, &message));
+
+ android::sp<IClientManager> receiver = IClientManager::getService();
+ ConnectionId receiverId;
+ ASSERT_TRUE((bool)receiver);
+
+ status = mManager->registerSender(receiver, mConnectionId, &receiverId);
+ ASSERT_TRUE(status == ResultStatus::OK);
+ {
+ native_handle_t *shandle = nullptr;
+ std::shared_ptr<BufferPoolData> sbuffer;
+ TransactionId transactionId;
+ int64_t postUs;
+ std::vector<uint8_t> vecParams;
+ void *mem = nullptr;
+ IpcMutex *mutex = nullptr;
+
+ getIpcMutexParams(&vecParams);
+ status = mManager->allocate(mConnectionId, vecParams, &shandle, &sbuffer);
+ ASSERT_TRUE(status == ResultStatus::OK);
+
+ ASSERT_TRUE(TestBufferPoolAllocator::MapMemoryForMutex(shandle, &mem));
+
+ mutex = new(mem) IpcMutex();
+ mutex->init();
+
+ status = mManager->postSend(receiverId, sbuffer, &transactionId, &postUs);
+ ASSERT_TRUE(status == ResultStatus::OK);
+
+ message.data.command = PipeCommand::SEND;
+ message.data.bufferId = sbuffer->mId;
+ message.data.connectionId = receiverId;
+ message.data.transactionId = transactionId;
+ message.data.timestampUs = postUs;
+ sendMessage(mCommandPipeFds, message);
+ for (int i=0; i < 200000000; ++i) {
+ // no-op in order to ensure
+ // pthread_cond_wait is called before pthread_cond_signal
+ }
+ pthread_mutex_lock(&(mutex->lock));
+ mutex->counter = kSignalInt;
+ mutex->signalled = true;
+ pthread_cond_signal(&(mutex->cond));
+ pthread_mutex_unlock(&(mutex->lock));
+ (void)TestBufferPoolAllocator::UnmapMemoryForMutex(mem);
+ if (shandle) {
+ native_handle_close(shandle);
+ native_handle_delete(shandle);
+ }
+ }
+ EXPECT_TRUE(receiveMessage(mResultPipeFds, &message));
+ EXPECT_TRUE(message.data.command == PipeCommand::RECEIVE_OK);
+}
+
+} // anonymous namespace
+
+int main(int argc, char** argv) {
+ android::hardware::details::setTrebleTestingOverride(true);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
+ return status;
+}
diff --git a/media/bufferpool/2.0/tests/multi.cpp b/media/bufferpool/2.0/tests/multi.cpp
index b40838e..43b0a8c 100644
--- a/media/bufferpool/2.0/tests/multi.cpp
+++ b/media/bufferpool/2.0/tests/multi.cpp
@@ -161,11 +161,18 @@
message.data.bufferId, message.data.timestampUs, &rhandle, &rbuffer);
mManager->close(message.data.connectionId);
if (status != ResultStatus::OK) {
- if (!TestBufferPoolAllocator::Verify(rhandle, 0x77)) {
- message.data.command = PipeCommand::RECEIVE_ERROR;
- sendMessage(mResultPipeFds, message);
- return;
- }
+ message.data.command = PipeCommand::RECEIVE_ERROR;
+ sendMessage(mResultPipeFds, message);
+ return;
+ }
+ if (!TestBufferPoolAllocator::Verify(rhandle, 0x77)) {
+ message.data.command = PipeCommand::RECEIVE_ERROR;
+ sendMessage(mResultPipeFds, message);
+ return;
+ }
+ if (rhandle) {
+ native_handle_close(rhandle);
+ native_handle_delete(rhandle);
}
}
message.data.command = PipeCommand::RECEIVE_OK;
@@ -198,6 +205,10 @@
ASSERT_TRUE(status == ResultStatus::OK);
ASSERT_TRUE(TestBufferPoolAllocator::Fill(shandle, 0x77));
+ if (shandle) {
+ native_handle_close(shandle);
+ native_handle_delete(shandle);
+ }
status = mManager->postSend(receiverId, sbuffer, &transactionId, &postUs);
ASSERT_TRUE(status == ResultStatus::OK);
@@ -210,6 +221,7 @@
sendMessage(mCommandPipeFds, message);
}
EXPECT_TRUE(receiveMessage(mResultPipeFds, &message));
+ EXPECT_TRUE(message.data.command == PipeCommand::RECEIVE_OK);
}
} // anonymous namespace
diff --git a/media/bufferpool/2.0/tests/single.cpp b/media/bufferpool/2.0/tests/single.cpp
index 777edcf..1e9027b 100644
--- a/media/bufferpool/2.0/tests/single.cpp
+++ b/media/bufferpool/2.0/tests/single.cpp
@@ -102,6 +102,10 @@
for (int i = 0; i < kNumAllocationTest; ++i) {
status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &buffer[i]);
ASSERT_TRUE(status == ResultStatus::OK);
+ if (allocHandle) {
+ native_handle_close(allocHandle);
+ native_handle_delete(allocHandle);
+ }
}
for (int i = 0; i < kNumAllocationTest; ++i) {
for (int j = i + 1; j < kNumAllocationTest; ++j) {
@@ -125,6 +129,10 @@
status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &buffer);
ASSERT_TRUE(status == ResultStatus::OK);
bid[i] = buffer->mId;
+ if (allocHandle) {
+ native_handle_close(allocHandle);
+ native_handle_delete(allocHandle);
+ }
}
for (int i = 1; i < kNumRecycleTest; ++i) {
ASSERT_TRUE(bid[i - 1] == bid[i]);
@@ -154,6 +162,15 @@
&recvHandle, &rbuffer);
EXPECT_TRUE(status == ResultStatus::OK);
ASSERT_TRUE(TestBufferPoolAllocator::Verify(recvHandle, 0x77));
+
+ if (allocHandle) {
+ native_handle_close(allocHandle);
+ native_handle_delete(allocHandle);
+ }
+ if (recvHandle) {
+ native_handle_close(recvHandle);
+ native_handle_delete(recvHandle);
+ }
}
} // anonymous namespace
diff --git a/media/codec2/TEST_MAPPING b/media/codec2/TEST_MAPPING
index 8afa1a8..6ac4210 100644
--- a/media/codec2/TEST_MAPPING
+++ b/media/codec2/TEST_MAPPING
@@ -1,5 +1,12 @@
{
"presubmit": [
+ // TODO failing 4 of 13
+ // { "name": "codec2_core_param_test"},
+ // TODO(b/155516524)
+ // { "name": "codec2_vndk_interface_test"},
+ { "name": "codec2_vndk_test"}
+ ],
+ "presubmit-large": [
{
"name": "CtsMediaTestCases",
"options": [
diff --git a/media/codec2/components/aac/DrcPresModeWrap.cpp b/media/codec2/components/aac/DrcPresModeWrap.cpp
index bee969b..7ce5c9d 100644
--- a/media/codec2/components/aac/DrcPresModeWrap.cpp
+++ b/media/codec2/components/aac/DrcPresModeWrap.cpp
@@ -161,7 +161,7 @@
int newHeavy = mDesHeavy;
if (mDataUpdate) {
- // sanity check
+ // Validation check
if ((mDesTarget < MAX_TARGET_LEVEL) && (mDesTarget != -1)){
mDesTarget = MAX_TARGET_LEVEL; // limit target level to -10 dB or below
newTarget = MAX_TARGET_LEVEL;
@@ -217,7 +217,7 @@
}
else { // handle other used encoder target levels
- // Sanity check: DRC presentation mode is only specified for max. 5.1 channels
+ // Validation check: DRC presentation mode is only specified for max. 5.1 channels
if (mStreamNrAACChan > 6) {
drcPresMode = 0;
}
@@ -308,7 +308,7 @@
} // switch()
} // if (mEncoderTarget == GPM_ENCODER_TARGET_LEVEL)
- // sanity again
+ // Validation check again
if (newHeavy == 1) {
newBoostFactor=127; // not really needed as the same would be done by the decoder anyway
newAttFactor = 127;
diff --git a/media/codec2/components/flac/Android.bp b/media/codec2/components/flac/Android.bp
index 1143bed..38dfce4 100644
--- a/media/codec2/components/flac/Android.bp
+++ b/media/codec2/components/flac/Android.bp
@@ -42,11 +42,8 @@
srcs: ["C2SoftFlacEnc.cpp"],
- shared_libs: [
- "libaudioutils",
- ],
-
static_libs: [
"libFLAC",
+ "libaudioutils",
],
}
diff --git a/media/codec2/components/flac/C2SoftFlacEnc.cpp b/media/codec2/components/flac/C2SoftFlacEnc.cpp
index 1c0babd..6fead3a 100644
--- a/media/codec2/components/flac/C2SoftFlacEnc.cpp
+++ b/media/codec2/components/flac/C2SoftFlacEnc.cpp
@@ -262,9 +262,10 @@
work->result = C2_NO_MEMORY;
return;
}
- C2WriteView wView = mOutputBlock->map().get();
- if (wView.error()) {
- ALOGE("write view map failed %d", wView.error());
+
+ err = mOutputBlock->map().get().error();
+ if (err) {
+ ALOGE("write view map failed %d", err);
work->result = C2_CORRUPTED;
return;
}
diff --git a/media/codec2/hidl/1.0/utils/Android.bp b/media/codec2/hidl/1.0/utils/Android.bp
index 008def8..122aacd 100644
--- a/media/codec2/hidl/1.0/utils/Android.bp
+++ b/media/codec2/hidl/1.0/utils/Android.bp
@@ -15,7 +15,6 @@
defaults: ["hidl_defaults"],
srcs: [
- "OutputBufferQueue.cpp",
"types.cpp",
],
diff --git a/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp b/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp
index 5ec88ec..7c2e014 100644
--- a/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp
+++ b/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp
@@ -201,6 +201,8 @@
c2_status_t err = mAllocator->priorGraphicAllocation(handle, &alloc);
mAllocatorMutex.unlock();
if (err != OK) {
+ native_handle_close(handle);
+ native_handle_delete(handle);
return UNKNOWN_ERROR;
}
std::shared_ptr<C2GraphicBlock> block =
diff --git a/media/codec2/hidl/1.0/utils/OutputBufferQueue.cpp b/media/codec2/hidl/1.0/utils/OutputBufferQueue.cpp
deleted file mode 100644
index c4a72ef..0000000
--- a/media/codec2/hidl/1.0/utils/OutputBufferQueue.cpp
+++ /dev/null
@@ -1,335 +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-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/OutputBufferQueue.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/OutputBufferQueue.h
deleted file mode 100644
index 80368f7..0000000
--- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/OutputBufferQueue.h
+++ /dev/null
@@ -1,85 +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 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 {
-namespace c2 {
-namespace V1_0 {
-namespace utils {
-
-// BufferQueue-Based Block Operations
-// ==================================
-
-// Manage BufferQueue and graphic blocks for both component and codec.
-// Manage graphic blocks ownership consistently during surface change.
-struct OutputBufferQueue {
-
- OutputBufferQueue();
-
- ~OutputBufferQueue();
-
- // Configure a new surface to render graphic blocks.
- // Graphic blocks from older surface will be migrated to new surface.
- bool configure(const sp<IGraphicBufferProducer>& igbp,
- uint32_t generation,
- uint64_t bqId);
-
- // Render a graphic block to current surface.
- status_t outputBuffer(
- const C2ConstGraphicBlock& block,
- const BnGraphicBufferProducer::QueueBufferInput& input,
- BnGraphicBufferProducer::QueueBufferOutput* output);
-
- // Call holdBufferQueueBlock() on output blocks in the given workList.
- // The OutputBufferQueue will take the ownership of output blocks.
- //
- // Note: This function should be called after WorkBundle has been received
- // from another process.
- void holdBufferQueueBlocks(
- const std::list<std::unique_ptr<C2Work>>& workList);
-
-private:
-
- 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
-} // namespace V1_0
-} // namespace c2
-} // namespace media
-} // namespace hardware
-} // namespace android
-
-#endif // CODEC2_HIDL_V1_0_UTILS_OUTPUT_BUFFER_QUEUE
diff --git a/media/codec2/hidl/1.0/vts/functional/master/VtsHalMediaC2V1_0TargetMasterTest.cpp b/media/codec2/hidl/1.0/vts/functional/master/VtsHalMediaC2V1_0TargetMasterTest.cpp
index fb1c291..47ceed5 100644
--- a/media/codec2/hidl/1.0/vts/functional/master/VtsHalMediaC2V1_0TargetMasterTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/master/VtsHalMediaC2V1_0TargetMasterTest.cpp
@@ -73,9 +73,10 @@
ASSERT_NE(listener, nullptr);
// Create component from all known services
- component =
- mClient->CreateComponentByName(listTraits[i].name.c_str(), listener, &mClient);
- ASSERT_NE(component, nullptr)
+ const c2_status_t status =
+ android::Codec2Client::CreateComponentByName(
+ listTraits[i].name.c_str(), listener, &component, &mClient);
+ ASSERT_EQ(status, C2_OK)
<< "Create component failed for " << listTraits[i].name.c_str();
}
}
diff --git a/media/codec2/hidl/1.1/utils/Android.bp b/media/codec2/hidl/1.1/utils/Android.bp
index 839a910..0eeedb6 100644
--- a/media/codec2/hidl/1.1/utils/Android.bp
+++ b/media/codec2/hidl/1.1/utils/Android.bp
@@ -15,7 +15,6 @@
defaults: ["hidl_defaults"],
srcs: [
- "OutputBufferQueue.cpp",
"types.cpp",
],
@@ -176,14 +175,3 @@
],
}
-// 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/ComponentStore.cpp b/media/codec2/hidl/1.1/utils/ComponentStore.cpp
index 163686d..d47abdd 100644
--- a/media/codec2/hidl/1.1/utils/ComponentStore.cpp
+++ b/media/codec2/hidl/1.1/utils/ComponentStore.cpp
@@ -366,6 +366,9 @@
mStore->createComponent(name, &c2component));
if (status == Status::OK) {
+#ifndef __ANDROID_APEX__
+ c2component = GetFilterWrapper()->maybeWrapComponent(c2component);
+#endif
onInterfaceLoaded(c2component->intf());
component = new Component(c2component, listener, this, pool);
if (!component) {
diff --git a/media/codec2/hidl/1.1/utils/OutputBufferQueue.cpp b/media/codec2/hidl/1.1/utils/OutputBufferQueue.cpp
deleted file mode 100644
index 65756e8..0000000
--- a/media/codec2/hidl/1.1/utils/OutputBufferQueue.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * 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/OutputBufferQueue.h b/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/OutputBufferQueue.h
deleted file mode 100644
index f77852d..0000000
--- a/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/OutputBufferQueue.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.2/utils/Android.bp b/media/codec2/hidl/1.2/utils/Android.bp
new file mode 100644
index 0000000..e4e4ad5
--- /dev/null
+++ b/media/codec2/hidl/1.2/utils/Android.bp
@@ -0,0 +1,206 @@
+// DO NOT DEPEND ON THIS DIRECTLY
+// use libcodec2-hidl-client-defaults instead
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_library {
+ name: "libcodec2_hidl_client@1.2",
+
+ defaults: ["hidl_defaults"],
+
+ srcs: [
+ "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",
+ "android.hardware.media.c2@1.2",
+ "libbase",
+ "libcodec2",
+ "libcodec2_hidl_client@1.0",
+ "libcodec2_hidl_client@1.1",
+ "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",
+ "android.hardware.media.c2@1.2",
+ "libcodec2",
+ "libcodec2_hidl_client@1.0",
+ "libcodec2_hidl_client@1.1",
+ "libgui",
+ "libstagefright_bufferpool@2.0.1",
+ "libui",
+ ],
+
+ // Device does not boot when global ThinLTO is enabled for this library.
+ // http://b/170595429
+ lto: {
+ never: true,
+ },
+}
+
+
+// DO NOT DEPEND ON THIS DIRECTLY
+// use libcodec2-hidl-defaults instead
+cc_library {
+ name: "libcodec2_hidl@1.2",
+ vendor_available: true,
+ min_sdk_version: "29",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media.swcodec",
+ "test_com.android.media.swcodec",
+ ],
+
+ 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.c2@1.2",
+ "android.hardware.media.omx@1.0",
+ "libbase",
+ "libcodec2",
+ "libcodec2_hidl@1.0",
+ "libcodec2_hidl@1.1",
+ "libcodec2_hidl_plugin_stub",
+ "libcodec2_vndk",
+ "libcutils",
+ "libhidlbase",
+ "liblog",
+ "libstagefright_bufferpool@2.0.1",
+ "libstagefright_bufferqueue_helper_novndk",
+ "libui",
+ "libutils",
+ ],
+
+ target: {
+ vendor: {
+ exclude_shared_libs: [
+ "libstagefright_bufferqueue_helper_novndk",
+ "libcodec2_hidl_plugin_stub",
+ ],
+ shared_libs: [
+ "libstagefright_bufferqueue_helper",
+ "libcodec2_hidl_plugin",
+ ],
+ },
+ apex: {
+ exclude_shared_libs: [
+ "libcodec2_hidl_plugin_stub",
+ "libcodec2_hidl_plugin",
+ ],
+ },
+ },
+
+ export_include_dirs: [
+ "include",
+ ],
+
+ export_shared_lib_headers: [
+ "android.hardware.media.c2@1.0",
+ "android.hardware.media.c2@1.1",
+ "android.hardware.media.c2@1.2",
+ "libcodec2",
+ "libcodec2_hidl@1.0",
+ "libcodec2_hidl@1.1",
+ "libcodec2_vndk",
+ "libhidlbase",
+ "libstagefright_bufferpool@2.0.1",
+ "libui",
+ ],
+}
+
+// public dependency for Codec 2.0 HAL service implementations
+cc_defaults {
+ name: "libcodec2-hidl-defaults@1.2",
+ defaults: ["libcodec2-impl-defaults"],
+
+ shared_libs: [
+ "android.hardware.media.c2@1.0",
+ "android.hardware.media.c2@1.1",
+ "android.hardware.media.c2@1.2",
+ "libcodec2_hidl@1.0",
+ "libcodec2_hidl@1.1",
+ "libcodec2_hidl@1.2",
+ "libcodec2_vndk",
+ "libhidlbase",
+ ],
+}
+
+// public dependency for Codec 2.0 HAL client
+cc_defaults {
+ name: "libcodec2-hidl-client-defaults@1.2",
+ defaults: ["libcodec2-impl-defaults"],
+
+ shared_libs: [
+ "android.hardware.media.c2@1.0",
+ "android.hardware.media.c2@1.1",
+ "android.hardware.media.c2@1.2",
+ "libcodec2_hidl_client@1.0",
+ "libcodec2_hidl_client@1.1",
+ "libcodec2_hidl_client@1.2",
+ "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.2"],
+}
+
+// Alias to the latest "defaults" for Codec 2.0 HAL client
+cc_defaults {
+ name: "libcodec2-hidl-client-defaults",
+ defaults: ["libcodec2-hidl-client-defaults@1.2"],
+}
+
diff --git a/media/codec2/hidl/1.2/utils/Component.cpp b/media/codec2/hidl/1.2/utils/Component.cpp
new file mode 100644
index 0000000..8924e6d
--- /dev/null
+++ b/media/codec2/hidl/1.2/utils/Component.cpp
@@ -0,0 +1,535 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Codec2-Component@1.2"
+#include <android-base/logging.h>
+
+#include <codec2/hidl/1.2/Component.h>
+#include <codec2/hidl/1.2/ComponentStore.h>
+#include <codec2/hidl/1.2/InputBufferManager.h>
+
+#ifndef __ANDROID_APEX__
+#include <FilterWrapper.h>
+#endif
+
+#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_2 {
+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;
+#ifdef __ANDROID_APEX__
+ c2_status_t status = CreateCodec2BlockPool(
+ static_cast<C2PlatformAllocatorStore::id_t>(allocatorId),
+ mComponent,
+ &blockPool);
+#else
+ c2_status_t status = ComponentStore::GetFilterWrapper()->createBlockPool(
+ static_cast<C2PlatformAllocatorStore::id_t>(allocatorId),
+ mComponent,
+ &blockPool);
+#endif
+ 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();
+}
+
+Return<Status> Component::setOutputSurfaceWithSyncObj(
+ uint64_t blockPoolId, const sp<HGraphicBufferProducer2>& surface,
+ const SurfaceSyncObj& syncObject) {
+ 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) {
+ const native_handle_t *h = syncObject.syncMemory;
+ native_handle_t *syncMemory = h ? native_handle_clone(h) : nullptr;
+ uint64_t bqId = syncObject.bqId;
+ uint32_t generationId = syncObject.generationId;
+ uint64_t consumerUsage = syncObject.consumerUsage;
+
+ bqPool->setRenderCallback(cb);
+ bqPool->configureProducer(surface, syncMemory, bqId,
+ generationId, consumerUsage);
+ }
+ }
+ return Status::OK;
+}
+
+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_2
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/codec2/hidl/1.2/utils/ComponentInterface.cpp b/media/codec2/hidl/1.2/utils/ComponentInterface.cpp
new file mode 100644
index 0000000..30fe4d6
--- /dev/null
+++ b/media/codec2/hidl/1.2/utils/ComponentInterface.cpp
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <codec2/hidl/1.2/ComponentInterface.h>
diff --git a/media/codec2/hidl/1.2/utils/ComponentStore.cpp b/media/codec2/hidl/1.2/utils/ComponentStore.cpp
new file mode 100644
index 0000000..9fac5d5
--- /dev/null
+++ b/media/codec2/hidl/1.2/utils/ComponentStore.cpp
@@ -0,0 +1,562 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Codec2-ComponentStore@1.2"
+#include <android-base/logging.h>
+
+#include <codec2/hidl/1.2/ComponentStore.h>
+#include <codec2/hidl/1.2/InputSurface.h>
+#include <codec2/hidl/1.2/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>
+
+#ifndef __ANDROID_APEX__
+#include <codec2/hidl/plugin/FilterPlugin.h>
+#include <dlfcn.h>
+#include <C2Config.h>
+#include <DefaultFilterPlugin.h>
+#include <FilterWrapper.h>
+#endif
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_2 {
+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;
+}
+
+#ifndef __ANDROID_APEX__
+// static
+std::shared_ptr<FilterWrapper> ComponentStore::GetFilterWrapper() {
+ constexpr const char kPluginPath[] = "libc2filterplugin.so";
+ static std::shared_ptr<FilterWrapper> wrapper = FilterWrapper::Create(
+ std::make_unique<DefaultFilterPlugin>(kPluginPath));
+ return wrapper;
+}
+#endif
+
+// 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) {
+#ifndef __ANDROID_APEX__
+ c2component = GetFilterWrapper()->maybeWrapComponent(c2component);
+#endif
+ 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) {
+#ifndef __ANDROID_APEX__
+ c2interface = GetFilterWrapper()->maybeWrapInterface(c2interface);
+#endif
+ 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) {
+#ifndef __ANDROID_APEX__
+ c2component = GetFilterWrapper()->maybeWrapComponent(c2component);
+#endif
+ 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();
+}
+
+// Methods from ::android::hardware::media::c2::V1_2::IComponentStore
+Return<void> ComponentStore::createComponent_1_2(
+ const hidl_string& name,
+ const sp<IComponentListener>& listener,
+ const sp<IClientManager>& pool,
+ createComponent_1_2_cb _hidl_cb) {
+
+ sp<Component> component;
+ std::shared_ptr<C2Component> c2component;
+ Status status = static_cast<Status>(
+ mStore->createComponent(name, &c2component));
+
+ if (status == Status::OK) {
+#ifndef __ANDROID_APEX__
+ c2component = GetFilterWrapper()->maybeWrapComponent(c2component);
+#endif
+ 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_2
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/codec2/hidl/1.2/utils/Configurable.cpp b/media/codec2/hidl/1.2/utils/Configurable.cpp
new file mode 100644
index 0000000..243870e
--- /dev/null
+++ b/media/codec2/hidl/1.2/utils/Configurable.cpp
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <codec2/hidl/1.2/Configurable.h>
diff --git a/media/codec2/hidl/1.2/utils/InputBufferManager.cpp b/media/codec2/hidl/1.2/utils/InputBufferManager.cpp
new file mode 100644
index 0000000..1120075
--- /dev/null
+++ b/media/codec2/hidl/1.2/utils/InputBufferManager.cpp
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <codec2/hidl/1.2/InputBufferManager.h>
diff --git a/media/codec2/hidl/1.2/utils/InputSurface.cpp b/media/codec2/hidl/1.2/utils/InputSurface.cpp
new file mode 100644
index 0000000..7c4d28b
--- /dev/null
+++ b/media/codec2/hidl/1.2/utils/InputSurface.cpp
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <codec2/hidl/1.2/InputSurface.h>
diff --git a/media/codec2/hidl/1.2/utils/InputSurfaceConnection.cpp b/media/codec2/hidl/1.2/utils/InputSurfaceConnection.cpp
new file mode 100644
index 0000000..1bd58c2
--- /dev/null
+++ b/media/codec2/hidl/1.2/utils/InputSurfaceConnection.cpp
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <codec2/hidl/1.2/InputSurfaceConnection.h>
diff --git a/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/Component.h b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/Component.h
new file mode 100644
index 0000000..7937664
--- /dev/null
+++ b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/Component.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CODEC2_HIDL_V1_2_UTILS_COMPONENT_H
+#define CODEC2_HIDL_V1_2_UTILS_COMPONENT_H
+
+#include <android/hardware/media/bufferpool/2.0/IClientManager.h>
+#include <android/hardware/media/c2/1.2/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.2/IComponentStore.h>
+#include <android/hardware/media/c2/1.0/IInputSink.h>
+#include <codec2/hidl/1.2/ComponentInterface.h>
+#include <codec2/hidl/1.2/Configurable.h>
+#include <codec2/hidl/1.2/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_2 {
+
+using ::android::hardware::media::c2::V1_2::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;
+ virtual Return<Status> setOutputSurfaceWithSyncObj(
+ uint64_t blockPoolId,
+ const sp<HGraphicBufferProducer2>& surface,
+ const SurfaceSyncObj& syncObject) 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_2::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_2
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // CODEC2_HIDL_V1_2_UTILS_COMPONENT_H
diff --git a/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/ComponentInterface.h b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/ComponentInterface.h
new file mode 100644
index 0000000..09d9f93
--- /dev/null
+++ b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/ComponentInterface.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CODEC2_HIDL_V1_2_UTILS_COMPONENT_INTERFACE_H
+#define CODEC2_HIDL_V1_2_UTILS_COMPONENT_INTERFACE_H
+
+#include <codec2/hidl/1.0/ComponentInterface.h>
+#include <codec2/hidl/1.2/types.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_2 {
+namespace utils {
+
+using ::android::hardware::media::c2::V1_0::utils::ComponentInterface;
+
+} // namespace utils
+} // namespace V1_2
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // CODEC2_HIDL_V1_2_UTILS_COMPONENT_INTERFACE_H
diff --git a/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/ComponentStore.h b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/ComponentStore.h
new file mode 100644
index 0000000..e95a651
--- /dev/null
+++ b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/ComponentStore.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CODEC2_HIDL_V1_2_UTILS_COMPONENT_STORE_H
+#define CODEC2_HIDL_V1_2_UTILS_COMPONENT_STORE_H
+
+#include <codec2/hidl/1.2/Component.h>
+#include <codec2/hidl/1.2/ComponentInterface.h>
+#include <codec2/hidl/1.2/Configurable.h>
+#include <codec2/hidl/1.2/types.h>
+
+#include <android/hardware/media/bufferpool/2.0/IClientManager.h>
+#include <android/hardware/media/c2/1.2/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 {
+class FilterWrapper;
+
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_2 {
+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;
+
+ static std::shared_ptr<FilterWrapper> GetFilterWrapper();
+
+ // 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;
+
+ // Methods from ::android::hardware::media::c2::V1_2::IComponentStore.
+ virtual Return<void> createComponent_1_2(
+ const hidl_string& name,
+ const sp<IComponentListener>& listener,
+ const sp<IClientManager>& pool,
+ createComponent_1_2_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_2
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // CODEC2_HIDL_V1_2_UTILS_COMPONENT_STORE_H
diff --git a/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/Configurable.h b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/Configurable.h
new file mode 100644
index 0000000..2efad31
--- /dev/null
+++ b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/Configurable.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CODEC2_HIDL_V1_2_UTILS_CONFIGURABLE_H
+#define CODEC2_HIDL_V1_2_UTILS_CONFIGURABLE_H
+
+#include <codec2/hidl/1.0/Configurable.h>
+#include <codec2/hidl/1.2/types.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_2 {
+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_2
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // CODEC2_HIDL_V1_2_UTILS_CONFIGURABLE_H
diff --git a/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/InputBufferManager.h b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/InputBufferManager.h
new file mode 100644
index 0000000..e4a5db4
--- /dev/null
+++ b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/InputBufferManager.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CODEC2_HIDL_V1_2_UTILS_INPUT_BUFFER_MANAGER_H
+#define CODEC2_HIDL_V1_2_UTILS_INPUT_BUFFER_MANAGER_H
+
+#include <codec2/hidl/1.0/InputBufferManager.h>
+#include <codec2/hidl/1.2/types.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_2 {
+namespace utils {
+
+using ::android::hardware::media::c2::V1_0::utils::InputBufferManager;
+
+} // namespace utils
+} // namespace V1_2
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // CODEC2_HIDL_V1_2_UTILS_INPUT_BUFFER_MANAGER_H
diff --git a/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/InputSurface.h b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/InputSurface.h
new file mode 100644
index 0000000..3fae86b
--- /dev/null
+++ b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/InputSurface.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CODEC2_HIDL_V1_2_UTILS_INPUT_SURFACE_H
+#define CODEC2_HIDL_V1_2_UTILS_INPUT_SURFACE_H
+
+#include <codec2/hidl/1.0/InputSurface.h>
+#include <codec2/hidl/1.2/types.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_2 {
+namespace utils {
+
+using ::android::hardware::media::c2::V1_0::utils::InputSurface;
+
+} // namespace utils
+} // namespace V1_2
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // CODEC2_HIDL_V1_2_UTILS_INPUT_SURFACE_H
diff --git a/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/InputSurfaceConnection.h b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/InputSurfaceConnection.h
new file mode 100644
index 0000000..13a8a61
--- /dev/null
+++ b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/InputSurfaceConnection.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CODEC2_HIDL_V1_2_UTILS_INPUT_SURFACE_CONNECTION_H
+#define CODEC2_HIDL_V1_2_UTILS_INPUT_SURFACE_CONNECTION_H
+
+#include <codec2/hidl/1.0/InputSurfaceConnection.h>
+#include <codec2/hidl/1.2/types.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_2 {
+namespace utils {
+
+using ::android::hardware::media::c2::V1_0::utils::InputSurfaceConnection;
+
+} // namespace utils
+} // namespace V1_2
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // CODEC2_HIDL_V1_2_UTILS_INPUT_SURFACE_CONNECTION_H
diff --git a/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/types.h b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/types.h
new file mode 100644
index 0000000..d3180b0
--- /dev/null
+++ b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/types.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CODEC2_HIDL_V1_2_UTILS_TYPES_H
+#define CODEC2_HIDL_V1_2_UTILS_TYPES_H
+
+#include <android/hardware/media/c2/1.2/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.2/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>
+#include <android/hardware/media/c2/1.2/types.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_2 {
+
+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_2::SurfaceSyncObj;
+
+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_2
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // CODEC2_HIDL_V1_2_UTILS_TYPES_H
diff --git a/media/codec2/hidl/1.2/utils/types.cpp b/media/codec2/hidl/1.2/utils/types.cpp
new file mode 100644
index 0000000..9e0a08b
--- /dev/null
+++ b/media/codec2/hidl/1.2/utils/types.cpp
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <codec2/hidl/1.2/types.h>
diff --git a/media/codec2/hidl/client/Android.bp b/media/codec2/hidl/client/Android.bp
index 5a34c30..0e52813 100644
--- a/media/codec2/hidl/client/Android.bp
+++ b/media/codec2/hidl/client/Android.bp
@@ -12,6 +12,11 @@
srcs: [
"client.cpp",
+ "output.cpp",
+ ],
+
+ header_libs: [
+ "libcodec2_internal", // private
],
shared_libs: [
@@ -19,11 +24,13 @@
"android.hardware.media.bufferpool@2.0",
"android.hardware.media.c2@1.0",
"android.hardware.media.c2@1.1",
+ "android.hardware.media.c2@1.2",
"libbase",
"libbinder",
"libcodec2",
"libcodec2_hidl_client@1.0",
"libcodec2_hidl_client@1.1",
+ "libcodec2_hidl_client@1.2",
"libcodec2_vndk",
"libcutils",
"libgui",
@@ -41,9 +48,11 @@
export_shared_lib_headers: [
"android.hardware.media.c2@1.0",
"android.hardware.media.c2@1.1",
+ "android.hardware.media.c2@1.2",
"libcodec2",
"libcodec2_hidl_client@1.0",
"libcodec2_hidl_client@1.1",
+ "libcodec2_hidl_client@1.2",
"libcodec2_vndk",
],
diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp
index 4650672..71857e0 100644
--- a/media/codec2/hidl/client/client.cpp
+++ b/media/codec2/hidl/client/client.cpp
@@ -33,16 +33,17 @@
#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 <codec2/hidl/1.2/types.h>
+#include <codec2/hidl/output.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>
#include <limits>
@@ -73,6 +74,7 @@
V2_0::utils::B2HGraphicBufferProducer;
using H2BGraphicBufferProducer2 = ::android::hardware::graphics::bufferqueue::
V2_0::utils::H2BGraphicBufferProducer;
+using ::android::hardware::media::c2::V1_2::SurfaceSyncObj;
namespace /* unnamed */ {
@@ -592,9 +594,9 @@
// Codec2Client::Component::OutputBufferQueue
struct Codec2Client::Component::OutputBufferQueue :
- hardware::media::c2::V1_1::utils::OutputBufferQueue {
+ hardware::media::c2::OutputBufferQueue {
OutputBufferQueue()
- : hardware::media::c2::V1_1::utils::OutputBufferQueue() {
+ : hardware::media::c2::OutputBufferQueue() {
}
};
@@ -612,6 +614,7 @@
},
mBase1_0{base},
mBase1_1{Base1_1::castFrom(base)},
+ mBase1_2{Base1_2::castFrom(base)},
mServiceIndex{serviceIndex} {
Return<sp<IClientManager>> transResult = base->getPoolClientManager();
if (!transResult.isOk()) {
@@ -633,6 +636,10 @@
return mBase1_1;
}
+sp<Codec2Client::Base1_2> const& Codec2Client::getBase1_2() const {
+ return mBase1_2;
+}
+
std::string const& Codec2Client::getServiceName() const {
return GetServiceNames()[mServiceIndex];
}
@@ -645,8 +652,9 @@
c2_status_t status;
sp<Component::HidlListener> hidlListener = new Component::HidlListener{};
hidlListener->base = listener;
- Return<void> transStatus = mBase1_1 ?
- mBase1_1->createComponent_1_1(
+ Return<void> transStatus;
+ if (mBase1_2) {
+ transStatus = mBase1_2->createComponent_1_2(
name,
hidlListener,
ClientManager::getInstance(),
@@ -659,8 +667,25 @@
}
*component = std::make_shared<Codec2Client::Component>(c);
hidlListener->component = *component;
- }) :
- mBase1_0->createComponent(
+ });
+ }
+ else if (mBase1_1) {
+ transStatus = mBase1_1->createComponent_1_1(
+ name,
+ hidlListener,
+ ClientManager::getInstance(),
+ [&status, component, hidlListener](
+ Status s,
+ const sp<IComponent>& c) {
+ status = static_cast<c2_status_t>(s);
+ if (status != C2_OK) {
+ return;
+ }
+ *component = std::make_shared<Codec2Client::Component>(c);
+ hidlListener->component = *component;
+ });
+ } else if (mBase1_0) { // ver1_0
+ transStatus = mBase1_0->createComponent(
name,
hidlListener,
ClientManager::getInstance(),
@@ -674,6 +699,9 @@
*component = std::make_shared<Codec2Client::Component>(c);
hidlListener->component = *component;
});
+ } else {
+ status = C2_CORRUPTED;
+ }
if (!transStatus.isOk()) {
LOG(ERROR) << "createComponent(" << name.c_str()
<< ") -- transaction failed.";
@@ -1008,6 +1036,8 @@
std::scoped_lock lock{key2IndexMutex};
key2Index[key] = index; // update last known client index
return C2_OK;
+ } else if (status == C2_NO_MEMORY) {
+ return C2_NO_MEMORY;
} else if (status == C2_TRANSACTION_FAILED) {
LOG(WARNING) << "\"" << key << "\" failed for service \""
<< client->getName()
@@ -1028,24 +1058,23 @@
return status; // return the last status from a valid client
}
-std::shared_ptr<Codec2Client::Component>
- Codec2Client::CreateComponentByName(
+c2_status_t Codec2Client::CreateComponentByName(
const char* componentName,
const std::shared_ptr<Listener>& listener,
+ std::shared_ptr<Component>* component,
std::shared_ptr<Codec2Client>* owner,
size_t numberOfAttempts) {
std::string key{"create:"};
key.append(componentName);
- std::shared_ptr<Component> component;
c2_status_t status = ForAllServices(
key,
numberOfAttempts,
- [owner, &component, componentName, &listener](
+ [owner, component, componentName, &listener](
const std::shared_ptr<Codec2Client> &client)
-> c2_status_t {
c2_status_t status = client->createComponent(componentName,
listener,
- &component);
+ component);
if (status == C2_OK) {
if (owner) {
*owner = client;
@@ -1064,7 +1093,7 @@
<< "\" from all known services. "
"Last returned status = " << status << ".";
}
- return component;
+ return status;
}
std::shared_ptr<Codec2Client::Interface>
@@ -1192,6 +1221,7 @@
},
mBase1_0{base},
mBase1_1{Base1_1::castFrom(base)},
+ mBase1_2{Base1_2::castFrom(base)},
mBufferPoolSender{std::make_unique<BufferPoolSender>()},
mOutputBufferQueue{std::make_unique<OutputBufferQueue>()} {
}
@@ -1214,6 +1244,30 @@
},
mBase1_0{base},
mBase1_1{base},
+ mBase1_2{Base1_2::castFrom(base)},
+ mBufferPoolSender{std::make_unique<BufferPoolSender>()},
+ mOutputBufferQueue{std::make_unique<OutputBufferQueue>()} {
+}
+
+Codec2Client::Component::Component(const sp<Base1_2>& 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},
+ mBase1_2{base},
mBufferPoolSender{std::make_unique<BufferPoolSender>()},
mOutputBufferQueue{std::make_unique<OutputBufferQueue>()} {
}
@@ -1428,7 +1482,8 @@
c2_status_t Codec2Client::Component::setOutputSurface(
C2BlockPool::local_id_t blockPoolId,
const sp<IGraphicBufferProducer>& surface,
- uint32_t generation) {
+ uint32_t generation,
+ int maxDequeueCount) {
uint64_t bqId = 0;
sp<IGraphicBufferProducer> nullIgbp;
sp<HGraphicBufferProducer2> nullHgbp;
@@ -1439,21 +1494,29 @@
igbp = new B2HGraphicBufferProducer2(surface);
}
+ std::shared_ptr<SurfaceSyncObj> syncObj;
+
if (!surface) {
- mOutputBufferQueue->configure(nullIgbp, generation, 0);
+ mOutputBufferQueue->configure(nullIgbp, generation, 0, maxDequeueCount, nullptr);
} 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, maxDequeueCount, nullptr);
} else {
- mOutputBufferQueue->configure(surface, generation, bqId);
+ mOutputBufferQueue->configure(surface, generation, bqId, maxDequeueCount, mBase1_2 ?
+ &syncObj : nullptr);
}
- ALOGD("generation remote change %u", generation);
+ ALOGD("surface generation remote change %u HAL ver: %s",
+ generation, syncObj ? "1.2" : "1.0");
- Return<Status> transStatus = mBase1_0->setOutputSurface(
- static_cast<uint64_t>(blockPoolId),
- bqId == 0 ? nullHgbp : igbp);
+ Return<Status> transStatus = syncObj ?
+ mBase1_2->setOutputSurfaceWithSyncObj(
+ static_cast<uint64_t>(blockPoolId),
+ bqId == 0 ? nullHgbp : igbp, *syncObj) :
+ mBase1_0->setOutputSurface(
+ static_cast<uint64_t>(blockPoolId),
+ bqId == 0 ? nullHgbp : igbp);
if (!transStatus.isOk()) {
LOG(ERROR) << "setOutputSurface -- transaction failed.";
return C2_TRANSACTION_FAILED;
@@ -1463,6 +1526,7 @@
if (status != C2_OK) {
LOG(DEBUG) << "setOutputSurface -- call failed: " << status << ".";
}
+ ALOGD("Surface configure completed");
return status;
}
@@ -1473,6 +1537,11 @@
return mOutputBufferQueue->outputBuffer(block, input, output);
}
+void Codec2Client::Component::setOutputSurfaceMaxDequeueCount(
+ int maxDequeueCount) {
+ mOutputBufferQueue->updateMaxDequeueBufferCount(maxDequeueCount);
+}
+
c2_status_t Codec2Client::Component::connectToInputSurface(
const std::shared_ptr<InputSurface>& inputSurface,
std::shared_ptr<InputSurfaceConnection>* connection) {
diff --git a/media/codec2/hidl/client/include/codec2/hidl/client.h b/media/codec2/hidl/client/include/codec2/hidl/client.h
index ffd194a..347e58a 100644
--- a/media/codec2/hidl/client/include/codec2/hidl/client.h
+++ b/media/codec2/hidl/client/include/codec2/hidl/client.h
@@ -78,6 +78,11 @@
struct IComponentStore;
} // namespace android::hardware::media::c2::V1_1
+namespace android::hardware::media::c2::V1_2 {
+struct IComponent;
+struct IComponentStore;
+} // namespace android::hardware::media::c2::V1_2
+
namespace android::hardware::media::bufferpool::V2_0 {
struct IClientManager;
} // namespace android::hardware::media::bufferpool::V2_0
@@ -137,6 +142,7 @@
typedef ::android::hardware::media::c2::V1_0::IComponentStore Base1_0;
typedef ::android::hardware::media::c2::V1_1::IComponentStore Base1_1;
+ typedef ::android::hardware::media::c2::V1_2::IComponentStore Base1_2;
typedef Base1_0 Base;
struct Listener;
@@ -156,6 +162,7 @@
sp<Base> const& getBase() const;
sp<Base1_0> const& getBase1_0() const;
sp<Base1_1> const& getBase1_1() const;
+ sp<Base1_2> const& getBase1_2() const;
std::string const& getServiceName() const;
@@ -200,9 +207,10 @@
// Try to create a component with a given name from all known
// IComponentStore services. numberOfAttempts determines the number of times
// to retry the HIDL call if the transaction fails.
- static std::shared_ptr<Component> CreateComponentByName(
+ static c2_status_t CreateComponentByName(
char const* componentName,
std::shared_ptr<Listener> const& listener,
+ std::shared_ptr<Component>* component,
std::shared_ptr<Codec2Client>* owner = nullptr,
size_t numberOfAttempts = 10);
@@ -227,6 +235,7 @@
protected:
sp<Base1_0> mBase1_0;
sp<Base1_1> mBase1_1;
+ sp<Base1_2> mBase1_2;
// 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
@@ -317,6 +326,7 @@
typedef ::android::hardware::media::c2::V1_0::IComponent Base1_0;
typedef ::android::hardware::media::c2::V1_1::IComponent Base1_1;
+ typedef ::android::hardware::media::c2::V1_2::IComponent Base1_2;
typedef Base1_0 Base;
c2_status_t createBlockPool(
@@ -374,7 +384,8 @@
c2_status_t setOutputSurface(
C2BlockPool::local_id_t blockPoolId,
const sp<IGraphicBufferProducer>& surface,
- uint32_t generation);
+ uint32_t generation,
+ int maxDequeueBufferCount);
// Extract a slot number from of the block, then call
// IGraphicBufferProducer::queueBuffer().
@@ -397,6 +408,9 @@
const QueueBufferInput& input,
QueueBufferOutput* output);
+ // Set max dequeue count for output surface.
+ void setOutputSurfaceMaxDequeueCount(int maxDequeueCount);
+
// Connect to a given InputSurface.
c2_status_t connectToInputSurface(
const std::shared_ptr<InputSurface>& inputSurface,
@@ -412,12 +426,14 @@
// base cannot be null.
Component(const sp<Base>& base);
Component(const sp<Base1_1>& base);
+ Component(const sp<Base1_2>& base);
~Component();
protected:
sp<Base1_0> mBase1_0;
sp<Base1_1> mBase1_1;
+ sp<Base1_2> mBase1_2;
struct BufferPoolSender;
std::unique_ptr<BufferPoolSender> mBufferPoolSender;
diff --git a/media/codec2/hidl/client/include/codec2/hidl/output.h b/media/codec2/hidl/client/include/codec2/hidl/output.h
new file mode 100644
index 0000000..877148a
--- /dev/null
+++ b/media/codec2/hidl/client/include/codec2/hidl/output.h
@@ -0,0 +1,93 @@
+/*
+ * 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 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 <codec2/hidl/1.2/types.h>
+#include <C2Work.h>
+
+struct C2_HIDE _C2BlockPoolData;
+class C2SurfaceSyncMemory;
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+
+
+// BufferQueue-Based Block Operations
+// ==================================
+
+// Manage BufferQueue and graphic blocks for both component and codec.
+// Manage graphic blocks ownership consistently during surface change.
+struct OutputBufferQueue {
+
+ OutputBufferQueue();
+
+ ~OutputBufferQueue();
+
+ // Configure a new surface to render graphic blocks.
+ // Graphic blocks from older surface will be migrated to new surface.
+ bool configure(const sp<IGraphicBufferProducer>& igbp,
+ uint32_t generation,
+ uint64_t bqId,
+ int maxDequeueBufferCount,
+ std::shared_ptr<V1_2::SurfaceSyncObj> *syncObj);
+
+ // Render a graphic block to current surface.
+ status_t outputBuffer(
+ const C2ConstGraphicBlock& block,
+ const BnGraphicBufferProducer::QueueBufferInput& input,
+ BnGraphicBufferProducer::QueueBufferOutput* output);
+
+ // Call holdBufferQueueBlock() on output blocks in the given workList.
+ // The OutputBufferQueue will take the ownership of output blocks.
+ //
+ // Note: This function should be called after WorkBundle has been received
+ // from another process.
+ void holdBufferQueueBlocks(
+ const std::list<std::unique_ptr<C2Work>>& workList);
+
+ // Update # of max dequeue buffer from BQ. If # of max dequeued buffer is shared
+ // via shared memory between HAL and framework, Update # of max dequeued buffer
+ // and synchronize.
+ void updateMaxDequeueBufferCount(int maxDequeueBufferCount);
+
+private:
+
+ std::mutex mMutex;
+ sp<IGraphicBufferProducer> mIgbp;
+ uint32_t mGeneration;
+ uint64_t mBqId;
+ int32_t mMaxDequeueBufferCount;
+ 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];
+ std::shared_ptr<C2SurfaceSyncMemory> mSyncMem;
+
+ bool registerBuffer(const C2ConstGraphicBlock& block);
+};
+
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // CODEC2_HIDL_V1_0_UTILS_OUTPUT_BUFFER_QUEUE
diff --git a/media/codec2/hidl/client/output.cpp b/media/codec2/hidl/client/output.cpp
new file mode 100644
index 0000000..8cd4934
--- /dev/null
+++ b/media/codec2/hidl/client/output.cpp
@@ -0,0 +1,480 @@
+/*
+ * 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/output.h>
+#include <cutils/ashmem.h>
+#include <gui/bufferqueue/2.0/B2HGraphicBufferProducer.h>
+#include <sys/mman.h>
+
+#include <C2AllocatorGralloc.h>
+#include <C2BlockInternal.h>
+#include <C2Buffer.h>
+#include <C2PlatformSupport.h>
+#include <C2SurfaceSyncObj.h>
+
+#include <iomanip>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+
+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,
+ std::shared_ptr<C2SurfaceSyncMemory> syncMem) {
+ 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();
+
+ C2SyncVariables *syncVar = syncMem ? syncMem->mem() : nullptr;
+ status_t result = OK;
+ if (syncVar) {
+ syncVar->lock();
+ if (!syncVar->isDequeueableLocked() ||
+ syncVar->getSyncStatusLocked() == C2SyncVariables::STATUS_SWITCHING) {
+ syncVar->unlock();
+ LOG(WARNING) << "attachToBufferQueue -- attachBuffer failed: "
+ "status = " << INVALID_OPERATION << ".";
+ return INVALID_OPERATION;
+ }
+ result = igbp->attachBuffer(bqSlot, graphicBuffer);
+ if (result == OK) {
+ syncVar->notifyDequeuedLocked();
+ }
+ syncVar->unlock();
+ } else {
+ 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,
+ int maxDequeueBufferCount,
+ std::shared_ptr<V1_2::SurfaceSyncObj> *syncObj) {
+ uint64_t consumerUsage = 0;
+ if (igbp->getConsumerUsage(&consumerUsage) != OK) {
+ ALOGW("failed to get consumer usage");
+ }
+
+ // TODO : Abstract creation process into C2SurfaceSyncMemory class.
+ // use C2LinearBlock instead ashmem.
+ std::shared_ptr<C2SurfaceSyncMemory> syncMem;
+ if (syncObj && igbp) {
+ bool mapped = false;
+ int memFd = ashmem_create_region("C2SurfaceMem", sizeof(C2SyncVariables));
+ size_t memSize = memFd < 0 ? 0 : ashmem_get_size_region(memFd);
+ if (memSize > 0) {
+ syncMem = C2SurfaceSyncMemory::Create(memFd, memSize);
+ if (syncMem) {
+ mapped = true;
+ *syncObj = std::make_shared<V1_2::SurfaceSyncObj>();
+ (*syncObj)->syncMemory = syncMem->handle();
+ (*syncObj)->bqId = bqId;
+ (*syncObj)->generationId = generation;
+ (*syncObj)->consumerUsage = consumerUsage;
+ ALOGD("C2SurfaceSyncMemory created %zu(%zu)", sizeof(C2SyncVariables), memSize);
+ }
+ }
+ if (!mapped) {
+ if (memFd >= 0) {
+ ::close(memFd);
+ }
+ ALOGW("SurfaceSyncObj creation failure");
+ }
+ }
+
+ 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) {
+ // case of old BlockPool destruction
+ C2SyncVariables *var = mSyncMem ? mSyncMem->mem() : nullptr;
+ if (syncObj && var) {
+ *syncObj = std::make_shared<V1_2::SurfaceSyncObj>();
+ (*syncObj)->bqId = bqId;
+ (*syncObj)->syncMemory = mSyncMem->handle();
+ (*syncObj)->generationId = generation;
+ (*syncObj)->consumerUsage = consumerUsage;
+ mMaxDequeueBufferCount = maxDequeueBufferCount;
+ var->lock();
+ var->setSyncStatusLocked(C2SyncVariables::STATUS_INIT);
+ var->setInitialDequeueCountLocked(mMaxDequeueBufferCount, 0);
+ var->unlock();
+ }
+ return false;
+ }
+ std::shared_ptr<C2SurfaceSyncMemory> oldMem = mSyncMem;
+ C2SyncVariables *oldSync = mSyncMem ? mSyncMem->mem() : nullptr;
+ if (oldSync) {
+ oldSync->lock();
+ oldSync->setSyncStatusLocked(C2SyncVariables::STATUS_SWITCHING);
+ oldSync->unlock();
+ }
+ mSyncMem.reset();
+ if (syncMem) {
+ mSyncMem = syncMem;
+ }
+ C2SyncVariables *newSync = mSyncMem ? mSyncMem->mem() : nullptr;
+
+ mIgbp = igbp;
+ mGeneration = generation;
+ mBqId = bqId;
+ mOwner = std::make_shared<int>(0);
+ mMaxDequeueBufferCount = maxDequeueBufferCount;
+ 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;
+
+ // Update buffer's generation and usage.
+ if ((mBuffers[i]->getUsage() & consumerUsage) != consumerUsage) {
+ mBuffers[i] = new GraphicBuffer(
+ mBuffers[i]->handle, GraphicBuffer::CLONE_HANDLE,
+ mBuffers[i]->width, mBuffers[i]->height,
+ mBuffers[i]->format, mBuffers[i]->layerCount,
+ mBuffers[i]->getUsage() | consumerUsage,
+ mBuffers[i]->stride);
+ if (mBuffers[i]->initCheck() != OK) {
+ ALOGW("%s() failed to update usage, original usage=%" PRIx64
+ ", consumer usage=%" PRIx64,
+ __func__, mBuffers[i]->getUsage(), consumerUsage);
+ continue;
+ }
+ }
+ mBuffers[i]->setGenerationNumber(generation);
+
+ status_t result = igbp->attachBuffer(&bqSlot, mBuffers[i]);
+ if (result != OK) {
+ continue;
+ }
+ bool attach =
+ _C2BlockFactory::EndAttachBlockToBufferQueue(
+ data, mOwner, getHgbp(mIgbp), mSyncMem,
+ 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];
+ }
+ if (newSync) {
+ newSync->lock();
+ newSync->setInitialDequeueCountLocked(mMaxDequeueBufferCount, success);
+ newSync->unlock();
+ }
+ }
+ 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), mSyncMem);
+ 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 = V1_0::utils::displayBufferQueueBlock(block);
+ if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot) ||
+ bqId == 0) {
+ // Block not from bufferqueue -- it must be attached before queuing.
+
+ std::shared_ptr<C2SurfaceSyncMemory> syncMem;
+ mMutex.lock();
+ sp<IGraphicBufferProducer> outputIgbp = mIgbp;
+ uint32_t outputGeneration = mGeneration;
+ syncMem = mSyncMem;
+ mMutex.unlock();
+
+ status_t status = attachToBufferQueue(
+ block, outputIgbp, outputGeneration, &bqSlot, syncMem);
+
+ if (status != OK) {
+ LOG(WARNING) << "outputBuffer -- attaching failed.";
+ return INVALID_OPERATION;
+ }
+
+ auto syncVar = syncMem ? syncMem->mem() : nullptr;
+ if(syncVar) {
+ syncVar->lock();
+ status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
+ input, output);
+ if (status == OK) {
+ syncVar->notifyQueuedLocked();
+ }
+ syncVar->unlock();
+ } else {
+ 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;
+ }
+
+ std::shared_ptr<C2SurfaceSyncMemory> syncMem;
+ mMutex.lock();
+ sp<IGraphicBufferProducer> outputIgbp = mIgbp;
+ uint32_t outputGeneration = mGeneration;
+ uint64_t outputBqId = mBqId;
+ syncMem = mSyncMem;
+ 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;
+ }
+
+ auto syncVar = syncMem ? syncMem->mem() : nullptr;
+ status_t status = OK;
+ if (syncVar) {
+ syncVar->lock();
+ status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
+ input, output);
+ if (status == OK) {
+ syncVar->notifyQueuedLocked();
+ }
+ syncVar->unlock();
+ } else {
+ 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));
+}
+
+void OutputBufferQueue::updateMaxDequeueBufferCount(int maxDequeueBufferCount) {
+ mMutex.lock();
+ mMaxDequeueBufferCount = maxDequeueBufferCount;
+ auto syncVar = mSyncMem ? mSyncMem->mem() : nullptr;
+ if (syncVar) {
+ syncVar->lock();
+ syncVar->updateMaxDequeueCountLocked(maxDequeueBufferCount);
+ syncVar->unlock();
+ }
+ mMutex.unlock();
+ ALOGD("set max dequeue count %d from update", maxDequeueBufferCount);
+}
+
+} // namespace c2
+} // namespace media
+} // namespace hardware
+} // namespace android
+
diff --git a/media/codec2/hidl/plugin/DefaultFilterPlugin.cpp b/media/codec2/hidl/plugin/DefaultFilterPlugin.cpp
index cd1bcb0..b26e74b 100644
--- a/media/codec2/hidl/plugin/DefaultFilterPlugin.cpp
+++ b/media/codec2/hidl/plugin/DefaultFilterPlugin.cpp
@@ -105,4 +105,13 @@
return mPlugin->isFilteringEnabled(intf);
}
+c2_status_t DefaultFilterPlugin::queryParamsForPreviousComponent(
+ const std::shared_ptr<C2ComponentInterface> &intf,
+ std::vector<std::unique_ptr<C2Param>> *params) {
+ if (mInit != OK) {
+ return C2_NO_INIT;
+ }
+ return mPlugin->queryParamsForPreviousComponent(intf, params);
+}
+
} // namespace android
diff --git a/media/codec2/hidl/plugin/FilterWrapper.cpp b/media/codec2/hidl/plugin/FilterWrapper.cpp
index bed8aeb..70c63f2 100644
--- a/media/codec2/hidl/plugin/FilterWrapper.cpp
+++ b/media/codec2/hidl/plugin/FilterWrapper.cpp
@@ -45,8 +45,9 @@
public:
WrappedDecoderInterface(
std::shared_ptr<C2ComponentInterface> intf,
- std::vector<FilterWrapper::Component> &&filters)
- : mIntf(intf) {
+ std::vector<FilterWrapper::Component> &&filters,
+ std::weak_ptr<FilterWrapper> filterWrapper)
+ : mIntf(intf), mFilterWrapper(filterWrapper) {
takeFilters(std::move(filters));
}
@@ -101,6 +102,13 @@
mTypeToIndexForQuery[type.type()] = i;
}
}
+ for (size_t i = mFilters.size(); i > 0; --i) {
+ if (i == 1) {
+ backPropagateParams_l(mIntf, mFilters[0].intf, C2_MAY_BLOCK);
+ } else {
+ backPropagateParams_l(mFilters[i - 2].intf, mFilters[i - 1].intf, C2_MAY_BLOCK);
+ }
+ }
if (!mFilters.empty()) {
for (uint32_t type : kTypesForLastFilter) {
mTypeToIndexForQuery[type] = mFilters.size() - 1;
@@ -256,6 +264,13 @@
result = err;
}
}
+ for (size_t i = mFilters.size(); i > 0; --i) {
+ if (i == 1) {
+ backPropagateParams_l(mIntf, mFilters[0].intf, mayBlock);
+ } else {
+ backPropagateParams_l(mFilters[i - 2].intf, mFilters[i - 1].intf, mayBlock);
+ }
+ }
return result;
}
@@ -338,6 +353,7 @@
mutable std::mutex mMutex;
std::shared_ptr<C2ComponentInterface> mIntf;
std::vector<FilterWrapper::Component> mFilters;
+ std::weak_ptr<FilterWrapper> mFilterWrapper;
std::map<uint32_t, size_t> mTypeToIndexForQuery;
std::map<uint32_t, size_t> mTypeToIndexForConfig;
@@ -402,6 +418,41 @@
}
return C2_OK;
}
+
+ c2_status_t backPropagateParams_l(
+ const std::shared_ptr<C2ComponentInterface> &curr,
+ const std::shared_ptr<C2ComponentInterface> &next,
+ c2_blocking_t mayBlock) {
+ // NOTE: this implementation is preliminary --- it could change once
+ // we define what parameters needs to be propagated in component chaining.
+ std::shared_ptr<FilterWrapper> filterWrapper = mFilterWrapper.lock();
+ if (!filterWrapper) {
+ LOG(DEBUG) << "WrappedDecoderInterface: FilterWrapper not found";
+ return C2_OK;
+ }
+ std::vector<std::unique_ptr<C2Param>> params;
+ c2_status_t err = filterWrapper->queryParamsForPreviousComponent(next, ¶ms);
+ if (err != C2_OK) {
+ LOG(DEBUG) << "WrappedDecoderInterface: FilterWrapper returned error for "
+ << "queryParamsForPreviousComponent; intf=" << next->getName() << " err=" << err;
+ return C2_OK;
+ }
+ std::vector<C2Param *> configParams;
+ for (size_t i = 0; i < params.size(); ++i) {
+ if (!params[i]) {
+ continue;
+ }
+ configParams.push_back(params[i].get());
+ }
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ curr->config_vb(configParams, mayBlock, &failures);
+ if (err != C2_OK && err != C2_BAD_INDEX) {
+ LOG(DEBUG) << "WrappedDecoderInterface: " << next->getName()
+ << " returned error for config_vb; err=" << err;
+ return err;
+ }
+ return C2_OK;
+ }
};
class WrappedDecoder : public C2Component, public std::enable_shared_from_this<WrappedDecoder> {
@@ -413,7 +464,7 @@
: mComp(comp), mFilters(std::move(filters)), mFilterWrapper(filterWrapper) {
std::vector<FilterWrapper::Component> filtersDup(mFilters);
mIntf = std::make_shared<WrappedDecoderInterface>(
- comp->intf(), std::move(filtersDup));
+ comp->intf(), std::move(filtersDup), filterWrapper);
}
~WrappedDecoder() override = default;
@@ -844,7 +895,8 @@
<< " is not video/image decoder; not wrapping the interface";
return intf;
}
- return std::make_shared<WrappedDecoderInterface>(intf, createFilters());
+ return std::make_shared<WrappedDecoderInterface>(
+ intf, createFilters(), weak_from_this());
}
std::shared_ptr<C2Component> FilterWrapper::maybeWrapComponent(
@@ -917,4 +969,14 @@
return CreateCodec2BlockPool(allocatorId, component, pool);
}
+c2_status_t FilterWrapper::queryParamsForPreviousComponent(
+ const std::shared_ptr<C2ComponentInterface> &intf,
+ std::vector<std::unique_ptr<C2Param>> *params) {
+ if (mInit != OK) {
+ LOG(WARNING) << "queryParamsForPreviousComponent: Wrapper not initialized: ";
+ return C2_NO_INIT;
+ }
+ return mPlugin->queryParamsForPreviousComponent(intf, params);
+}
+
} // namespace android
diff --git a/media/codec2/hidl/plugin/include/codec2/hidl/plugin/FilterPlugin.h b/media/codec2/hidl/plugin/include/codec2/hidl/plugin/FilterPlugin.h
index 6f1f907..a1ac624 100644
--- a/media/codec2/hidl/plugin/include/codec2/hidl/plugin/FilterPlugin.h
+++ b/media/codec2/hidl/plugin/include/codec2/hidl/plugin/FilterPlugin.h
@@ -57,6 +57,15 @@
* current configuration; false if it will be no-op.
*/
virtual bool isFilteringEnabled(const std::shared_ptr<C2ComponentInterface> &intf) = 0;
+
+ /**
+ * Query parameters to |intf|, which the component wants applied to
+ * the previous component in the chain. For example, an image/video filter
+ * may require specific usage or pixel format from the previous component.
+ */
+ virtual c2_status_t queryParamsForPreviousComponent(
+ const std::shared_ptr<C2ComponentInterface> &intf,
+ std::vector<std::unique_ptr<C2Param>> *params) = 0;
};
} // namespace android
diff --git a/media/codec2/hidl/plugin/internal/DefaultFilterPlugin.h b/media/codec2/hidl/plugin/internal/DefaultFilterPlugin.h
index f856324..0aab39f 100644
--- a/media/codec2/hidl/plugin/internal/DefaultFilterPlugin.h
+++ b/media/codec2/hidl/plugin/internal/DefaultFilterPlugin.h
@@ -35,6 +35,9 @@
std::shared_ptr<C2ComponentStore> getStore() override { return mStore; }
bool describe(C2String name, FilterWrapper::Descriptor *desc) override;
bool isFilteringEnabled(const std::shared_ptr<C2ComponentInterface> &intf) override;
+ c2_status_t queryParamsForPreviousComponent(
+ const std::shared_ptr<C2ComponentInterface> &intf,
+ std::vector<std::unique_ptr<C2Param>> *params) override;
private:
status_t mInit;
diff --git a/media/codec2/hidl/plugin/internal/FilterWrapper.h b/media/codec2/hidl/plugin/internal/FilterWrapper.h
index 5ced435..cf2cc30 100644
--- a/media/codec2/hidl/plugin/internal/FilterWrapper.h
+++ b/media/codec2/hidl/plugin/internal/FilterWrapper.h
@@ -43,6 +43,9 @@
virtual std::shared_ptr<C2ComponentStore> getStore() = 0;
virtual bool describe(C2String name, Descriptor *desc) = 0;
virtual bool isFilteringEnabled(const std::shared_ptr<C2ComponentInterface> &intf) = 0;
+ virtual c2_status_t queryParamsForPreviousComponent(
+ const std::shared_ptr<C2ComponentInterface> &intf,
+ std::vector<std::unique_ptr<C2Param>> *params) = 0;
C2_DO_NOT_COPY(Plugin);
};
@@ -78,11 +81,21 @@
*/
bool isFilteringEnabled(const std::shared_ptr<C2ComponentInterface> &intf);
+ /**
+ * Create a C2BlockPool object with |allocatorId| for |component|.
+ */
c2_status_t createBlockPool(
C2PlatformAllocatorStore::id_t allocatorId,
std::shared_ptr<const C2Component> component,
std::shared_ptr<C2BlockPool> *pool);
+ /**
+ * Query parameters that |intf| wants from the previous component.
+ */
+ c2_status_t queryParamsForPreviousComponent(
+ const std::shared_ptr<C2ComponentInterface> &intf,
+ std::vector<std::unique_ptr<C2Param>> *params);
+
private:
status_t mInit;
std::unique_ptr<Plugin> mPlugin;
diff --git a/media/codec2/hidl/plugin/samples/Android.bp b/media/codec2/hidl/plugin/samples/Android.bp
new file mode 100644
index 0000000..32b760d
--- /dev/null
+++ b/media/codec2/hidl/plugin/samples/Android.bp
@@ -0,0 +1,58 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_defaults {
+ name: "sample-codec2-hidl-plugin-defaults",
+
+ srcs: [
+ "SampleFilterPlugin.cpp",
+ ],
+
+ defaults: [
+ "libcodec2-impl-defaults",
+ ],
+
+ header_libs: [
+ "libcodec2_hidl_plugin_headers",
+ "libgui_headers",
+ ],
+
+ shared_libs: [
+ "libEGL",
+ "libGLESv1_CM",
+ "libGLESv2",
+ "libGLESv3",
+ "libbase",
+ "libcodec2",
+ "libcutils",
+ "libprocessgroup",
+ "libsfplugin_ccodec_utils",
+ "libsync",
+ "libui",
+ "libutils",
+ ],
+
+ static_libs: [
+ "librenderfright",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+}
+
+cc_library {
+ name: "sample-codec2-hidl-plugin",
+ vendor: true,
+
+ defaults: [
+ "sample-codec2-hidl-plugin-defaults",
+ ],
+}
diff --git a/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp b/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp
new file mode 100644
index 0000000..7de3503
--- /dev/null
+++ b/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp
@@ -0,0 +1,989 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "SampleFilterPlugin"
+#include <android-base/logging.h>
+
+#include <chrono>
+#include <thread>
+
+#include <codec2/hidl/plugin/FilterPlugin.h>
+
+#include <C2AllocatorGralloc.h>
+#include <C2Config.h>
+#include <C2PlatformSupport.h>
+#include <Codec2Mapper.h>
+#include <util/C2InterfaceHelper.h>
+
+#include <renderengine/RenderEngine.h>
+#include <system/window.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/RefBase.h>
+
+typedef C2StreamParam<C2Info, C2ColorAspectsStruct,
+ kParamIndexColorAspects | C2Param::CoreIndex::IS_REQUEST_FLAG>
+ C2StreamColorAspectsRequestInfo;
+
+namespace android {
+
+using namespace std::literals::chrono_literals;
+
+class SampleToneMappingFilter
+ : public C2Component, public std::enable_shared_from_this<SampleToneMappingFilter> {
+public:
+ class Interface : public C2ComponentInterface {
+ public:
+ static const std::string NAME;
+ static const FilterPlugin_V1::Descriptor DESCRIPTOR;
+
+ explicit Interface(c2_node_id_t id)
+ : mId(id),
+ mReflector(std::make_shared<C2ReflectorHelper>()),
+ mHelper(mReflector) {
+ }
+ ~Interface() override = default;
+ C2String getName() const override { return NAME; }
+ c2_node_id_t getId() const override { return mId; }
+
+ c2_status_t query_vb(
+ const std::vector<C2Param*> &stackParams,
+ const std::vector<C2Param::Index> &heapParamIndices,
+ c2_blocking_t mayBlock,
+ std::vector<std::unique_ptr<C2Param>>* const heapParams) const override {
+ return mHelper.query(stackParams, heapParamIndices, mayBlock, heapParams);
+ }
+ c2_status_t config_vb(
+ const std::vector<C2Param*> ¶ms,
+ c2_blocking_t mayBlock,
+ std::vector<std::unique_ptr<C2SettingResult>>* const failures) override {
+ return mHelper.config(params, mayBlock, failures);
+ }
+ c2_status_t querySupportedParams_nb(
+ std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const override {
+ return mHelper.querySupportedParams(params);
+ }
+ c2_status_t querySupportedValues_vb(
+ std::vector<C2FieldSupportedValuesQuery> &fields,
+ c2_blocking_t mayBlock) const override {
+ return mHelper.querySupportedValues(fields, mayBlock);
+ }
+ c2_status_t createTunnel_sm(c2_node_id_t) override { return C2_OMITTED; }
+ c2_status_t releaseTunnel_sm(c2_node_id_t) override { return C2_OMITTED; }
+
+ uint32_t getDataSpace() {
+ Helper::Lock lock = mHelper.lock();
+ uint32_t dataspace = HAL_DATASPACE_UNKNOWN;
+ C2Mapper::map(
+ mHelper.mInputColorAspectInfo->range,
+ mHelper.mInputColorAspectInfo->primaries,
+ mHelper.mInputColorAspectInfo->matrix,
+ mHelper.mInputColorAspectInfo->transfer,
+ &dataspace);
+ return dataspace;
+ }
+ std::shared_ptr<C2StreamHdrStaticInfo::input> getHdrStaticMetadata() {
+ Helper::Lock lock = mHelper.lock();
+ return mHelper.mInputHdrStaticInfo;
+ }
+ C2BlockPool::local_id_t getPoolId() {
+ Helper::Lock lock = mHelper.lock();
+ return mHelper.mOutputPoolIds->m.values[0];
+ }
+
+ static bool IsFilteringEnabled(const std::shared_ptr<C2ComponentInterface> &intf) {
+ C2StreamColorAspectsRequestInfo::output info(0u);
+ std::vector<std::unique_ptr<C2Param>> heapParams;
+ c2_status_t err = intf->query_vb({&info}, {}, C2_MAY_BLOCK, &heapParams);
+ if (err != C2_OK && err != C2_BAD_INDEX) {
+ LOG(WARNING) << "SampleToneMappingFilter::Interface::IsFilteringEnabled: "
+ << "query failed for " << intf->getName();
+ return false;
+ }
+ return info && info.transfer == C2Color::TRANSFER_170M;
+ }
+
+ static c2_status_t QueryParamsForPreviousComponent(
+ [[maybe_unused]] const std::shared_ptr<C2ComponentInterface> &intf,
+ std::vector<std::unique_ptr<C2Param>> *params) {
+ params->emplace_back(new C2StreamUsageTuning::output(
+ 0u, C2AndroidMemoryUsage::HW_TEXTURE_READ));
+ params->emplace_back(new C2StreamPixelFormatInfo::output(
+ 0u, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED));
+ return C2_OK;
+ }
+ private:
+ const c2_node_id_t mId;
+ std::shared_ptr<C2ReflectorHelper> mReflector;
+ struct Helper : public C2InterfaceHelper {
+ explicit Helper(std::shared_ptr<C2ReflectorHelper> reflector)
+ : C2InterfaceHelper(reflector) {
+ setDerivedInstance(this);
+
+ addParameter(
+ DefineParam(mApiFeatures, C2_PARAMKEY_API_FEATURES)
+ .withConstValue(new C2ApiFeaturesSetting(C2Config::api_feature_t(
+ API_REFLECTION |
+ API_VALUES |
+ API_CURRENT_VALUES |
+ API_DEPENDENCY |
+ API_SAME_INPUT_BUFFER)))
+ .build());
+
+ mName = C2ComponentNameSetting::AllocShared(NAME.size() + 1);
+ strncpy(mName->m.value, NAME.c_str(), NAME.size() + 1);
+ addParameter(
+ DefineParam(mName, C2_PARAMKEY_COMPONENT_NAME)
+ .withConstValue(mName)
+ .build());
+
+ addParameter(
+ DefineParam(mKind, C2_PARAMKEY_COMPONENT_KIND)
+ .withConstValue(new C2ComponentKindSetting(C2Component::KIND_OTHER))
+ .build());
+
+ addParameter(
+ DefineParam(mDomain, C2_PARAMKEY_COMPONENT_DOMAIN)
+ .withConstValue(new C2ComponentDomainSetting(C2Component::DOMAIN_VIDEO))
+ .build());
+
+ addParameter(
+ DefineParam(mInputStreamCount, C2_PARAMKEY_INPUT_STREAM_COUNT)
+ .withConstValue(new C2PortStreamCountTuning::input(1))
+ .build());
+
+ addParameter(
+ DefineParam(mOutputStreamCount, C2_PARAMKEY_OUTPUT_STREAM_COUNT)
+ .withConstValue(new C2PortStreamCountTuning::output(1))
+ .build());
+
+ addParameter(
+ DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
+ .withConstValue(new C2StreamBufferTypeSetting::input(
+ 0u, C2BufferData::GRAPHIC))
+ .build());
+
+ static const std::string kRawMediaType = "video/raw";
+ mInputMediaType = C2PortMediaTypeSetting::input::AllocShared(
+ kRawMediaType.size() + 1);
+ strncpy(mInputMediaType->m.value, kRawMediaType.c_str(), kRawMediaType.size() + 1);
+ addParameter(
+ DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
+ .withConstValue(mInputMediaType)
+ .build());
+
+ addParameter(
+ DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
+ .withConstValue(new C2StreamBufferTypeSetting::output(
+ 0u, C2BufferData::GRAPHIC))
+ .build());
+
+ mOutputMediaType = C2PortMediaTypeSetting::output::AllocShared(
+ kRawMediaType.size() + 1);
+ strncpy(mOutputMediaType->m.value, kRawMediaType.c_str(), kRawMediaType.size() + 1);
+ addParameter(
+ DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
+ .withConstValue(mOutputMediaType)
+ .build());
+
+ addParameter(
+ DefineParam(mActualInputDelay, C2_PARAMKEY_INPUT_DELAY)
+ .withConstValue(new C2PortActualDelayTuning::input(0u))
+ .build());
+
+ addParameter(
+ DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY)
+ .withConstValue(new C2PortActualDelayTuning::output(0u))
+ .build());
+
+ addParameter(
+ DefineParam(mActualPipelineDelay, C2_PARAMKEY_PIPELINE_DELAY)
+ .withConstValue(new C2ActualPipelineDelayTuning(0u))
+ .build());
+
+ C2BlockPool::local_id_t outputPoolIds[1] = { C2BlockPool::BASIC_GRAPHIC };
+ addParameter(
+ DefineParam(mOutputPoolIds, C2_PARAMKEY_OUTPUT_BLOCK_POOLS)
+ .withDefault(C2PortBlockPoolsTuning::output::AllocShared(outputPoolIds))
+ .withFields({ C2F(mOutputPoolIds, m.values[0]).any(),
+ C2F(mOutputPoolIds, m.values).inRange(0, 1) })
+ .withSetter(OutputBlockPoolSetter)
+ .build());
+
+ addParameter(
+ DefineParam(mInputHdrStaticInfo, C2_PARAMKEY_HDR_STATIC_INFO)
+ .withDefault(new C2StreamHdrStaticInfo::input(0u))
+ .withFields({
+ C2F(mInputHdrStaticInfo, mastering.red.x).any(),
+ })
+ .withSetter(HdrStaticInfoSetter)
+ .build());
+
+ addParameter(
+ DefineParam(mOutputHdrStaticInfo, C2_PARAMKEY_HDR_STATIC_INFO)
+ .withConstValue(new C2StreamHdrStaticInfo::output(0u))
+ .build());
+
+ addParameter(
+ DefineParam(mInputColorAspectInfo, C2_PARAMKEY_COLOR_ASPECTS)
+ .withDefault(new C2StreamColorAspectsInfo::input(0u))
+ .withFields({
+ C2F(mInputColorAspectInfo, range).any(),
+ C2F(mInputColorAspectInfo, primaries).any(),
+ C2F(mInputColorAspectInfo, transfer).any(),
+ C2F(mInputColorAspectInfo, matrix).any(),
+ })
+ .withSetter(InputColorAspectsSetter)
+ .build());
+
+ addParameter(
+ DefineParam(
+ mColorAspectRequestInfo,
+ (std::string(C2_PARAMKEY_COLOR_ASPECTS) + ".request").c_str())
+ .withDefault(new C2StreamColorAspectsRequestInfo::output(0u))
+ .withFields({
+ C2F(mColorAspectRequestInfo, range).any(),
+ C2F(mColorAspectRequestInfo, primaries).any(),
+ C2F(mColorAspectRequestInfo, transfer).oneOf({
+ C2Color::TRANSFER_UNSPECIFIED,
+ C2Color::TRANSFER_170M,
+ }),
+ C2F(mColorAspectRequestInfo, matrix).any(),
+ })
+ .withSetter(ColorAspectsRequestSetter)
+ .build());
+
+ addParameter(
+ DefineParam(mOutputColorAspectInfo, C2_PARAMKEY_COLOR_ASPECTS)
+ .withDefault(new C2StreamColorAspectsInfo::output(0u))
+ .withFields({
+ C2F(mOutputColorAspectInfo, range).any(),
+ C2F(mOutputColorAspectInfo, primaries).any(),
+ C2F(mOutputColorAspectInfo, transfer).any(),
+ C2F(mOutputColorAspectInfo, matrix).any(),
+ })
+ .withSetter(OutputColorAspectsSetter,
+ mInputColorAspectInfo,
+ mColorAspectRequestInfo)
+ .build());
+ }
+
+ static C2R OutputBlockPoolSetter(
+ bool mayBlock,
+ C2P<C2PortBlockPoolsTuning::output> &me) {
+ (void)mayBlock, (void)me;
+ return C2R::Ok();
+ }
+
+ static C2R HdrStaticInfoSetter(
+ bool mayBlock,
+ C2P<C2StreamHdrStaticInfo::input> &me) {
+ (void)mayBlock, (void)me;
+ return C2R::Ok();
+ }
+
+ static C2R InputColorAspectsSetter(
+ bool mayBlock,
+ C2P<C2StreamColorAspectsInfo::input> &me) {
+ (void)mayBlock, (void)me;
+ return C2R::Ok();
+ }
+
+ static C2R OutputColorAspectsSetter(
+ bool mayBlock,
+ C2P<C2StreamColorAspectsInfo::output> &me,
+ const C2P<C2StreamColorAspectsInfo::input> &inputColor,
+ const C2P<C2StreamColorAspectsRequestInfo::output> &request) {
+ (void)mayBlock;
+ me.set().range = inputColor.v.range;
+ me.set().primaries = inputColor.v.primaries;
+ me.set().transfer = inputColor.v.transfer;
+ if (request.v.transfer == C2Color::TRANSFER_170M) {
+ me.set().transfer = C2Color::TRANSFER_170M;
+ }
+ me.set().matrix = inputColor.v.matrix;
+ return C2R::Ok();
+ }
+
+ static C2R ColorAspectsRequestSetter(
+ bool mayBlock,
+ C2P<C2StreamColorAspectsRequestInfo::output> &me) {
+ (void)mayBlock;
+ if (me.v.range != C2Color::RANGE_UNSPECIFIED) {
+ me.set().range = C2Color::RANGE_UNSPECIFIED;
+ }
+ if (me.v.primaries != C2Color::PRIMARIES_UNSPECIFIED) {
+ me.set().primaries = C2Color::PRIMARIES_UNSPECIFIED;
+ }
+ if (me.v.transfer != C2Color::TRANSFER_170M) {
+ me.set().transfer = C2Color::TRANSFER_UNSPECIFIED;
+ }
+ if (me.v.matrix != C2Color::MATRIX_UNSPECIFIED) {
+ me.set().matrix = C2Color::MATRIX_UNSPECIFIED;
+ }
+ return C2R::Ok();
+ }
+
+ std::shared_ptr<C2ApiFeaturesSetting> mApiFeatures;
+
+ std::shared_ptr<C2ComponentNameSetting> mName;
+ std::shared_ptr<C2ComponentAliasesSetting> mAliases;
+ std::shared_ptr<C2ComponentKindSetting> mKind;
+ std::shared_ptr<C2ComponentDomainSetting> mDomain;
+
+ std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
+ std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
+ std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
+ std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
+
+ std::shared_ptr<C2PortActualDelayTuning::input> mActualInputDelay;
+ std::shared_ptr<C2PortActualDelayTuning::output> mActualOutputDelay;
+ std::shared_ptr<C2ActualPipelineDelayTuning> mActualPipelineDelay;
+
+ std::shared_ptr<C2PortStreamCountTuning::input> mInputStreamCount;
+ std::shared_ptr<C2PortStreamCountTuning::output> mOutputStreamCount;
+
+ std::shared_ptr<C2PortBlockPoolsTuning::output> mOutputPoolIds;
+
+ std::shared_ptr<C2StreamHdrStaticInfo::input> mInputHdrStaticInfo;
+ std::shared_ptr<C2StreamHdrStaticInfo::output> mOutputHdrStaticInfo;
+ std::shared_ptr<C2StreamColorAspectsInfo::input> mInputColorAspectInfo;
+ std::shared_ptr<C2StreamColorAspectsInfo::output> mOutputColorAspectInfo;
+ std::shared_ptr<C2StreamColorAspectsRequestInfo::output> mColorAspectRequestInfo;
+ } mHelper;
+ };
+
+ explicit SampleToneMappingFilter(c2_node_id_t id)
+ : mIntf(std::make_shared<Interface>(id)) {
+ }
+ ~SampleToneMappingFilter() override {
+ if (mProcessingThread.joinable()) {
+ mProcessingThread.join();
+ }
+ }
+
+ c2_status_t setListener_vb(
+ const std::shared_ptr<Listener> &listener, c2_blocking_t mayBlock) override {
+ std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + 5ms;
+ {
+ std::unique_lock lock(mStateMutex);
+ if (mState == RELEASED) {
+ return C2_BAD_STATE;
+ }
+ if (mState == RUNNING && listener) {
+ return C2_BAD_STATE;
+ }
+ if (mState != STOPPED) {
+ return C2_BAD_STATE;
+ }
+ }
+ std::unique_lock lock(mListenerMutex, std::try_to_lock);
+ if (lock) {
+ mListener = listener;
+ return C2_OK;
+ }
+ if (mayBlock == C2_DONT_BLOCK) {
+ return C2_BLOCKING;
+ }
+ lock.try_lock_until(deadline);
+ if (!lock) {
+ return C2_TIMED_OUT;
+ }
+ mListener = listener;
+ return C2_OK;
+ }
+
+ c2_status_t queue_nb(std::list<std::unique_ptr<C2Work>>* const items) override {
+ if (!items) {
+ return C2_BAD_VALUE;
+ }
+ {
+ std::unique_lock lock(mStateMutex);
+ if (mState != RUNNING) {
+ return C2_BAD_STATE;
+ }
+ }
+ std::unique_lock lock(mQueueMutex);
+ mQueue.splice(mQueue.end(), *items);
+ return C2_OK;
+ }
+
+ c2_status_t announce_nb(const std::vector<C2WorkOutline> &) override { return C2_OMITTED; }
+
+ c2_status_t flush_sm(
+ flush_mode_t mode,
+ std::list<std::unique_ptr<C2Work>>* const flushedWork) override {
+ if (!flushedWork) {
+ return C2_BAD_VALUE;
+ }
+ if (mode == FLUSH_CHAIN) {
+ return C2_BAD_VALUE;
+ }
+ {
+ std::unique_lock lock(mStateMutex);
+ if (mState != RUNNING) {
+ return C2_BAD_STATE;
+ }
+ }
+ {
+ std::unique_lock lock(mQueueMutex);
+ mQueue.swap(*flushedWork);
+ }
+ // NOTE: this component does not have internal state to flush.
+ return C2_OK;
+ }
+
+ c2_status_t drain_nb(drain_mode_t mode) override {
+ if (mode == DRAIN_CHAIN) {
+ return C2_BAD_VALUE;
+ }
+ {
+ std::unique_lock lock(mStateMutex);
+ if (mState != RUNNING) {
+ return C2_BAD_STATE;
+ }
+ }
+ // NOTE: this component does not wait for work items before processing.
+ return C2_OK;
+ }
+
+ c2_status_t start() override {
+ //std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + 500ms;
+ {
+ std::unique_lock lock(mStateMutex);
+ if (mState == STARTING) {
+ return C2_DUPLICATE;
+ }
+ if (mState != STOPPED) {
+ return C2_BAD_STATE;
+ }
+ mState = STARTING;
+ }
+ {
+ std::unique_lock lock(mProcessingMutex);
+ if (!mProcessingThread.joinable()) {
+ mProcessingThread = std::thread([this]() {
+ processLoop(shared_from_this());
+ });
+ }
+ }
+ {
+ std::unique_lock lock(mStateMutex);
+ mState = RUNNING;
+ }
+ return C2_OK;
+ }
+
+ c2_status_t stop() override {
+ //std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + 500ms;
+ {
+ std::unique_lock lock(mStateMutex);
+ if (mState == STOPPING) {
+ return C2_DUPLICATE;
+ }
+ if (mState != RUNNING) {
+ return C2_BAD_STATE;
+ }
+ mState = STOPPING;
+ }
+ {
+ std::unique_lock lock(mQueueMutex);
+ mQueueCondition.notify_all();
+ }
+ {
+ std::unique_lock lock(mProcessingMutex);
+ if (mProcessingThread.joinable()) {
+ mProcessingThread.join();
+ }
+ }
+ {
+ std::unique_lock lock(mStateMutex);
+ mState = STOPPED;
+ }
+ return C2_OK;
+ }
+
+ c2_status_t reset() override {
+ //std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + 500ms;
+ {
+ std::unique_lock lock(mStateMutex);
+ if (mState == RESETTING) {
+ return C2_DUPLICATE;
+ }
+ if (mState == RELEASED) {
+ return C2_BAD_STATE;
+ }
+ mState = RESETTING;
+ }
+ {
+ std::unique_lock lock(mQueueMutex);
+ mQueueCondition.notify_all();
+ }
+ {
+ std::unique_lock lock(mProcessingMutex);
+ if (mProcessingThread.joinable()) {
+ mProcessingThread.join();
+ }
+ }
+ {
+ std::unique_lock lock(mStateMutex);
+ mState = STOPPED;
+ }
+ return C2_OK;
+ }
+
+ c2_status_t release() override {
+ //std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + 500ms;
+ {
+ std::unique_lock lock(mStateMutex);
+ if (mState == RELEASED || mState == RELEASING) {
+ return C2_DUPLICATE;
+ }
+ // TODO: return C2_BAD_STATE if not stopped
+ mState = RELEASING;
+ }
+ {
+ std::unique_lock lock(mQueueMutex);
+ mQueueCondition.notify_all();
+ }
+ {
+ std::unique_lock lock(mProcessingMutex);
+ if (mProcessingThread.joinable()) {
+ mProcessingThread.join();
+ }
+ }
+ {
+ std::unique_lock lock(mStateMutex);
+ mState = RELEASED;
+ }
+ return C2_OK;
+ }
+
+ std::shared_ptr<C2ComponentInterface> intf() override {
+ return mIntf;
+ }
+
+private:
+ void processLoop(std::shared_ptr<SampleToneMappingFilter> thiz) {
+ constexpr float kDefaultMaxLumiance = 500.0;
+ constexpr float kDefaultMaxMasteringLuminance = 1000.0;
+ constexpr float kDefaultMaxContentLuminance = 1000.0;
+ constexpr uint32_t kDstUsage =
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+
+ int32_t workCount = 0;
+ std::unique_ptr<renderengine::RenderEngine> renderEngine = 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 (!renderEngine) {
+ std::unique_lock lock(mListenerMutex);
+ mListener->onError_nb(thiz, C2_CORRUPTED);
+ return;
+ }
+ uint32_t textureName = 0;
+ renderEngine->genTextures(1, &textureName);
+
+ while (true) {
+ // Before doing anything, verify the state
+ {
+ std::unique_lock lock(mStateMutex);
+ if (mState != RUNNING) {
+ break;
+ }
+ }
+ // Extract one work item
+ std::unique_ptr<C2Work> work;
+ {
+ std::unique_lock lock(mQueueMutex);
+ if (mQueue.empty()) {
+ mQueueCondition.wait_for(lock, 1s);
+ }
+ if (mQueue.empty()) {
+ continue;
+ }
+ mQueue.front().swap(work);
+ mQueue.pop_front();
+ ++workCount;
+ }
+ LOG(VERBOSE) << "work #" << workCount << ": flags=" << work->input.flags
+ << " timestamp=" << work->input.ordinal.timestamp.peek();;
+ std::shared_ptr<C2StreamHdrStaticInfo::input> hdrStaticInfo =
+ mIntf->getHdrStaticMetadata();
+ uint32_t dataspace = mIntf->getDataSpace();
+
+ std::shared_ptr<C2Buffer> buffer;
+ if (!work->input.buffers.empty()) {
+ buffer = work->input.buffers.front();
+ }
+ std::shared_ptr<C2Buffer> outC2Buffer;
+ status_t err = OK;
+ if (buffer) {
+ if (buffer->hasInfo(C2StreamHdrStaticInfo::output::PARAM_TYPE)) {
+ std::shared_ptr<const C2Info> info =
+ buffer->getInfo(C2StreamHdrStaticInfo::output::PARAM_TYPE);
+ std::unique_ptr<C2Param> flipped = C2Param::CopyAsStream(
+ *info, false /* output */, info->stream());
+ hdrStaticInfo.reset(static_cast<C2StreamHdrStaticInfo::input *>(
+ flipped.release()));
+ }
+ const C2Handle *c2Handle =
+ buffer->data().graphicBlocks().front().handle();
+ uint32_t width, height, format, stride, igbp_slot, generation;
+ uint64_t usage, igbp_id;
+ _UnwrapNativeCodec2GrallocMetadata(
+ c2Handle, &width, &height, &format, &usage, &stride, &generation,
+ &igbp_id, &igbp_slot);
+ native_handle_t *grallocHandle = UnwrapNativeCodec2GrallocHandle(c2Handle);
+ sp<GraphicBuffer> srcBuffer = new GraphicBuffer(
+ grallocHandle, GraphicBuffer::CLONE_HANDLE,
+ width, height, format, 1, usage, stride);
+
+ std::shared_ptr<C2GraphicBlock> dstBlock;
+ C2BlockPool::local_id_t poolId = mIntf->getPoolId();
+ std::shared_ptr<C2BlockPool> pool;
+ GetCodec2BlockPool(poolId, thiz, &pool);
+ pool->fetchGraphicBlock(
+ width, height, HAL_PIXEL_FORMAT_RGBA_8888, C2AndroidMemoryUsage::FromGrallocUsage(kDstUsage),
+ &dstBlock);
+ outC2Buffer = C2Buffer::CreateGraphicBuffer(
+ dstBlock->share(C2Rect(width, height), C2Fence()));
+ c2Handle = dstBlock->handle();
+ _UnwrapNativeCodec2GrallocMetadata(
+ c2Handle, &width, &height, &format, &usage, &stride, &generation,
+ &igbp_id, &igbp_slot);
+ grallocHandle = UnwrapNativeCodec2GrallocHandle(c2Handle);
+ sp<GraphicBuffer> dstBuffer = new GraphicBuffer(
+ grallocHandle, GraphicBuffer::CLONE_HANDLE,
+ width, height, format, 1, usage, stride);
+
+ Rect sourceCrop(0, 0, width, height);
+
+ renderengine::DisplaySettings clientCompositionDisplay;
+ std::vector<const renderengine::LayerSettings*> clientCompositionLayers;
+
+ clientCompositionDisplay.physicalDisplay = sourceCrop;
+ clientCompositionDisplay.clip = sourceCrop;
+
+ clientCompositionDisplay.outputDataspace = ui::Dataspace::V0_SRGB;
+ clientCompositionDisplay.maxLuminance = kDefaultMaxLumiance;
+ clientCompositionDisplay.clearRegion = Region::INVALID_REGION;
+ renderengine::LayerSettings layerSettings;
+ layerSettings.geometry.boundaries = sourceCrop.toFloatRect();
+ layerSettings.alpha = 1.0f;
+
+ layerSettings.sourceDataspace = static_cast<ui::Dataspace>(dataspace);
+
+ // from BufferLayer
+ layerSettings.source.buffer.buffer = srcBuffer;
+ layerSettings.source.buffer.isOpaque = true;
+ // TODO: fence
+ layerSettings.source.buffer.fence = Fence::NO_FENCE;
+ layerSettings.source.buffer.textureName = textureName;
+ layerSettings.source.buffer.usePremultipliedAlpha = false;
+ layerSettings.source.buffer.isY410BT2020 =
+ (layerSettings.sourceDataspace == ui::Dataspace::BT2020_ITU_PQ ||
+ layerSettings.sourceDataspace == ui::Dataspace::BT2020_ITU_HLG) &&
+ format == HAL_PIXEL_FORMAT_RGBA_1010102;
+ layerSettings.source.buffer.maxMasteringLuminance =
+ (hdrStaticInfo && *hdrStaticInfo &&
+ hdrStaticInfo->mastering.maxLuminance > 0 &&
+ hdrStaticInfo->mastering.minLuminance > 0)
+ ? hdrStaticInfo->mastering.maxLuminance : kDefaultMaxMasteringLuminance;
+ layerSettings.source.buffer.maxContentLuminance =
+ (hdrStaticInfo && *hdrStaticInfo && hdrStaticInfo->maxCll > 0)
+ ? hdrStaticInfo->maxCll : 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.
+ layerSettings.source.buffer.useTextureFiltering = false;
+ layerSettings.source.buffer.textureTransform = mat4();
+ 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;
+ renderEngine->useProtectedContext(false);
+ err = renderEngine->drawLayers(
+ clientCompositionDisplay, clientCompositionLayers, dstBuffer.get(),
+ /*useFramebufferCache=*/false, std::move(bufferFence), &drawFence);
+
+ sp<Fence> fence = new Fence(std::move(drawFence));
+
+ // We can move waiting for fence & sending it back on a separate thread to improve
+ // efficiency, but leaving it here for simplicity.
+ if (err != OK) {
+ LOG(ERROR) << "drawLayers returned err " << err;
+ } else {
+ err = fence->wait(500);
+ if (err != OK) {
+ LOG(WARNING) << "wait for fence returned err " << err;
+ }
+ }
+ renderEngine->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL);
+ }
+
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ work->worklets.front()->output.flags = work->input.flags;
+ if (err == OK) {
+ work->workletsProcessed = 1;
+ if (outC2Buffer) {
+ work->worklets.front()->output.buffers.push_back(outC2Buffer);
+ }
+ work->result = C2_OK;
+ } else {
+ work->result = C2_CORRUPTED;
+ }
+ std::list<std::unique_ptr<C2Work>> items;
+ items.push_back(std::move(work));
+
+ std::unique_lock lock(mListenerMutex);
+ mListener->onWorkDone_nb(thiz, std::move(items));
+ LOG(VERBOSE) << "sent work #" << workCount;
+ }
+ }
+
+ mutable std::timed_mutex mListenerMutex;
+ std::shared_ptr<Listener> mListener;
+
+ mutable std::mutex mQueueMutex;
+ mutable std::condition_variable mQueueCondition;
+ std::list<std::unique_ptr<C2Work>> mQueue;
+
+ const std::shared_ptr<Interface> mIntf;
+
+ mutable std::mutex mStateMutex;
+ enum State {
+ STOPPED,
+ RUNNING,
+ RELEASED,
+ STARTING, // STOPPED -> RUNNING
+ STOPPING, // RUNNING -> STOPPED
+ RESETTING, // <<ANY>> -> STOPPED
+ RELEASING, // STOPPED -> RELEASED
+ } mState;
+
+ mutable std::mutex mProcessingMutex;
+ std::thread mProcessingThread;
+
+};
+
+// static
+const std::string SampleToneMappingFilter::Interface::NAME = "c2.sample.tone-mapper";
+// static
+const FilterPlugin_V1::Descriptor SampleToneMappingFilter::Interface::DESCRIPTOR = {
+ // controlParams
+ { C2StreamColorAspectsRequestInfo::output::PARAM_TYPE },
+ // affectedParams
+ {
+ C2StreamHdrStaticInfo::output::PARAM_TYPE,
+ C2StreamHdr10PlusInfo::output::PARAM_TYPE,
+ C2StreamColorAspectsInfo::output::PARAM_TYPE,
+ },
+};
+
+class SampleC2ComponentStore : public C2ComponentStore {
+public:
+ SampleC2ComponentStore()
+ : mReflector(std::make_shared<C2ReflectorHelper>()),
+ mIntf(mReflector),
+ mFactories(CreateFactories()) {
+ }
+ ~SampleC2ComponentStore() = default;
+
+ C2String getName() const override { return "android.sample.filter-plugin-store"; }
+ c2_status_t createComponent(
+ C2String name, std::shared_ptr<C2Component>* const component) override {
+ if (mFactories.count(name) == 0) {
+ return C2_BAD_VALUE;
+ }
+ return mFactories.at(name)->createComponent(++mNodeId, component);
+ }
+ c2_status_t createInterface(
+ C2String name, std::shared_ptr<C2ComponentInterface>* const interface) override {
+ if (mFactories.count(name) == 0) {
+ return C2_BAD_VALUE;
+ }
+ return mFactories.at(name)->createInterface(++mNodeId, interface);
+ }
+ std::vector<std::shared_ptr<const C2Component::Traits>> listComponents() override {
+ std::vector<std::shared_ptr<const C2Component::Traits>> ret;
+ for (const auto &[name, factory] : mFactories) {
+ ret.push_back(factory->getTraits());
+ }
+ return ret;
+ }
+ c2_status_t copyBuffer(
+ std::shared_ptr<C2GraphicBuffer>, std::shared_ptr<C2GraphicBuffer>) override {
+ return C2_OMITTED;
+ }
+ c2_status_t query_sm(
+ const std::vector<C2Param*> &stackParams,
+ const std::vector<C2Param::Index> &heapParamIndices,
+ std::vector<std::unique_ptr<C2Param>>* const heapParams) const override {
+ return mIntf.query(stackParams, heapParamIndices, C2_MAY_BLOCK, heapParams);
+ }
+ c2_status_t config_sm(
+ const std::vector<C2Param*> ¶ms,
+ std::vector<std::unique_ptr<C2SettingResult>>* const failures) override {
+ return mIntf.config(params, C2_MAY_BLOCK, failures);
+ }
+ std::shared_ptr<C2ParamReflector> getParamReflector() const override {
+ return mReflector;
+ }
+ c2_status_t querySupportedParams_nb(
+ std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const override {
+ return mIntf.querySupportedParams(params);
+ }
+ c2_status_t querySupportedValues_sm(
+ std::vector<C2FieldSupportedValuesQuery> &fields) const override {
+ return mIntf.querySupportedValues(fields, C2_MAY_BLOCK);
+ }
+
+private:
+ class ComponentFactory {
+ public:
+ virtual ~ComponentFactory() = default;
+
+ const std::shared_ptr<const C2Component::Traits> &getTraits() { return mTraits; }
+
+ virtual c2_status_t createComponent(
+ c2_node_id_t id,
+ std::shared_ptr<C2Component>* const component) const = 0;
+ virtual c2_status_t createInterface(
+ c2_node_id_t id,
+ std::shared_ptr<C2ComponentInterface>* const interface) const = 0;
+ protected:
+ ComponentFactory(const std::shared_ptr<const C2Component::Traits> &traits)
+ : mTraits(traits) {
+ }
+ private:
+ const std::shared_ptr<const C2Component::Traits> mTraits;
+ };
+
+ template <class T>
+ struct ComponentFactoryImpl : public ComponentFactory {
+ public:
+ ComponentFactoryImpl(const std::shared_ptr<const C2Component::Traits> &traits)
+ : ComponentFactory(traits) {
+ }
+ ~ComponentFactoryImpl() override = default;
+ c2_status_t createComponent(
+ c2_node_id_t id,
+ std::shared_ptr<C2Component>* const component) const override {
+ *component = std::make_shared<T>(id);
+ return C2_OK;
+ }
+ c2_status_t createInterface(
+ c2_node_id_t id,
+ std::shared_ptr<C2ComponentInterface>* const interface) const override {
+ *interface = std::make_shared<typename T::Interface>(id);
+ return C2_OK;
+ }
+ };
+
+ template <class T>
+ static void AddFactory(std::map<C2String, std::unique_ptr<ComponentFactory>> *factories) {
+ std::shared_ptr<C2ComponentInterface> intf{new typename T::Interface(0)};
+ std::shared_ptr<C2Component::Traits> traits(new (std::nothrow) C2Component::Traits);
+ CHECK(C2InterfaceUtils::FillTraitsFromInterface(traits.get(), intf))
+ << "Failed to fill traits from interface";
+ factories->emplace(traits->name, new ComponentFactoryImpl<T>(traits));
+ }
+
+ static std::map<C2String, std::unique_ptr<ComponentFactory>> CreateFactories() {
+ std::map<C2String, std::unique_ptr<ComponentFactory>> factories;
+ AddFactory<SampleToneMappingFilter>(&factories);
+ return factories;
+ }
+
+
+ std::shared_ptr<C2ReflectorHelper> mReflector;
+ struct Interface : public C2InterfaceHelper {
+ explicit Interface(std::shared_ptr<C2ReflectorHelper> reflector)
+ : C2InterfaceHelper(reflector) {
+ }
+ } mIntf;
+
+ const std::map<C2String, std::unique_ptr<ComponentFactory>> mFactories;
+
+ std::atomic_int32_t mNodeId{0};
+};
+
+class SampleFilterPlugin : public FilterPlugin_V1 {
+public:
+ SampleFilterPlugin() : mStore(new SampleC2ComponentStore) {}
+ ~SampleFilterPlugin() override = default;
+
+ std::shared_ptr<C2ComponentStore> getComponentStore() override {
+ return mStore;
+ }
+
+ bool describe(C2String name, Descriptor *desc) override {
+ if (name == SampleToneMappingFilter::Interface::NAME) {
+ *desc = SampleToneMappingFilter::Interface::DESCRIPTOR;
+ return true;
+ }
+ return false;
+ }
+
+ bool isFilteringEnabled(const std::shared_ptr<C2ComponentInterface> &intf) override {
+ if (intf->getName() == SampleToneMappingFilter::Interface::NAME) {
+ return SampleToneMappingFilter::Interface::IsFilteringEnabled(intf);
+ }
+ return false;
+ }
+
+ c2_status_t queryParamsForPreviousComponent(
+ const std::shared_ptr<C2ComponentInterface> &intf,
+ std::vector<std::unique_ptr<C2Param>> *params) override {
+ if (intf->getName() == SampleToneMappingFilter::Interface::NAME) {
+ return SampleToneMappingFilter::Interface::QueryParamsForPreviousComponent(
+ intf, params);
+ }
+ return C2_BAD_VALUE;
+ }
+
+private:
+ std::shared_ptr<C2ComponentStore> mStore;
+};
+
+} // namespace android
+
+extern "C" {
+
+int32_t GetFilterPluginVersion() {
+ return ::android::SampleFilterPlugin::VERSION;
+}
+
+void *CreateFilterPlugin() {
+ return new ::android::SampleFilterPlugin;
+}
+
+void DestroyFilterPlugin(void *plugin) {
+ delete (::android::SampleFilterPlugin *)plugin;
+}
+
+} // extern "C"
diff --git a/media/codec2/hidl/services/Android.bp b/media/codec2/hidl/services/Android.bp
index 5a23217..bb9f51f 100644
--- a/media/codec2/hidl/services/Android.bp
+++ b/media/codec2/hidl/services/Android.bp
@@ -39,11 +39,11 @@
}
cc_binary {
- name: "android.hardware.media.c2@1.1-default-service",
+ name: "android.hardware.media.c2@1.2-default-service",
vendor: true,
relative_install_path: "hw",
- init_rc: ["android.hardware.media.c2@1.1-default-service.rc"],
+ init_rc: ["android.hardware.media.c2@1.2-default-service.rc"],
defaults: ["libcodec2-hidl-defaults"],
srcs: [
@@ -55,7 +55,7 @@
"libavservices_minijail_vendor",
"libbinder",
],
- required: ["android.hardware.media.c2@1.1-default-seccomp_policy"],
+ required: ["android.hardware.media.c2@1.2-default-seccomp_policy"],
// The content in manifest_media_c2_V1_1_default.xml can be included
// directly in the main device manifest.xml file or via vintf_fragments.
@@ -73,23 +73,23 @@
// 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",
+ name: "android.hardware.media.c2@1.2-default-seccomp_policy",
vendor: true,
sub_dir: "seccomp_policy",
// If a specific architecture is targeted, multiple choices are not needed.
arch: {
arm: {
- src: "seccomp_policy/android.hardware.media.c2@1.1-default-arm.policy",
+ src: "seccomp_policy/android.hardware.media.c2@1.2-default-arm.policy",
},
arm64: {
- src: "seccomp_policy/android.hardware.media.c2@1.1-default-arm64.policy",
+ src: "seccomp_policy/android.hardware.media.c2@1.2-default-arm64.policy",
},
x86: {
- src: "seccomp_policy/android.hardware.media.c2@1.1-default-x86.policy",
+ src: "seccomp_policy/android.hardware.media.c2@1.2-default-x86.policy",
},
x86_64: {
- src: "seccomp_policy/android.hardware.media.c2@1.1-default-x86_64.policy",
+ src: "seccomp_policy/android.hardware.media.c2@1.2-default-x86_64.policy",
},
},
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
deleted file mode 100644
index 44f2d8e..0000000
--- a/media/codec2/hidl/services/android.hardware.media.c2@1.1-default-service.rc
+++ /dev/null
@@ -1,7 +0,0 @@
-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/android.hardware.media.c2@1.2-default-service.rc b/media/codec2/hidl/services/android.hardware.media.c2@1.2-default-service.rc
new file mode 100644
index 0000000..03f6e3d
--- /dev/null
+++ b/media/codec2/hidl/services/android.hardware.media.c2@1.2-default-service.rc
@@ -0,0 +1,7 @@
+service android-hardware-media-c2-hal-1-2 /vendor/bin/hw/android.hardware.media.c2@1.2-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/manifest_media_c2_V1_2_default.xml b/media/codec2/hidl/services/manifest_media_c2_V1_2_default.xml
new file mode 100644
index 0000000..a5e8d87
--- /dev/null
+++ b/media/codec2/hidl/services/manifest_media_c2_V1_2_default.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal>
+ <name>android.hardware.media.c2</name>
+ <transport>hwbinder</transport>
+ <version>1.2</version>
+ <interface>
+ <name>IComponentStore</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-arm64.policy b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-arm64.policy
deleted file mode 100644
index 85fd28d..0000000
--- a/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-arm64.policy
+++ /dev/null
@@ -1,81 +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.
-
-futex: 1
-# ioctl calls are filtered via the selinux policy.
-ioctl: 1
-sched_yield: 1
-close: 1
-dup: 1
-ppoll: 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
-# |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 || arg3 == MREMAP_MAYMOVE
-munmap: 1
-prctl: 1
-writev: 1
-sigaltstack: 1
-clone: 1
-exit: 1
-lseek: 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
-
-# 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/android.hardware.media.c2@1.1-default-x86.policy b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-x86.policy
deleted file mode 100644
index d9c4045..0000000
--- a/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-x86.policy
+++ /dev/null
@@ -1,71 +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.
-
-read: 1
-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
-_llseek: 1
-lseek: 1
-clone: 1
-sigaltstack: 1
-setpriority: 1
-restart_syscall: 1
-exit: 1
-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
-sched_yield: 1
-getpid: 1
-gettid: 1
-
-@include /system/etc/seccomp_policy/crash_dump.x86.policy
diff --git a/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-x86_64.policy b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-x86_64.policy
deleted file mode 100644
index d9c4045..0000000
--- a/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-x86_64.policy
+++ /dev/null
@@ -1,71 +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.
-
-read: 1
-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
-_llseek: 1
-lseek: 1
-clone: 1
-sigaltstack: 1
-setpriority: 1
-restart_syscall: 1
-exit: 1
-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
-sched_yield: 1
-getpid: 1
-gettid: 1
-
-@include /system/etc/seccomp_policy/crash_dump.x86.policy
diff --git a/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-arm.policy b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.2-default-arm.policy
similarity index 100%
rename from media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.1-default-arm.policy
rename to media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.2-default-arm.policy
diff --git a/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.2-default-arm64.policy b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.2-default-arm64.policy
new file mode 100644
index 0000000..5d0284f
--- /dev/null
+++ b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.2-default-arm64.policy
@@ -0,0 +1,81 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+futex: 1
+# ioctl calls are filtered via the selinux policy.
+ioctl: 1
+sched_yield: 1
+close: 1
+dup: 1
+ppoll: 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
+# |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 || arg3 == MREMAP_MAYMOVE
+munmap: 1
+prctl: 1
+writev: 1
+sigaltstack: 1
+clone: 1
+exit: 1
+lseek: 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
+
+# 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/android.hardware.media.c2@1.2-default-x86.policy b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.2-default-x86.policy
new file mode 100644
index 0000000..056c690
--- /dev/null
+++ b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.2-default-x86.policy
@@ -0,0 +1,71 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+read: 1
+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
+_llseek: 1
+lseek: 1
+clone: 1
+sigaltstack: 1
+setpriority: 1
+restart_syscall: 1
+exit: 1
+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
+sched_yield: 1
+getpid: 1
+gettid: 1
+
+@include /system/etc/seccomp_policy/crash_dump.x86.policy
diff --git a/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.2-default-x86_64.policy b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.2-default-x86_64.policy
new file mode 100644
index 0000000..056c690
--- /dev/null
+++ b/media/codec2/hidl/services/seccomp_policy/android.hardware.media.c2@1.2-default-x86_64.policy
@@ -0,0 +1,71 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+read: 1
+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
+_llseek: 1
+lseek: 1
+clone: 1
+sigaltstack: 1
+setpriority: 1
+restart_syscall: 1
+exit: 1
+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
+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 3ddb039..0d0684d 100644
--- a/media/codec2/hidl/services/vendor.cpp
+++ b/media/codec2/hidl/services/vendor.cpp
@@ -15,11 +15,11 @@
*/
//#define LOG_NDEBUG 0
-#define LOG_TAG "android.hardware.media.c2@1.1-service"
+#define LOG_TAG "android.hardware.media.c2@1.2-service"
#include <android-base/logging.h>
#include <binder/ProcessState.h>
-#include <codec2/hidl/1.1/ComponentStore.h>
+#include <codec2/hidl/1.2/ComponentStore.h>
#include <hidl/HidlTransportSupport.h>
#include <minijail.h>
@@ -31,13 +31,13 @@
// "android.hardware.media.c2@1.1-default-seccomp_policy" in Android.bp.
static constexpr char kBaseSeccompPolicyPath[] =
"/vendor/etc/seccomp_policy/"
- "android.hardware.media.c2@1.1-default-seccomp-policy";
+ "android.hardware.media.c2@1.2-default-seccomp-policy";
// Additional seccomp permissions can be added in this file.
// This file does not exist by default.
static constexpr char kExtSeccompPolicyPath[] =
"/vendor/etc/seccomp_policy/"
- "android.hardware.media.c2@1.1-extended-seccomp-policy";
+ "android.hardware.media.c2@1.2-extended-seccomp-policy";
class StoreImpl : public C2ComponentStore {
public:
@@ -164,7 +164,7 @@
int main(int /* argc */, char** /* argv */) {
using namespace ::android;
- LOG(DEBUG) << "android.hardware.media.c2@1.1-service starting...";
+ LOG(DEBUG) << "android.hardware.media.c2@1.2-service starting...";
// Set up minijail to limit system calls.
signal(SIGPIPE, SIG_IGN);
@@ -180,7 +180,7 @@
// Create IComponentStore service.
{
- using namespace ::android::hardware::media::c2::V1_1;
+ using namespace ::android::hardware::media::c2::V1_2;
sp<IComponentStore> store;
// TODO: Replace this with
diff --git a/media/codec2/sfplugin/C2OMXNode.cpp b/media/codec2/sfplugin/C2OMXNode.cpp
index 2460490..c049187 100644
--- a/media/codec2/sfplugin/C2OMXNode.cpp
+++ b/media/codec2/sfplugin/C2OMXNode.cpp
@@ -90,6 +90,10 @@
}
}
+ void setPriority(int priority) {
+ androidSetThreadPriority(getTid(), priority);
+ }
+
protected:
bool threadLoop() override {
constexpr nsecs_t kIntervalNs = nsecs_t(10) * 1000 * 1000; // 10ms
@@ -419,6 +423,8 @@
if (err != OK) {
(void)fd0.release();
(void)fd1.release();
+ native_handle_close(handle);
+ native_handle_delete(handle);
return UNKNOWN_ERROR;
}
block = _C2BlockFactory::CreateGraphicBlock(alloc);
@@ -527,4 +533,8 @@
return *mDataspace.lock();
}
+void C2OMXNode::setPriority(int priority) {
+ mQueueThread->setPriority(priority);
+}
+
} // namespace android
diff --git a/media/codec2/sfplugin/C2OMXNode.h b/media/codec2/sfplugin/C2OMXNode.h
index 9c04969..6669318 100644
--- a/media/codec2/sfplugin/C2OMXNode.h
+++ b/media/codec2/sfplugin/C2OMXNode.h
@@ -98,6 +98,11 @@
*/
android_dataspace getDataspace();
+ /**
+ * Sets priority of the queue thread.
+ */
+ void setPriority(int priority);
+
private:
std::weak_ptr<Codec2Client::Component> mComp;
sp<IOMXBufferSource> mBufferSource;
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 12b9605..051e9cf 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -398,6 +398,14 @@
// consumer usage is queried earlier.
+ // priority
+ if (mConfig.mPriority != config.mPriority) {
+ if (config.mPriority != INT_MAX) {
+ mNode->setPriority(config.mPriority);
+ }
+ mConfig.mPriority = config.mPriority;
+ }
+
if (status.str().empty()) {
ALOGD("ISConfig not changed");
} else {
@@ -732,17 +740,18 @@
std::make_shared<Codec2ClientInterfaceWrapper>(client));
}
- std::shared_ptr<Codec2Client::Component> comp =
- Codec2Client::CreateComponentByName(
+ std::shared_ptr<Codec2Client::Component> comp;
+ c2_status_t status = Codec2Client::CreateComponentByName(
componentName.c_str(),
mClientListener,
+ &comp,
&client);
- if (!comp) {
- ALOGE("Failed Create component: %s", componentName.c_str());
+ if (status != C2_OK) {
+ ALOGE("Failed Create component: %s, error=%d", componentName.c_str(), status);
Mutexed<State>::Locked state(mState);
state->set(RELEASED);
state.unlock();
- mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+ mCallback->onError((status == C2_NO_MEMORY ? NO_MEMORY : UNKNOWN_ERROR), ACTION_CODE_FATAL);
state.lock();
return;
}
@@ -982,6 +991,7 @@
}
}
config->mISConfig->mUsage = 0;
+ config->mISConfig->mPriority = INT_MAX;
}
/*
@@ -1173,6 +1183,16 @@
configUpdate.push_back(std::move(qp));
}
+ int32_t background = 0;
+ if ((config->mDomain & Config::IS_VIDEO)
+ && msg->findInt32("android._background-mode", &background)
+ && background) {
+ androidSetThreadPriority(gettid(), ANDROID_PRIORITY_BACKGROUND);
+ if (config->mISConfig) {
+ config->mISConfig->mPriority = ANDROID_PRIORITY_BACKGROUND;
+ }
+ }
+
err = config->setParameters(comp, configUpdate, C2_DONT_BLOCK);
if (err != OK) {
ALOGW("failed to configure c2 params");
@@ -1206,6 +1226,7 @@
C2AndroidMemoryUsage androidUsage(C2MemoryUsage(usage.value));
config->mISConfig->mUsage = androidUsage.asGrallocUsage();
}
+ config->mInputFormat->setInt64("android._C2MemoryUsage", usage.value);
}
// NOTE: we don't blindly use client specified input size if specified as clients
@@ -2434,7 +2455,18 @@
return;
}
- ALOGW("previous call to %s exceeded timeout", name.c_str());
+ C2String compName;
+ {
+ Mutexed<State>::Locked state(mState);
+ if (!state->comp) {
+ ALOGD("previous call to %s exceeded timeout "
+ "and the component is already released", name.c_str());
+ return;
+ }
+ compName = state->comp->getName();
+ }
+ ALOGW("[%s] previous call to %s exceeded timeout", compName.c_str(), name.c_str());
+
initiateRelease(false);
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
}
@@ -2680,15 +2712,17 @@
return OK;
}
}
- uint64_t minUsage = usage.expected;
- uint64_t maxUsage = ~0ull;
std::set<C2Allocator::id_t> allocators;
GetCommonAllocatorIds(names, C2Allocator::LINEAR, &allocators);
if (allocators.empty()) {
*isCompatible = false;
return OK;
}
+
+ uint64_t minUsage = 0;
+ uint64_t maxUsage = ~0ull;
CalculateMinMaxUsage(names, &minUsage, &maxUsage);
+ minUsage |= usage.expected;
*isCompatible = ((maxUsage & minUsage) == minUsage);
return OK;
}
@@ -2715,14 +2749,16 @@
// static
std::shared_ptr<C2LinearBlock> CCodec::FetchLinearBlock(
size_t capacity, const C2MemoryUsage &usage, const std::vector<std::string> &names) {
- uint64_t minUsage = usage.expected;
- uint64_t maxUsage = ~0ull;
std::set<C2Allocator::id_t> allocators;
GetCommonAllocatorIds(names, C2Allocator::LINEAR, &allocators);
if (allocators.empty()) {
allocators.insert(C2PlatformAllocatorStore::DEFAULT_LINEAR);
}
+
+ uint64_t minUsage = 0;
+ uint64_t maxUsage = ~0ull;
CalculateMinMaxUsage(names, &minUsage, &maxUsage);
+ minUsage |= usage.expected;
if ((maxUsage & minUsage) != minUsage) {
allocators.clear();
allocators.insert(C2PlatformAllocatorStore::DEFAULT_LINEAR);
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 5a58fd8..3c3b41d 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -161,6 +161,10 @@
output->outputDelay = 0u;
output->numSlots = kSmoothnessFactor;
}
+ {
+ Mutexed<BlockPools>::Locked pools(mBlockPools);
+ pools->outputPoolId = C2BlockPool::BASIC_LINEAR;
+ }
}
CCodecBufferChannel::~CCodecBufferChannel() {
@@ -1173,9 +1177,10 @@
if (outputFormat != nullptr) {
sp<IGraphicBufferProducer> outputSurface;
uint32_t outputGeneration;
+ int maxDequeueCount = 0;
{
Mutexed<OutputSurface>::Locked output(mOutputSurface);
- output->maxDequeueBuffers = numOutputSlots +
+ maxDequeueCount = output->maxDequeueBuffers = numOutputSlots +
reorderDepth.value + kRenderingDepth;
outputSurface = output->surface ?
output->surface->getIGraphicBufferProducer() : nullptr;
@@ -1187,10 +1192,13 @@
bool graphic = (oStreamFormat.value == C2BufferData::GRAPHIC);
C2BlockPool::local_id_t outputPoolId_;
+ C2BlockPool::local_id_t prevOutputPoolId;
{
Mutexed<BlockPools>::Locked pools(mBlockPools);
+ prevOutputPoolId = pools->outputPoolId;
+
// set default allocator ID.
pools->outputAllocatorId = (graphic) ? C2PlatformAllocatorStore::GRALLOC
: preferredLinearId;
@@ -1284,6 +1292,15 @@
outputPoolId_ = pools->outputPoolId;
}
+ if (prevOutputPoolId != C2BlockPool::BASIC_LINEAR
+ && prevOutputPoolId != C2BlockPool::BASIC_GRAPHIC) {
+ c2_status_t err = mComponent->destroyBlockPool(prevOutputPoolId);
+ if (err != C2_OK) {
+ ALOGW("Failed to clean up previous block pool %llu - %s (%d)\n",
+ (unsigned long long) prevOutputPoolId, asString(err), err);
+ }
+ }
+
Mutexed<Output>::Locked output(mOutput);
output->outputDelay = outputDelayValue;
output->numSlots = numOutputSlots;
@@ -1311,7 +1328,8 @@
mComponent->setOutputSurface(
outputPoolId_,
outputSurface,
- outputGeneration);
+ outputGeneration,
+ maxDequeueCount);
}
if (oStreamFormat.value == C2BufferData::LINEAR) {
@@ -1767,10 +1785,17 @@
mCCodecCallback->onOutputBuffersChanged();
}
if (needMaxDequeueBufferCountUpdate) {
- Mutexed<OutputSurface>::Locked output(mOutputSurface);
- output->maxDequeueBuffers = numOutputSlots + reorderDepth + kRenderingDepth;
- if (output->surface) {
- output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers);
+ int maxDequeueCount = 0;
+ {
+ Mutexed<OutputSurface>::Locked output(mOutputSurface);
+ maxDequeueCount = output->maxDequeueBuffers =
+ numOutputSlots + reorderDepth + kRenderingDepth;
+ if (output->surface) {
+ output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers);
+ }
+ }
+ if (maxDequeueCount > 0) {
+ mComponent->setOutputSurfaceMaxDequeueCount(maxDequeueCount);
}
}
@@ -1827,7 +1852,13 @@
}
}
- if (notifyClient && !buffer && !flags) {
+ bool drop = false;
+ if (worklet->output.flags & C2FrameData::FLAG_DROP_FRAME) {
+ ALOGV("[%s] onWorkDone: drop buffer but keep metadata", mName);
+ drop = true;
+ }
+
+ if (notifyClient && !buffer && !flags && !(drop && outputFormat)) {
ALOGV("[%s] onWorkDone: Not reporting output buffer (%lld)",
mName, work->input.ordinal.frameIndex.peekull());
notifyClient = false;
@@ -1854,7 +1885,7 @@
return false;
}
output->buffers->pushToStash(
- buffer,
+ drop ? nullptr : buffer,
notifyClient,
timestamp.peek(),
flags,
@@ -1918,10 +1949,11 @@
& ((1 << 10) - 1));
sp<IGraphicBufferProducer> producer;
+ int maxDequeueCount = mOutputSurface.lock()->maxDequeueBuffers;
if (newSurface) {
newSurface->setScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
newSurface->setDequeueTimeout(kDequeueTimeoutNs);
- newSurface->setMaxDequeuedBufferCount(mOutputSurface.lock()->maxDequeueBuffers);
+ newSurface->setMaxDequeuedBufferCount(maxDequeueCount);
producer = newSurface->getIGraphicBufferProducer();
producer->setGenerationNumber(generation);
} else {
@@ -1941,7 +1973,8 @@
if (mComponent->setOutputSurface(
outputPoolId,
producer,
- generation) != C2_OK) {
+ generation,
+ maxDequeueCount) != C2_OK) {
ALOGI("[%s] setSurface: component setOutputSurface failed", mName);
return INVALID_OPERATION;
}
diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp
index ba44074..e7207a5 100644
--- a/media/codec2/sfplugin/CCodecBuffers.cpp
+++ b/media/codec2/sfplugin/CCodecBuffers.cpp
@@ -791,8 +791,9 @@
capacity = kMaxLinearBufferSize;
}
- // TODO: read usage from intf
- C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+ int64_t usageValue = 0;
+ (void)format->findInt64("android._C2MemoryUsage", &usageValue);
+ C2MemoryUsage usage{usageValue | C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE};
std::shared_ptr<C2LinearBlock> block;
c2_status_t err = pool->fetchLinearBlock(capacity, usage, &block);
@@ -1038,8 +1039,9 @@
}
sp<Codec2Buffer> GraphicInputBuffers::createNewBuffer() {
- // TODO: read usage from intf
- C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+ int64_t usageValue = 0;
+ (void)mFormat->findInt64("android._C2MemoryUsage", &usageValue);
+ C2MemoryUsage usage{usageValue | C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE};
return AllocateGraphicBuffer(
mPool, mFormat, extractPixelFormat(mFormat), usage, mLocalBufferPool);
}
diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp
index fc4ee51..34e6a88 100644
--- a/media/codec2/sfplugin/Codec2Buffer.cpp
+++ b/media/codec2/sfplugin/Codec2Buffer.cpp
@@ -713,6 +713,8 @@
c2_status_t err = mAlloc->priorGraphicAllocation(handle, &alloc);
if (err != C2_OK) {
ALOGD("Failed to wrap VideoNativeMetadata into C2GraphicAllocation");
+ native_handle_close(handle);
+ native_handle_delete(handle);
return nullptr;
}
std::shared_ptr<C2GraphicBlock> block = _C2BlockFactory::CreateGraphicBlock(alloc);
diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
index a26f89e..77a63a7 100644
--- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp
+++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
@@ -338,6 +338,59 @@
// parse default XML files
parser.parseXmlFilesInSearchDirs();
+ // The mainline modules for media may optionally include some codec shaping information.
+ // Based on vendor partition SDK, and the brand/product/device information
+ // (expect to be empty in almost always)
+ //
+ {
+ // get build info so we know what file to search
+ // ro.vendor.build.fingerprint
+ std::string fingerprint = base::GetProperty("ro.vendor.build.fingerprint",
+ "brand/product/device:");
+ ALOGV("property_get for ro.vendor.build.fingerprint == '%s'", fingerprint.c_str());
+
+ // ro.vendor.build.version.sdk
+ std::string sdk = base::GetProperty("ro.vendor.build.version.sdk", "0");
+ ALOGV("property_get for ro.vendor.build.version.sdk == '%s'", sdk.c_str());
+
+ std::string brand;
+ std::string product;
+ std::string device;
+ size_t pos1;
+ pos1 = fingerprint.find('/');
+ if (pos1 != std::string::npos) {
+ brand = fingerprint.substr(0, pos1);
+ size_t pos2 = fingerprint.find('/', pos1+1);
+ if (pos2 != std::string::npos) {
+ product = fingerprint.substr(pos1+1, pos2 - pos1 - 1);
+ size_t pos3 = fingerprint.find('/', pos2+1);
+ if (pos3 != std::string::npos) {
+ device = fingerprint.substr(pos2+1, pos3 - pos2 - 1);
+ size_t pos4 = device.find(':');
+ if (pos4 != std::string::npos) {
+ device.resize(pos4);
+ }
+ }
+ }
+ }
+
+ ALOGV("parsed: sdk '%s' brand '%s' product '%s' device '%s'",
+ sdk.c_str(), brand.c_str(), product.c_str(), device.c_str());
+
+ std::string base = "/apex/com.android.media/etc/formatshaper";
+
+ // looking in these directories within the apex
+ const std::vector<std::string> modulePathnames = {
+ base + "/" + sdk + "/" + brand + "/" + product + "/" + device,
+ base + "/" + sdk + "/" + brand + "/" + product,
+ base + "/" + sdk + "/" + brand,
+ base + "/" + sdk,
+ base
+ };
+
+ parser.parseXmlFilesInSearchDirs( { "media_codecs_shaping.xml" }, modulePathnames);
+ }
+
if (parser.getParsingStatus() != OK) {
ALOGD("XML parser no good");
return OK;
diff --git a/media/codec2/sfplugin/FrameReassembler.cpp b/media/codec2/sfplugin/FrameReassembler.cpp
index cf1be17..af054c7 100644
--- a/media/codec2/sfplugin/FrameReassembler.cpp
+++ b/media/codec2/sfplugin/FrameReassembler.cpp
@@ -161,7 +161,7 @@
if (mWriteView->error() != C2_OK) {
return mWriteView->error();
}
- ALOGV("buffer={offset=%zu size=%zu) copySize=%zu",
+ ALOGV("buffer={offset=%zu size=%zu} copySize=%zu",
buffer->offset(), buffer->size(), copySize);
memcpy(mWriteView->base(), buffer->data(), copySize);
mWriteView->setOffset(0u);
diff --git a/media/codec2/sfplugin/InputSurfaceWrapper.h b/media/codec2/sfplugin/InputSurfaceWrapper.h
index 50d600c..44ba78a 100644
--- a/media/codec2/sfplugin/InputSurfaceWrapper.h
+++ b/media/codec2/sfplugin/InputSurfaceWrapper.h
@@ -79,6 +79,7 @@
float mFixedAdjustedFps = 0.0; // fixed fps via PTS manipulation
float mMinAdjustedFps = 0.0; // minimum fps via PTS manipulation
uint64_t mUsage = 0; // consumer usage
+ int mPriority = INT_MAX; // priority of queue thread (if any); INT_MAX for no-op
};
/**
diff --git a/media/codec2/sfplugin/TEST_MAPPING b/media/codec2/sfplugin/TEST_MAPPING
new file mode 100644
index 0000000..045e5b5
--- /dev/null
+++ b/media/codec2/sfplugin/TEST_MAPPING
@@ -0,0 +1,12 @@
+// mappings for frameworks/av/media/codec2/sfplugin
+{
+ "presubmit": [
+ // failing 1 of 11
+ // TODO(b/156167471)
+ // { "name": "ccodec_unit_test" },
+
+ // failing 4 of 17, around max-input-size defaults & overrides
+ // TODO(b/156167471)
+ //{ "name": "mc_sanity_test"}
+ ]
+}
diff --git a/media/codec2/sfplugin/tests/Android.bp b/media/codec2/sfplugin/tests/Android.bp
index f9c7969..92f3754 100644
--- a/media/codec2/sfplugin/tests/Android.bp
+++ b/media/codec2/sfplugin/tests/Android.bp
@@ -9,6 +9,7 @@
cc_test {
name: "ccodec_unit_test",
+ test_suites: ["device-tests"],
srcs: [
"CCodecBuffers_test.cpp",
@@ -53,6 +54,7 @@
cc_test {
name: "mc_sanity_test",
+ test_suites: ["device-tests"],
srcs: [
"MediaCodec_sanity_test.cpp",
diff --git a/media/codec2/tests/Android.bp b/media/codec2/tests/Android.bp
index 8ebb6c0..b858fa5 100644
--- a/media/codec2/tests/Android.bp
+++ b/media/codec2/tests/Android.bp
@@ -9,6 +9,7 @@
cc_test {
name: "codec2_core_param_test",
+ test_suites: ["device-tests"],
srcs: [
"C2Param_test.cpp",
@@ -37,6 +38,7 @@
cc_test {
name: "codec2_vndk_test",
+ test_suites: ["device-tests"],
srcs: [
"C2_test.cpp",
diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp
index 0401c1d..be81c84 100644
--- a/media/codec2/vndk/Android.bp
+++ b/media/codec2/vndk/Android.bp
@@ -36,9 +36,11 @@
"C2Buffer.cpp",
"C2Config.cpp",
"C2DmaBufAllocator.cpp",
+ "C2Fence.cpp",
"C2PlatformStorePluginLoader.cpp",
"C2Store.cpp",
"platform/C2BqBuffer.cpp",
+ "platform/C2SurfaceSyncObj.cpp",
"types.cpp",
"util/C2Debug.cpp",
"util/C2InterfaceHelper.cpp",
diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp
index bee6b7f..4ffa3f1 100644
--- a/media/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/codec2/vndk/C2AllocatorGralloc.cpp
@@ -42,7 +42,9 @@
* Usage mask that is passed through from gralloc to Codec 2.0 usage.
*/
PASSTHROUGH_USAGE_MASK =
- ~(GRALLOC_USAGE_SW_READ_MASK | GRALLOC_USAGE_SW_WRITE_MASK | GRALLOC_USAGE_PROTECTED)
+ ~static_cast<uint64_t>(GRALLOC_USAGE_SW_READ_MASK |
+ GRALLOC_USAGE_SW_WRITE_MASK |
+ GRALLOC_USAGE_PROTECTED)
};
// verify that passthrough mask is within the platform mask
diff --git a/media/codec2/vndk/C2Fence.cpp b/media/codec2/vndk/C2Fence.cpp
new file mode 100644
index 0000000..9c5183e
--- /dev/null
+++ b/media/codec2/vndk/C2Fence.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2FenceFactory"
+#include <utils/Log.h>
+
+#include <C2FenceFactory.h>
+#include <C2SurfaceSyncObj.h>
+
+class C2Fence::Impl {
+public:
+ virtual c2_status_t wait(c2_nsecs_t timeoutNs) = 0;
+
+ virtual bool valid() const = 0;
+
+ virtual bool ready() const = 0;
+
+ virtual int fd() const = 0;
+
+ virtual bool isHW() const = 0;
+
+ virtual ~Impl() = default;
+
+ Impl() = default;
+};
+
+c2_status_t C2Fence::wait(c2_nsecs_t timeoutNs) {
+ if (mImpl) {
+ return mImpl->wait(timeoutNs);
+ }
+ // null fence is always signalled.
+ return C2_OK;
+}
+
+bool C2Fence::valid() const {
+ if (mImpl) {
+ return mImpl->valid();
+ }
+ // null fence is always valid.
+ return true;
+}
+
+bool C2Fence::ready() const {
+ if (mImpl) {
+ return mImpl->ready();
+ }
+ // null fence is always signalled.
+ return true;
+}
+
+int C2Fence::fd() const {
+ if (mImpl) {
+ return mImpl->fd();
+ }
+ // null fence does not have fd.
+ return -1;
+}
+
+bool C2Fence::isHW() const {
+ if (mImpl) {
+ return mImpl->isHW();
+ }
+ return false;
+}
+
+/**
+ * Fence implementation for C2BufferQueueBlockPool based block allocation.
+ * The implementation supports all C2Fence interface except fd().
+ */
+class _C2FenceFactory::SurfaceFenceImpl: public C2Fence::Impl {
+public:
+ virtual c2_status_t wait(c2_nsecs_t timeoutNs) {
+ if (mPtr) {
+ return mPtr->waitForChange(mWaitId, timeoutNs);
+ }
+ return C2_OK;
+ }
+
+ virtual bool valid() const {
+ return mPtr;
+ }
+
+ virtual bool ready() const {
+ uint32_t status;
+ if (mPtr) {
+ mPtr->lock();
+ status = mPtr->getWaitIdLocked();
+ mPtr->unlock();
+
+ return status != mWaitId;
+ }
+ return true;
+ }
+
+ virtual int fd() const {
+ // does not support fd, since this is shared mem and futex based
+ return -1;
+ }
+
+ virtual bool isHW() const {
+ return false;
+ }
+
+ virtual ~SurfaceFenceImpl() {};
+
+ SurfaceFenceImpl(std::shared_ptr<C2SurfaceSyncMemory> syncMem, uint32_t waitId) :
+ mSyncMem(syncMem),
+ mPtr(syncMem ? syncMem->mem() : nullptr),
+ mWaitId(syncMem ? waitId : 0) {}
+private:
+ const std::shared_ptr<const C2SurfaceSyncMemory> mSyncMem; // This is for life-cycle guarantee
+ C2SyncVariables *const mPtr;
+ const uint32_t mWaitId;
+};
+
+C2Fence::C2Fence(std::shared_ptr<Impl> impl) : mImpl(impl) {}
+
+C2Fence _C2FenceFactory::CreateSurfaceFence(
+ std::shared_ptr<C2SurfaceSyncMemory> syncMem,
+ uint32_t waitId) {
+ if (syncMem) {
+ C2Fence::Impl *p
+ = new _C2FenceFactory::SurfaceFenceImpl(syncMem, waitId);
+ if (p->valid()) {
+ return C2Fence(std::shared_ptr<C2Fence::Impl>(p));
+ } else {
+ delete p;
+ }
+ }
+ return C2Fence();
+}
diff --git a/media/codec2/vndk/include/C2AllocatorGralloc.h b/media/codec2/vndk/include/C2AllocatorGralloc.h
index 578cf76..1da3e14 100644
--- a/media/codec2/vndk/include/C2AllocatorGralloc.h
+++ b/media/codec2/vndk/include/C2AllocatorGralloc.h
@@ -37,7 +37,8 @@
* Wrap the gralloc handle and metadata into Codec2 handle recognized by
* C2AllocatorGralloc.
*
- * @return a new NON-OWNING C2Handle that must be deleted using native_handle_delete.
+ * @return a new NON-OWNING C2Handle that must be closed and deleted using native_handle_close and
+ * native_handle_delete.
*/
C2Handle *WrapNativeCodec2GrallocHandle(
const native_handle_t *const handle,
diff --git a/media/codec2/vndk/include/C2BqBufferPriv.h b/media/codec2/vndk/include/C2BqBufferPriv.h
index e1a8138..b2636e9 100644
--- a/media/codec2/vndk/include/C2BqBufferPriv.h
+++ b/media/codec2/vndk/include/C2BqBufferPriv.h
@@ -20,9 +20,14 @@
#include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h>
#include <C2Buffer.h>
+#include <C2BlockInternal.h>
#include <functional>
+namespace android {
+class GraphicBuffer;
+} // namespace android
+
class C2BufferQueueBlockPool : public C2BlockPool {
public:
C2BufferQueueBlockPool(const std::shared_ptr<C2Allocator> &allocator, const local_id_t localId);
@@ -44,6 +49,14 @@
C2MemoryUsage usage,
std::shared_ptr<C2GraphicBlock> *block /* nonnull */) override;
+ virtual c2_status_t fetchGraphicBlock(
+ uint32_t width,
+ uint32_t height,
+ uint32_t format,
+ C2MemoryUsage usage,
+ std::shared_ptr<C2GraphicBlock> *block /* nonnull */,
+ C2Fence *fence /* nonnull */) override;
+
typedef std::function<void(uint64_t producer, int32_t slot, int64_t nsecs)> OnRenderCallback;
/**
@@ -67,6 +80,27 @@
*/
virtual void configureProducer(const android::sp<HGraphicBufferProducer> &producer);
+ /**
+ * Configures an IGBP in order to create blocks. A newly created block is
+ * dequeued from the configured IGBP. Unique Id of IGBP and the slot number of
+ * blocks are passed via native_handle. Managing IGBP is responsibility of caller.
+ * When IGBP is not configured, block will be created via allocator.
+ * Since zero is not used for Unique Id of IGBP, if IGBP is not configured or producer
+ * is configured as nullptr, unique id which is bundled in native_handle is zero.
+ *
+ * \param producer the IGBP, which will be used to fetch blocks
+ * \param syncMemory Shared memory for synchronization of allocation & deallocation.
+ * \param bqId Id of IGBP
+ * \param generationId Generation Id for rendering output
+ * \param consumerUsage consumerUsage flagof the IGBP
+ */
+ virtual void configureProducer(
+ const android::sp<HGraphicBufferProducer> &producer,
+ native_handle_t *syncMemory,
+ uint64_t bqId,
+ uint32_t generationId,
+ uint64_t consumerUsage);
+
private:
const std::shared_ptr<C2Allocator> mAllocator;
const local_id_t mLocalId;
@@ -77,4 +111,72 @@
friend struct C2BufferQueueBlockPoolData;
};
+class C2SurfaceSyncMemory;
+
+struct C2BufferQueueBlockPoolData : public _C2BlockPoolData {
+public:
+ typedef ::android::hardware::graphics::bufferqueue::V2_0::
+ IGraphicBufferProducer HGraphicBufferProducer;
+
+ // Create a remote BlockPoolData.
+ C2BufferQueueBlockPoolData(
+ uint32_t generation, uint64_t bqId, int32_t bqSlot,
+ const std::shared_ptr<int> &owner,
+ const android::sp<HGraphicBufferProducer>& producer);
+
+ // Create a local BlockPoolData.
+ C2BufferQueueBlockPoolData(
+ uint32_t generation, uint64_t bqId, int32_t bqSlot,
+ const android::sp<HGraphicBufferProducer>& producer,
+ std::shared_ptr<C2SurfaceSyncMemory>, int noUse);
+
+ virtual ~C2BufferQueueBlockPoolData() override;
+
+ virtual type_t getType() const override;
+
+ int migrate(const android::sp<HGraphicBufferProducer>& producer,
+ uint32_t toGeneration, uint64_t toUsage, uint64_t toBqId,
+ android::sp<android::GraphicBuffer>& graphicBuffer, uint32_t oldGeneration,
+ std::shared_ptr<C2SurfaceSyncMemory> syncMem);
+
+private:
+ friend struct _C2BlockFactory;
+
+ // Methods delegated from _C2BlockFactory.
+ void getBufferQueueData(uint32_t* generation, uint64_t* bqId, int32_t* bqSlot) const;
+ bool holdBlockFromBufferQueue(const std::shared_ptr<int>& owner,
+ const android::sp<HGraphicBufferProducer>& igbp,
+ std::shared_ptr<C2SurfaceSyncMemory> syncMem);
+ bool beginTransferBlockToClient();
+ bool endTransferBlockToClient(bool transfer);
+ bool beginAttachBlockToBufferQueue();
+ bool endAttachBlockToBufferQueue(const std::shared_ptr<int>& owner,
+ const android::sp<HGraphicBufferProducer>& igbp,
+ std::shared_ptr<C2SurfaceSyncMemory> syncMem,
+ uint32_t generation, uint64_t bqId, int32_t bqSlot);
+ bool displayBlockToBufferQueue();
+
+ const bool mLocal;
+ bool mHeld;
+
+ // Data of the corresponding buffer.
+ uint32_t mGeneration;
+ uint64_t mBqId;
+ int32_t mBqSlot;
+
+ // Data of the current IGBP, updated at migrate(). If the values are
+ // mismatched, then the corresponding buffer will not be cancelled back to
+ // IGBP at the destructor.
+ uint32_t mCurrentGeneration;
+ uint64_t mCurrentBqId;
+
+ bool mTransfer; // local transfer to remote
+ bool mAttach; // attach on remote
+ bool mDisplay; // display on remote;
+ std::weak_ptr<int> mOwner;
+ android::sp<HGraphicBufferProducer> mIgbp;
+ std::shared_ptr<C2SurfaceSyncMemory> mSyncMem;
+ mutable std::mutex mLock;
+};
+
#endif // STAGEFRIGHT_CODEC2_BUFFER_PRIV_H_
diff --git a/media/codec2/vndk/include/C2FenceFactory.h b/media/codec2/vndk/include/C2FenceFactory.h
new file mode 100644
index 0000000..d4bed26
--- /dev/null
+++ b/media/codec2/vndk/include/C2FenceFactory.h
@@ -0,0 +1,44 @@
+/*
+ * 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 STAGEFRIGHT_CODEC2_FENCE_FACTORY_H_
+#define STAGEFRIGHT_CODEC2_FENCE_FACTORY_H_
+
+
+#include <C2Buffer.h>
+
+class C2SurfaceSyncMemory;
+
+/**
+ * C2Fence implementation factory
+ */
+struct _C2FenceFactory {
+
+ class SurfaceFenceImpl;
+
+ /*
+ * Create C2Fence for BufferQueueBased blockpool.
+ *
+ * \param syncMem Shared memory object for synchronization between processes.
+ * \param waitId wait id for tracking status change for C2Fence.
+ */
+ static C2Fence CreateSurfaceFence(
+ std::shared_ptr<C2SurfaceSyncMemory> syncMem,
+ uint32_t waitId);
+};
+
+
+#endif // STAGEFRIGHT_CODEC2_FENCE_FACTORY_H_
diff --git a/media/codec2/vndk/include/C2SurfaceSyncObj.h b/media/codec2/vndk/include/C2SurfaceSyncObj.h
new file mode 100644
index 0000000..ac87fe4
--- /dev/null
+++ b/media/codec2/vndk/include/C2SurfaceSyncObj.h
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef STAGEFRIGHT_CODEC2_SURFACE_SYNC_OBJ_H_
+#define STAGEFRIGHT_CODEC2_SURFACE_SYNC_OBJ_H_
+
+#include <cutils/native_handle.h>
+#include <memory>
+#include <atomic>
+
+#include <C2Buffer.h>
+
+/**
+ * Futex based lock / wait implementation for sharing output buffer allocation
+ * information between Framework and HAL.
+ */
+struct C2SyncVariables {
+ enum SyncStatus : uint32_t {
+ STATUS_INIT = 0, // When surface configuration starts.
+ STATUS_ACTIVE = 1, // When surface configuration finishs.
+ // STATUS_INIT -> STATUS_ACTIVE
+ STATUS_SWITCHING = 2, // When the surface is replaced by a new surface
+ // during surface configuration.
+ // STATUS_ACTIVE -> STATUS_SWITCHING
+ };
+
+ /**
+ * Lock the memory region
+ */
+ int lock();
+
+ /**
+ * Unlock the memory region
+ */
+ int unlock();
+
+ /**
+ * Set initial dequeued buffer count.
+ *
+ * \param maxDequeueCount Initial value of # of max dequeued buffer count
+ * \param curDequeueCount Initial value of # of current dequeued buffer count
+ */
+ void setInitialDequeueCountLocked(int32_t maxDequeueCount, int32_t curDequeueCount);
+
+ /**
+ * Get a waitId which will be used to implement fence.
+ */
+ uint32_t getWaitIdLocked();
+
+ /**
+ * Return whether the upcoming dequeue operation is not blocked.
+ * if it's blocked and waitId is non-null, waitId is returned to be used for waiting.
+ *
+ * \retval false dequeue operation is blocked now.
+ * \retval true dequeue operation is possible.
+ */
+ bool isDequeueableLocked(uint32_t *waitId = nullptr);
+
+ /**
+ * Notify a buffer is queued. Return whether the upcoming dequeue operation
+ * is not blocked. if it's blocked and waitId is non-null, waitId is returned
+ * to be used for waiting.
+ *
+ * \retval false dequeue operation is blocked now.
+ * \retval true dequeue operation is possible.
+ */
+ bool notifyQueuedLocked(uint32_t *waitId = nullptr);
+
+ /**
+ * Notify a buffer is dequeued.
+ */
+ void notifyDequeuedLocked();
+
+ /**
+ * Set sync status.
+ */
+ void setSyncStatusLocked(SyncStatus status);
+
+ /**
+ * Get sync status.
+ */
+ C2SyncVariables::SyncStatus getSyncStatusLocked();
+
+ /**
+ * Update current max dequeue count.
+ */
+ void updateMaxDequeueCountLocked(int32_t maxDequeueCount);
+
+ /**
+ * Wait until status is no longer equal to waitId, or until timeout.
+ *
+ * \param waitId internal status for waiting until it is changed.
+ * \param timeousNs nano seconds to timeout.
+ *
+ * \retval C2_TIMEDOUT change does not happen during waiting.
+ * \retval C2_BAD_VALUE invalid event waiting.
+ * \retval C2_OK change was signalled.
+ */
+ c2_status_t waitForChange(uint32_t waitId, c2_nsecs_t timeoutNs);
+
+ C2SyncVariables() {}
+
+private:
+ /**
+ * signal one waiter to wake up.
+ */
+ int signal();
+
+ /**
+ * signal all waiter to wake up.
+ */
+ int broadcast();
+
+ /**
+ * wait for signal or broadcast.
+ */
+ int wait();
+
+ std::atomic<uint32_t> mLock;
+ std::atomic<uint32_t> mCond;
+ int32_t mMaxDequeueCount;
+ int32_t mCurDequeueCount;
+ SyncStatus mStatus;
+};
+
+/**
+ * Shared memory in order to synchronize information for Surface(IGBP)
+ * based output buffer allocation.
+ */
+class C2SurfaceSyncMemory {
+public:
+ /**
+ * Shared memory handle in order to synchronize information for
+ * Surface based output buffer allocation.
+ */
+ struct HandleSyncMem : public native_handle_t {
+ HandleSyncMem(int fd, size_t size) :
+ native_handle_t(cHeader),
+ mFds{fd},
+ mInts{int(size & 0xFFFFFFFF),
+ int((uint64_t(size) >> 32) & 0xFFFFFFFF), kMagic} {}
+
+ /** Returns a file descriptor of the shared memory
+ * \return a file descriptor representing the shared memory
+ */
+ int memFd() const {return mFds.mMem;}
+
+ /** Returns the size of the shared memory */
+ size_t size() const {
+ return size_t(unsigned(mInts.mSizeLo))
+ | size_t(uint64_t(unsigned(mInts.mSizeHi)) << 32);
+ }
+
+ /** Check whether the native handle is in the form of HandleSyncMem
+ *
+ * \return whether the native handle is compatible
+ */
+ static bool isValid(const native_handle_t * const o);
+
+ protected:
+ struct {
+ int mMem;
+ } mFds;
+ struct {
+ int mSizeLo;
+ int mSizeHi;
+ int mMagic;
+ } mInts;
+ private:
+ enum {
+ kMagic = 'ssm\x00',
+ numFds = sizeof(mFds) / sizeof(int),
+ numInts = sizeof(mInts) / sizeof(int),
+ version = sizeof(native_handle_t)
+ };
+ const static native_handle_t cHeader;
+ };
+
+ /**
+ * Imports a shared memory object from a native handle(The shared memory is already existing).
+ * This is usually used after native_handle_t is passed via RPC.
+ *
+ * \param handle handle representing shared memory for output buffer allocation.
+ */
+ static std::shared_ptr<C2SurfaceSyncMemory> Import(native_handle_t *handle);
+
+ /**
+ * Creats a shared memory object for synchronization of output buffer allocation.
+ * Shared memory creation should be done explicitly.
+ *
+ * \param fd file descriptor to shared memory
+ * \param size size of the shared memory
+ */
+ static std::shared_ptr<C2SurfaceSyncMemory> Create(int fd, size_t size);
+
+ /**
+ * Returns a handle representing the shread memory for synchronization of
+ * output buffer allocation.
+ */
+ native_handle_t *handle();
+
+ /**
+ * Returns synchronization object which will provide synchronization primitives.
+ *
+ * \return a ptr to synchronization primitive class
+ */
+ C2SyncVariables *mem();
+
+ ~C2SurfaceSyncMemory();
+
+private:
+ bool mInit;
+ HandleSyncMem *mHandle;
+ C2SyncVariables *mMem;
+
+ C2SurfaceSyncMemory();
+};
+
+#endif // STAGEFRIGHT_CODEC2_SURFACE_SYNC_OBJ_H_
diff --git a/media/codec2/vndk/internal/C2BlockInternal.h b/media/codec2/vndk/internal/C2BlockInternal.h
index 4ae946a..c510fca 100644
--- a/media/codec2/vndk/internal/C2BlockInternal.h
+++ b/media/codec2/vndk/internal/C2BlockInternal.h
@@ -52,6 +52,8 @@
struct C2BufferQueueBlockPoolData;
+class C2SurfaceSyncMemory;
+
/**
* Internal only interface for creating blocks by block pool/buffer passing implementations.
*
@@ -279,6 +281,8 @@
* anymore.
* \param igbp \c IGraphicBufferProducer instance to be assigned to the
* block. This is not needed when the block is local.
+ * \param syncMem Memory block which will support synchronization
+ * between Framework and HAL.
*
* \return The previous held status.
*/
@@ -287,7 +291,8 @@
const std::shared_ptr<_C2BlockPoolData>& poolData,
const std::shared_ptr<int>& owner,
const ::android::sp<::android::hardware::graphics::bufferqueue::
- V2_0::IGraphicBufferProducer>& igbp = nullptr);
+ V2_0::IGraphicBufferProducer>& igbp = nullptr,
+ std::shared_ptr<C2SurfaceSyncMemory> syncMem = nullptr);
/**
* Prepare a block to be transferred to other process. This blocks
@@ -358,6 +363,7 @@
const std::shared_ptr<int>& owner,
const ::android::sp<::android::hardware::graphics::bufferqueue::
V2_0::IGraphicBufferProducer>& igbp,
+ std::shared_ptr<C2SurfaceSyncMemory>,
uint32_t generation,
uint64_t bqId,
int32_t bqSlot);
diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp
index fff12c4..169de0c 100644
--- a/media/codec2/vndk/platform/C2BqBuffer.cpp
+++ b/media/codec2/vndk/platform/C2BqBuffer.cpp
@@ -29,6 +29,8 @@
#include <C2AllocatorGralloc.h>
#include <C2BqBufferPriv.h>
#include <C2BlockInternal.h>
+#include <C2FenceFactory.h>
+#include <C2SurfaceSyncObj.h>
#include <list>
#include <map>
@@ -54,59 +56,13 @@
using HGraphicBufferProducer = ::android::hardware::graphics::bufferqueue::V2_0
::IGraphicBufferProducer;
-struct C2BufferQueueBlockPoolData : public _C2BlockPoolData {
-
- bool held;
- bool local;
- uint32_t generation;
- uint64_t bqId;
- int32_t bqSlot;
- bool transfer; // local transfer to remote
- bool attach; // attach on remote
- bool display; // display on remote;
- std::weak_ptr<int> owner;
- sp<HGraphicBufferProducer> igbp;
- std::shared_ptr<C2BufferQueueBlockPool::Impl> localPool;
- mutable std::mutex lock;
-
- virtual type_t getType() const override {
- return TYPE_BUFFERQUEUE;
- }
-
- // Create a remote BlockPoolData.
- C2BufferQueueBlockPoolData(
- uint32_t generation, uint64_t bqId, int32_t bqSlot,
- const std::shared_ptr<int> &owner,
- const sp<HGraphicBufferProducer>& producer);
-
- // Create a local BlockPoolData.
- C2BufferQueueBlockPoolData(
- uint32_t generation, uint64_t bqId, int32_t bqSlot,
- const std::shared_ptr<C2BufferQueueBlockPool::Impl>& pool);
-
- virtual ~C2BufferQueueBlockPoolData() override;
-
- int migrate(const sp<HGraphicBufferProducer>& producer,
- uint32_t toGeneration, uint64_t toBqId,
- sp<GraphicBuffer> *buffers, uint32_t oldGeneration);
-};
-
bool _C2BlockFactory::GetBufferQueueData(
const std::shared_ptr<const _C2BlockPoolData>& data,
uint32_t* generation, uint64_t* bqId, int32_t* bqSlot) {
if (data && data->getType() == _C2BlockPoolData::TYPE_BUFFERQUEUE) {
- if (generation) {
- const std::shared_ptr<const C2BufferQueueBlockPoolData> poolData =
- std::static_pointer_cast<const C2BufferQueueBlockPoolData>(data);
- std::scoped_lock<std::mutex> lock(poolData->lock);
- *generation = poolData->generation;
- if (bqId) {
- *bqId = poolData->bqId;
- }
- if (bqSlot) {
- *bqSlot = poolData->bqSlot;
- }
- }
+ const std::shared_ptr<const C2BufferQueueBlockPoolData> poolData =
+ std::static_pointer_cast<const C2BufferQueueBlockPoolData>(data);
+ poolData->getBufferQueueData(generation, bqId, bqSlot);
return true;
}
return false;
@@ -115,29 +71,18 @@
bool _C2BlockFactory::HoldBlockFromBufferQueue(
const std::shared_ptr<_C2BlockPoolData>& data,
const std::shared_ptr<int>& owner,
- const sp<HGraphicBufferProducer>& igbp) {
+ const sp<HGraphicBufferProducer>& igbp,
+ std::shared_ptr<C2SurfaceSyncMemory> syncMem) {
const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
- std::scoped_lock<std::mutex> lock(poolData->lock);
- if (!poolData->local) {
- poolData->owner = owner;
- poolData->igbp = igbp;
- }
- if (poolData->held) {
- poolData->held = true;
- return false;
- }
- poolData->held = true;
- return true;
+ return poolData->holdBlockFromBufferQueue(owner, igbp, syncMem);
}
bool _C2BlockFactory::BeginTransferBlockToClient(
const std::shared_ptr<_C2BlockPoolData>& data) {
const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
- std::scoped_lock<std::mutex> lock(poolData->lock);
- poolData->transfer = true;
- return true;
+ return poolData->beginTransferBlockToClient();
}
bool _C2BlockFactory::EndTransferBlockToClient(
@@ -145,28 +90,14 @@
bool transfer) {
const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
- std::scoped_lock<std::mutex> lock(poolData->lock);
- poolData->transfer = false;
- if (transfer) {
- poolData->held = false;
- }
- return true;
+ return poolData->endTransferBlockToClient(transfer);
}
bool _C2BlockFactory::BeginAttachBlockToBufferQueue(
const std::shared_ptr<_C2BlockPoolData>& data) {
const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
- std::scoped_lock<std::mutex> lock(poolData->lock);
- if (poolData->local || poolData->display ||
- poolData->attach || !poolData->held) {
- return false;
- }
- if (poolData->bqId == 0) {
- return false;
- }
- poolData->attach = true;
- return true;
+ return poolData->beginAttachBlockToBufferQueue();
}
// if display was tried during attach, buffer should be retired ASAP.
@@ -174,47 +105,20 @@
const std::shared_ptr<_C2BlockPoolData>& data,
const std::shared_ptr<int>& owner,
const sp<HGraphicBufferProducer>& igbp,
+ std::shared_ptr<C2SurfaceSyncMemory> syncMem,
uint32_t generation,
uint64_t bqId,
int32_t bqSlot) {
const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
- std::scoped_lock<std::mutex> lock(poolData->lock);
- if (poolData->local || !poolData->attach ) {
- return false;
- }
- if (poolData->display) {
- poolData->attach = false;
- poolData->held = false;
- return false;
- }
- poolData->attach = false;
- poolData->held = true;
- poolData->owner = owner;
- poolData->igbp = igbp;
- poolData->generation = generation;
- poolData->bqId = bqId;
- poolData->bqSlot = bqSlot;
- return true;
+ return poolData->endAttachBlockToBufferQueue(owner, igbp, syncMem, generation, bqId, bqSlot);
}
bool _C2BlockFactory::DisplayBlockToBufferQueue(
const std::shared_ptr<_C2BlockPoolData>& data) {
const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
- std::scoped_lock<std::mutex> lock(poolData->lock);
- if (poolData->local || poolData->display || !poolData->held) {
- return false;
- }
- if (poolData->bqId == 0) {
- return false;
- }
- poolData->display = true;
- if (poolData->attach) {
- return false;
- }
- poolData->held = false;
- return true;
+ return poolData->displayBlockToBufferQueue();
}
std::shared_ptr<C2GraphicBlock> _C2BlockFactory::CreateGraphicBlock(
@@ -267,8 +171,8 @@
return stamp;
}
-bool getGenerationNumber(const sp<HGraphicBufferProducer> &producer,
- uint32_t *generation) {
+bool getGenerationNumberAndUsage(const sp<HGraphicBufferProducer> &producer,
+ uint32_t *generation, uint64_t *usage) {
status_t status{};
int slot{};
bool bufferNeedsReallocation{};
@@ -302,7 +206,7 @@
// instead of a new allocation.
transResult = producer->requestBuffer(
slot,
- [&status, &slotBuffer, &generation](
+ [&status, &slotBuffer, &generation, &usage](
HStatus hStatus,
HBuffer const& hBuffer,
uint32_t generationNumber){
@@ -310,6 +214,7 @@
h2b(hBuffer, &slotBuffer) &&
slotBuffer) {
*generation = generationNumber;
+ *usage = slotBuffer->getUsage();
slotBuffer->setGenerationNumber(generationNumber);
} else {
status = android::BAD_VALUE;
@@ -330,12 +235,58 @@
class C2BufferQueueBlockPool::Impl
: public std::enable_shared_from_this<C2BufferQueueBlockPool::Impl> {
private:
+ c2_status_t dequeueBuffer(
+ uint32_t width,
+ uint32_t height,
+ uint32_t format,
+ C2AndroidMemoryUsage androidUsage,
+ int *slot, bool *needsRealloc, sp<Fence> *fence) {
+ status_t status{};
+ using Input = HGraphicBufferProducer::DequeueBufferInput;
+ using Output = HGraphicBufferProducer::DequeueBufferOutput;
+ Return<void> transResult = mProducer->dequeueBuffer(
+ Input{
+ width,
+ height,
+ format,
+ androidUsage.asGrallocUsage()},
+ [&status, slot, needsRealloc,
+ fence](HStatus hStatus,
+ int32_t hSlot,
+ Output const& hOutput) {
+ *slot = static_cast<int>(hSlot);
+ if (!h2b(hStatus, &status) ||
+ !h2b(hOutput.fence, fence)) {
+ status = ::android::BAD_VALUE;
+ } else {
+ *needsRealloc =
+ hOutput.bufferNeedsReallocation;
+ }
+ });
+ if (!transResult.isOk() || status != android::OK) {
+ if (transResult.isOk()) {
+ ++mDqFailure;
+ if (status == android::INVALID_OPERATION ||
+ status == android::TIMED_OUT ||
+ status == android::WOULD_BLOCK) {
+ // Dequeue buffer is blocked temporarily. Retrying is
+ // required.
+ return C2_BLOCKING;
+ }
+ }
+ ALOGD("cannot dequeue buffer %d", status);
+ return C2_BAD_VALUE;
+ }
+ return C2_OK;
+ }
+
c2_status_t fetchFromIgbp_l(
uint32_t width,
uint32_t height,
uint32_t format,
C2MemoryUsage usage,
- std::shared_ptr<C2GraphicBlock> *block /* nonnull */) {
+ std::shared_ptr<C2GraphicBlock> *block /* nonnull */,
+ C2Fence *c2Fence) {
// We have an IGBP now.
C2AndroidMemoryUsage androidUsage = usage;
status_t status{};
@@ -344,41 +295,42 @@
sp<Fence> fence = new Fence();
ALOGV("tries to dequeue buffer");
+ C2SyncVariables *syncVar = mSyncMem ? mSyncMem->mem(): nullptr;
{ // Call dequeueBuffer().
- using Input = HGraphicBufferProducer::DequeueBufferInput;
- using Output = HGraphicBufferProducer::DequeueBufferOutput;
- Return<void> transResult = mProducer->dequeueBuffer(
- Input{
- width,
- height,
- format,
- androidUsage.asGrallocUsage()},
- [&status, &slot, &bufferNeedsReallocation,
- &fence](HStatus hStatus,
- int32_t hSlot,
- Output const& hOutput) {
- slot = static_cast<int>(hSlot);
- if (!h2b(hStatus, &status) ||
- !h2b(hOutput.fence, &fence)) {
- status = ::android::BAD_VALUE;
- } else {
- bufferNeedsReallocation =
- hOutput.bufferNeedsReallocation;
- }
- });
- if (!transResult.isOk() || status != android::OK) {
- if (transResult.isOk()) {
- ++mDqFailure;
- if (status == android::INVALID_OPERATION ||
- status == android::TIMED_OUT ||
- status == android::WOULD_BLOCK) {
- // Dequeue buffer is blocked temporarily. Retrying is
- // required.
- return C2_BLOCKING;
+ c2_status_t c2Status;
+ if (syncVar) {
+ uint32_t waitId;
+ syncVar->lock();
+ if (!syncVar->isDequeueableLocked(&waitId)) {
+ syncVar->unlock();
+ if (c2Fence) {
+ *c2Fence = _C2FenceFactory::CreateSurfaceFence(mSyncMem, waitId);
}
+ return C2_BLOCKING;
}
- ALOGD("cannot dequeue buffer %d", status);
- return C2_BAD_VALUE;
+ if (syncVar->getSyncStatusLocked() != C2SyncVariables::STATUS_ACTIVE) {
+ waitId = syncVar->getWaitIdLocked();
+ syncVar->unlock();
+ if (c2Fence) {
+ *c2Fence = _C2FenceFactory::CreateSurfaceFence(mSyncMem, waitId);
+ }
+ return C2_BLOCKING;
+ }
+ syncVar->notifyDequeuedLocked();
+ syncVar->unlock();
+ c2Status = dequeueBuffer(width, height, format, androidUsage,
+ &slot, &bufferNeedsReallocation, &fence);
+ if (c2Status != C2_OK) {
+ syncVar->lock();
+ syncVar->notifyQueuedLocked();
+ syncVar->unlock();
+ }
+ } else {
+ c2Status = dequeueBuffer(width, height, format, usage,
+ &slot, &bufferNeedsReallocation, &fence);
+ }
+ if (c2Status != C2_OK) {
+ return c2Status;
}
mDqFailure = 0;
mLastDqTs = getTimestampNow();
@@ -389,18 +341,41 @@
return C2_BAD_VALUE;
}
ALOGV("dequeued a buffer successfully");
+ bool dequeueable = false;
+ uint32_t waitId;
if (fence) {
static constexpr int kFenceWaitTimeMs = 10;
status_t status = fence->wait(kFenceWaitTimeMs);
if (status == -ETIME) {
// fence is not signalled yet.
- (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+ if (syncVar) {
+ syncVar->lock();
+ (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+ dequeueable = syncVar->notifyQueuedLocked(&waitId);
+ syncVar->unlock();
+ if (c2Fence) {
+ *c2Fence = dequeueable ? C2Fence() :
+ _C2FenceFactory::CreateSurfaceFence(mSyncMem, waitId);
+ }
+ } else {
+ (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+ }
return C2_BLOCKING;
}
if (status != android::NO_ERROR) {
ALOGD("buffer fence wait error %d", status);
- (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+ if (syncVar) {
+ syncVar->lock();
+ (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+ syncVar->notifyQueuedLocked();
+ syncVar->unlock();
+ if (c2Fence) {
+ *c2Fence = C2Fence();
+ }
+ } else {
+ (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+ }
return C2_BAD_VALUE;
} else if (mRenderCallback) {
nsecs_t signalTime = fence->getSignalTime();
@@ -440,7 +415,17 @@
return C2_BAD_VALUE;
} else if (status != android::NO_ERROR) {
slotBuffer.clear();
- (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+ if (syncVar) {
+ syncVar->lock();
+ (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+ syncVar->notifyQueuedLocked();
+ syncVar->unlock();
+ if (c2Fence) {
+ *c2Fence = C2Fence();
+ }
+ } else {
+ (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+ }
return C2_BAD_VALUE;
}
if (mGeneration == 0) {
@@ -463,20 +448,36 @@
std::shared_ptr<C2GraphicAllocation> alloc;
c2_status_t err = mAllocator->priorGraphicAllocation(c2Handle, &alloc);
if (err != C2_OK) {
+ native_handle_close(c2Handle);
+ native_handle_delete(c2Handle);
return err;
}
std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::make_shared<C2BufferQueueBlockPoolData>(
slotBuffer->getGenerationNumber(),
mProducerId, slot,
- shared_from_this());
+ mProducer, mSyncMem, 0);
mPoolDatas[slot] = poolData;
*block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
return C2_OK;
}
// Block was not created. call requestBuffer# again next time.
slotBuffer.clear();
- (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+ if (syncVar) {
+ syncVar->lock();
+ (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+ syncVar->notifyQueuedLocked();
+ syncVar->unlock();
+ if (c2Fence) {
+ *c2Fence = C2Fence();
+ }
+ } else {
+ (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+ }
+ return C2_BAD_VALUE;
+ }
+ if (c2Fence) {
+ *c2Fence = C2Fence();
}
return C2_BAD_VALUE;
}
@@ -506,7 +507,8 @@
uint32_t height,
uint32_t format,
C2MemoryUsage usage,
- std::shared_ptr<C2GraphicBlock> *block /* nonnull */) {
+ std::shared_ptr<C2GraphicBlock> *block /* nonnull */,
+ C2Fence *fence) {
block->reset();
if (mInit != C2_OK) {
return mInit;
@@ -537,17 +539,19 @@
}
std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::make_shared<C2BufferQueueBlockPoolData>(
- 0, (uint64_t)0, ~0, shared_from_this());
+ 0, (uint64_t)0, ~0, nullptr, nullptr, 0);
*block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
ALOGV("allocated a buffer successfully");
return C2_OK;
}
- c2_status_t status = fetchFromIgbp_l(width, height, format, usage, block);
+ c2_status_t status = fetchFromIgbp_l(width, height, format, usage, block, fence);
if (status == C2_BLOCKING) {
lock.unlock();
- // in order not to drain cpu from component's spinning
- ::usleep(kMaxIgbpRetryDelayUs);
+ if (!fence) {
+ // in order not to drain cpu from component's spinning
+ ::usleep(kMaxIgbpRetryDelayUs);
+ }
}
return status;
}
@@ -557,10 +561,12 @@
mRenderCallback = renderCallback;
}
+ /* This is for Old HAL request for compatibility */
void configureProducer(const sp<HGraphicBufferProducer> &producer) {
uint64_t producerId = 0;
uint32_t generation = 0;
- bool haveGeneration = false;
+ uint64_t usage = 0;
+ bool bqInformation = false;
if (producer) {
Return<uint64_t> transResult = producer->getUniqueId();
if (!transResult.isOk()) {
@@ -568,14 +574,32 @@
return;
}
producerId = static_cast<uint64_t>(transResult);
- // TODO: provide gneration number from parameter.
- haveGeneration = getGenerationNumber(producer, &generation);
- if (!haveGeneration) {
+ bqInformation = getGenerationNumberAndUsage(producer, &generation, &usage);
+ if (!bqInformation) {
ALOGW("get generationNumber failed %llu",
(unsigned long long)producerId);
}
}
+ configureProducer(producer, nullptr, producerId, generation, usage, bqInformation);
+ }
+
+ void configureProducer(const sp<HGraphicBufferProducer> &producer,
+ native_handle_t *syncHandle,
+ uint64_t producerId,
+ uint32_t generation,
+ uint64_t usage,
+ bool bqInformation) {
+ std::shared_ptr<C2SurfaceSyncMemory> c2SyncMem;
+ if (syncHandle) {
+ if (!producer) {
+ native_handle_close(syncHandle);
+ native_handle_delete(syncHandle);
+ } else {
+ c2SyncMem = C2SurfaceSyncMemory::Import(syncHandle);
+ }
+ }
int migrated = 0;
+ std::shared_ptr<C2SurfaceSyncMemory> oldMem;
// poolDatas dtor should not be called during lock is held.
std::shared_ptr<C2BufferQueueBlockPoolData>
poolDatas[NUM_BUFFER_SLOTS];
@@ -595,22 +619,30 @@
if (producer) {
mProducer = producer;
mProducerId = producerId;
- mGeneration = haveGeneration ? generation : 0;
+ mGeneration = bqInformation ? generation : 0;
} else {
mProducer = nullptr;
mProducerId = 0;
mGeneration = 0;
ALOGW("invalid producer producer(%d), generation(%d)",
- (bool)producer, haveGeneration);
+ (bool)producer, bqInformation);
}
- if (mProducer && haveGeneration) { // migrate buffers
+ oldMem = mSyncMem; // preven destruction while locked.
+ mSyncMem = c2SyncMem;
+ C2SyncVariables *syncVar = mSyncMem ? mSyncMem->mem() : nullptr;
+ if (syncVar) {
+ syncVar->lock();
+ syncVar->setSyncStatusLocked(C2SyncVariables::STATUS_ACTIVE);
+ syncVar->unlock();
+ }
+ if (mProducer && bqInformation) { // migrate buffers
for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
std::shared_ptr<C2BufferQueueBlockPoolData> data =
mPoolDatas[i].lock();
if (data) {
int slot = data->migrate(
- mProducer, generation,
- producerId, mBuffers, oldGeneration);
+ mProducer, generation, usage,
+ producerId, mBuffers[i], oldGeneration, mSyncMem);
if (slot >= 0) {
buffers[slot] = mBuffers[i];
poolDatas[slot] = data;
@@ -624,7 +656,7 @@
mPoolDatas[i] = poolDatas[i];
}
}
- if (producer && haveGeneration) {
+ if (producer && bqInformation) {
ALOGD("local generation change %u , "
"bqId: %llu migrated buffers # %d",
generation, (unsigned long long)producerId, migrated);
@@ -634,17 +666,6 @@
private:
friend struct C2BufferQueueBlockPoolData;
- void cancel(uint32_t generation, uint64_t igbp_id, int32_t igbp_slot) {
- bool cancelled = false;
- {
- std::scoped_lock<std::mutex> lock(mMutex);
- if (generation == mGeneration && igbp_id == mProducerId && mProducer) {
- (void)mProducer->cancelBuffer(igbp_slot, hidl_handle{}).isOk();
- cancelled = true;
- }
- }
- }
-
c2_status_t mInit;
uint64_t mProducerId;
uint32_t mGeneration;
@@ -662,71 +683,123 @@
sp<GraphicBuffer> mBuffers[NUM_BUFFER_SLOTS];
std::weak_ptr<C2BufferQueueBlockPoolData> mPoolDatas[NUM_BUFFER_SLOTS];
+
+ std::shared_ptr<C2SurfaceSyncMemory> mSyncMem;
};
C2BufferQueueBlockPoolData::C2BufferQueueBlockPoolData(
uint32_t generation, uint64_t bqId, int32_t bqSlot,
const std::shared_ptr<int>& owner,
const sp<HGraphicBufferProducer>& producer) :
- held(producer && bqId != 0), local(false),
- generation(generation), bqId(bqId), bqSlot(bqSlot),
- transfer(false), attach(false), display(false),
- owner(owner), igbp(producer),
- localPool() {
+ mLocal(false), mHeld(producer && bqId != 0),
+ mGeneration(generation), mBqId(bqId), mBqSlot(bqSlot),
+ mCurrentGeneration(generation), mCurrentBqId(bqId),
+ mTransfer(false), mAttach(false), mDisplay(false),
+ mOwner(owner), mIgbp(producer) {
}
C2BufferQueueBlockPoolData::C2BufferQueueBlockPoolData(
uint32_t generation, uint64_t bqId, int32_t bqSlot,
- const std::shared_ptr<C2BufferQueueBlockPool::Impl>& pool) :
- held(true), local(true),
- generation(generation), bqId(bqId), bqSlot(bqSlot),
- transfer(false), attach(false), display(false),
- igbp(pool ? pool->mProducer : nullptr),
- localPool(pool) {
+ const android::sp<HGraphicBufferProducer>& producer,
+ std::shared_ptr<C2SurfaceSyncMemory> syncMem, int noUse) :
+ mLocal(true), mHeld(true),
+ mGeneration(generation), mBqId(bqId), mBqSlot(bqSlot),
+ mCurrentGeneration(generation), mCurrentBqId(bqId),
+ mTransfer(false), mAttach(false), mDisplay(false),
+ mIgbp(producer), mSyncMem(syncMem) {
+ (void)noUse;
}
C2BufferQueueBlockPoolData::~C2BufferQueueBlockPoolData() {
- if (!held || bqId == 0) {
+ if (!mHeld || mBqId == 0 || !mIgbp) {
return;
}
- if (local) {
- if (localPool) {
- localPool->cancel(generation, bqId, bqSlot);
+
+ if (mLocal) {
+ if (mGeneration == mCurrentGeneration && mBqId == mCurrentBqId) {
+ C2SyncVariables *syncVar = mSyncMem ? mSyncMem->mem() : nullptr;
+ if (syncVar) {
+ syncVar->lock();
+ if (syncVar->getSyncStatusLocked() == C2SyncVariables::STATUS_ACTIVE) {
+ mIgbp->cancelBuffer(mBqSlot, hidl_handle{}).isOk();
+ syncVar->notifyQueuedLocked();
+ }
+ syncVar->unlock();
+ } else {
+ mIgbp->cancelBuffer(mBqSlot, hidl_handle{}).isOk();
+ }
}
- } else if (igbp && !owner.expired()) {
- igbp->cancelBuffer(bqSlot, hidl_handle{}).isOk();
+ } else if (!mOwner.expired()) {
+ C2SyncVariables *syncVar = mSyncMem ? mSyncMem->mem() : nullptr;
+ if (syncVar) {
+ syncVar->lock();
+ if (syncVar->getSyncStatusLocked() != C2SyncVariables::STATUS_SWITCHING) {
+ mIgbp->cancelBuffer(mBqSlot, hidl_handle{}).isOk();
+ syncVar->notifyQueuedLocked();
+ }
+ syncVar->unlock();
+ } else {
+ mIgbp->cancelBuffer(mBqSlot, hidl_handle{}).isOk();
+ }
}
}
+
+C2BufferQueueBlockPoolData::type_t C2BufferQueueBlockPoolData::getType() const {
+ return TYPE_BUFFERQUEUE;
+}
+
int C2BufferQueueBlockPoolData::migrate(
const sp<HGraphicBufferProducer>& producer,
- uint32_t toGeneration, uint64_t toBqId,
- sp<GraphicBuffer> *buffers, uint32_t oldGeneration) {
- std::scoped_lock<std::mutex> l(lock);
- if (!held || bqId == 0) {
+ uint32_t toGeneration, uint64_t toUsage, uint64_t toBqId,
+ sp<GraphicBuffer>& graphicBuffer, uint32_t oldGeneration,
+ std::shared_ptr<C2SurfaceSyncMemory> syncMem) {
+ std::scoped_lock<std::mutex> l(mLock);
+
+ mCurrentBqId = toBqId;
+ mCurrentGeneration = toGeneration;
+
+ if (!mHeld || mBqId == 0) {
ALOGV("buffer is not owned");
return -1;
}
- if (!local || !localPool) {
+ if (!mLocal) {
ALOGV("pool is not local");
return -1;
}
- if (bqSlot < 0 || bqSlot >= NUM_BUFFER_SLOTS || !buffers[bqSlot]) {
+ if (mBqSlot < 0 || mBqSlot >= NUM_BUFFER_SLOTS) {
ALOGV("slot is not in effect");
return -1;
}
- if (toGeneration == generation && bqId == toBqId) {
+ if (!graphicBuffer) {
+ ALOGV("buffer is null");
+ return -1;
+ }
+ if (toGeneration == mGeneration && mBqId == toBqId) {
ALOGV("cannot migrate to same bufferqueue");
return -1;
}
- if (oldGeneration != generation) {
+ if (oldGeneration != mGeneration) {
ALOGV("cannot migrate stale buffer");
+ return -1;
}
- if (transfer) {
+ if (mTransfer) {
// either transferred or detached.
ALOGV("buffer is in transfer");
return -1;
}
- sp<GraphicBuffer> const& graphicBuffer = buffers[bqSlot];
+
+ if (toUsage != graphicBuffer->getUsage()) {
+ sp<GraphicBuffer> newBuffer = new GraphicBuffer(
+ graphicBuffer->handle, GraphicBuffer::CLONE_HANDLE,
+ graphicBuffer->width, graphicBuffer->height, graphicBuffer->format,
+ graphicBuffer->layerCount, toUsage | graphicBuffer->getUsage(), graphicBuffer->stride);
+ if (newBuffer->initCheck() == android::NO_ERROR) {
+ graphicBuffer = std::move(newBuffer);
+ } else {
+ ALOGW("%s() failed to update usage, original usage=%" PRIx64 ", toUsage=%" PRIx64,
+ __func__, graphicBuffer->getUsage(), toUsage);
+ }
+ }
graphicBuffer->setGenerationNumber(toGeneration);
HBuffer hBuffer{};
@@ -755,13 +828,124 @@
return -1;
}
ALOGV("local migration from gen %u : %u slot %d : %d",
- generation, toGeneration, bqSlot, slot);
- generation = toGeneration;
- bqId = toBqId;
- bqSlot = slot;
+ mGeneration, toGeneration, mBqSlot, slot);
+ mIgbp = producer;
+ mGeneration = toGeneration;
+ mBqId = toBqId;
+ mBqSlot = slot;
+ mSyncMem = syncMem;
+
+ C2SyncVariables *syncVar = syncMem ? syncMem->mem() : nullptr;
+ if (syncVar) {
+ syncVar->lock();
+ syncVar->notifyDequeuedLocked();
+ syncVar->unlock();
+ }
return slot;
}
+void C2BufferQueueBlockPoolData::getBufferQueueData(
+ uint32_t* generation, uint64_t* bqId, int32_t* bqSlot) const {
+ if (generation) {
+ std::scoped_lock<std::mutex> lock(mLock);
+ *generation = mGeneration;
+ if (bqId) {
+ *bqId = mBqId;
+ }
+ if (bqSlot) {
+ *bqSlot = mBqSlot;
+ }
+ }
+}
+
+bool C2BufferQueueBlockPoolData::holdBlockFromBufferQueue(
+ const std::shared_ptr<int>& owner,
+ const sp<HGraphicBufferProducer>& igbp,
+ std::shared_ptr<C2SurfaceSyncMemory> syncMem) {
+ std::scoped_lock<std::mutex> lock(mLock);
+ if (!mLocal) {
+ mOwner = owner;
+ mIgbp = igbp;
+ mSyncMem = syncMem;
+ }
+ if (mHeld) {
+ return false;
+ }
+ mHeld = true;
+ return true;
+}
+
+bool C2BufferQueueBlockPoolData::beginTransferBlockToClient() {
+ std::scoped_lock<std::mutex> lock(mLock);
+ mTransfer = true;
+ return true;
+}
+
+bool C2BufferQueueBlockPoolData::endTransferBlockToClient(bool transfer) {
+ std::scoped_lock<std::mutex> lock(mLock);
+ mTransfer = false;
+ if (transfer) {
+ mHeld = false;
+ }
+ return true;
+}
+
+bool C2BufferQueueBlockPoolData::beginAttachBlockToBufferQueue() {
+ std::scoped_lock<std::mutex> lock(mLock);
+ if (mLocal || mDisplay ||
+ mAttach || !mHeld) {
+ return false;
+ }
+ if (mBqId == 0) {
+ return false;
+ }
+ mAttach = true;
+ return true;
+}
+
+bool C2BufferQueueBlockPoolData::endAttachBlockToBufferQueue(
+ const std::shared_ptr<int>& owner,
+ const sp<HGraphicBufferProducer>& igbp,
+ std::shared_ptr<C2SurfaceSyncMemory> syncMem,
+ uint32_t generation,
+ uint64_t bqId,
+ int32_t bqSlot) {
+ std::scoped_lock<std::mutex> lock(mLock);
+ if (mLocal || !mAttach) {
+ return false;
+ }
+ if (mDisplay) {
+ mAttach = false;
+ mHeld = false;
+ return false;
+ }
+ mAttach = false;
+ mHeld = true;
+ mOwner = owner;
+ mIgbp = igbp;
+ mSyncMem = syncMem;
+ mGeneration = generation;
+ mBqId = bqId;
+ mBqSlot = bqSlot;
+ return true;
+}
+
+bool C2BufferQueueBlockPoolData::displayBlockToBufferQueue() {
+ std::scoped_lock<std::mutex> lock(mLock);
+ if (mLocal || mDisplay || !mHeld) {
+ return false;
+ }
+ if (mBqId == 0) {
+ return false;
+ }
+ mDisplay = true;
+ if (mAttach) {
+ return false;
+ }
+ mHeld = false;
+ return true;
+}
+
C2BufferQueueBlockPool::C2BufferQueueBlockPool(
const std::shared_ptr<C2Allocator> &allocator, const local_id_t localId)
: mAllocator(allocator), mLocalId(localId), mImpl(new Impl(allocator)) {}
@@ -775,7 +959,20 @@
C2MemoryUsage usage,
std::shared_ptr<C2GraphicBlock> *block /* nonnull */) {
if (mImpl) {
- return mImpl->fetchGraphicBlock(width, height, format, usage, block);
+ return mImpl->fetchGraphicBlock(width, height, format, usage, block, nullptr);
+ }
+ return C2_CORRUPTED;
+}
+
+c2_status_t C2BufferQueueBlockPool::fetchGraphicBlock(
+ uint32_t width,
+ uint32_t height,
+ uint32_t format,
+ C2MemoryUsage usage,
+ std::shared_ptr<C2GraphicBlock> *block /* nonnull */,
+ C2Fence *fence /* nonnull */) {
+ if (mImpl) {
+ return mImpl->fetchGraphicBlock(width, height, format, usage, block, fence);
}
return C2_CORRUPTED;
}
@@ -786,6 +983,18 @@
}
}
+void C2BufferQueueBlockPool::configureProducer(
+ const sp<HGraphicBufferProducer> &producer,
+ native_handle_t *syncMemory,
+ uint64_t bqId,
+ uint32_t generationId,
+ uint64_t consumerUsage) {
+ if (mImpl) {
+ mImpl->configureProducer(
+ producer, syncMemory, bqId, generationId, consumerUsage, true);
+ }
+}
+
void C2BufferQueueBlockPool::setRenderCallback(const OnRenderCallback &renderCallback) {
if (mImpl) {
mImpl->setRenderCallback(renderCallback);
diff --git a/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp b/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp
new file mode 100644
index 0000000..e55bdc0
--- /dev/null
+++ b/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2SurfaceSyncObj"
+#include <limits.h>
+#include <linux/futex.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <utils/Log.h>
+
+#include <chrono>
+#include <C2SurfaceSyncObj.h>
+
+const native_handle_t C2SurfaceSyncMemory::HandleSyncMem::cHeader = {
+ C2SurfaceSyncMemory::HandleSyncMem::version,
+ C2SurfaceSyncMemory::HandleSyncMem::numFds,
+ C2SurfaceSyncMemory::HandleSyncMem::numInts,
+ {}
+};
+
+bool C2SurfaceSyncMemory::HandleSyncMem::isValid(const native_handle_t * const o) {
+ if (!o || memcmp(o, &cHeader, sizeof(cHeader))) {
+ return false;
+ }
+
+ const HandleSyncMem *other = static_cast<const HandleSyncMem*>(o);
+ return other->mInts.mMagic == kMagic;
+}
+
+C2SurfaceSyncMemory::C2SurfaceSyncMemory()
+ : mInit(false), mHandle(nullptr), mMem(nullptr) {}
+
+C2SurfaceSyncMemory::~C2SurfaceSyncMemory() {
+ if (mInit) {
+ if (mMem) {
+ munmap(static_cast<void *>(mMem), mHandle->size());
+ }
+ if (mHandle) {
+ native_handle_close(mHandle);
+ native_handle_delete(mHandle);
+ }
+ }
+}
+
+std::shared_ptr<C2SurfaceSyncMemory> C2SurfaceSyncMemory::Import(
+ native_handle_t *handle) {
+ if (!HandleSyncMem::isValid(handle)) {
+ return nullptr;
+ }
+
+ HandleSyncMem *o = static_cast<HandleSyncMem*>(handle);
+ void *ptr = mmap(NULL, o->size(), PROT_READ | PROT_WRITE, MAP_SHARED, o->memFd(), 0);
+
+ if (ptr == MAP_FAILED) {
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ return nullptr;
+ }
+
+ std::shared_ptr<C2SurfaceSyncMemory> syncMem(new C2SurfaceSyncMemory);
+ syncMem->mInit = true;
+ syncMem->mHandle = o;
+ syncMem->mMem = static_cast<C2SyncVariables*>(ptr);
+ return syncMem;
+}
+
+std::shared_ptr<C2SurfaceSyncMemory> C2SurfaceSyncMemory::Create(int fd, size_t size) {
+ if (fd < 0 || size == 0) {
+ return nullptr;
+ }
+ HandleSyncMem *handle = new HandleSyncMem(fd, size);
+
+ void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (ptr == MAP_FAILED) {
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ return nullptr;
+ }
+ memset(ptr, 0, size);
+
+ std::shared_ptr<C2SurfaceSyncMemory> syncMem(new C2SurfaceSyncMemory);
+ syncMem->mInit = true;
+ syncMem->mHandle = handle;
+ syncMem->mMem = static_cast<C2SyncVariables*>(ptr);
+ return syncMem;
+}
+
+native_handle_t *C2SurfaceSyncMemory::handle() {
+ return !mInit ? nullptr : mHandle;
+}
+
+C2SyncVariables *C2SurfaceSyncMemory::mem() {
+ return !mInit ? nullptr : mMem;
+}
+
+namespace {
+ constexpr int kSpinNumForLock = 100;
+ constexpr int kSpinNumForUnlock = 200;
+
+ enum : uint32_t {
+ FUTEX_UNLOCKED = 0,
+ FUTEX_LOCKED_UNCONTENDED = 1, // user-space locking
+ FUTEX_LOCKED_CONTENDED = 2, // futex locking
+ };
+}
+
+int C2SyncVariables::lock() {
+ uint32_t old;
+ for (int i = 0; i < kSpinNumForLock; i++) {
+ old = 0;
+ if (mLock.compare_exchange_strong(old, FUTEX_LOCKED_UNCONTENDED)) {
+ return 0;
+ }
+ sched_yield();
+ }
+
+ if (old == FUTEX_LOCKED_UNCONTENDED)
+ old = mLock.exchange(FUTEX_LOCKED_CONTENDED);
+
+ while (old) {
+ (void) syscall(__NR_futex, &mLock, FUTEX_WAIT, FUTEX_LOCKED_CONTENDED, NULL, NULL, 0);
+ old = mLock.exchange(FUTEX_LOCKED_CONTENDED);
+ }
+ return 0;
+}
+
+int C2SyncVariables::unlock() {
+ if (mLock.exchange(FUTEX_UNLOCKED) == FUTEX_LOCKED_UNCONTENDED) return 0;
+
+ for (int i = 0; i < kSpinNumForUnlock; i++) {
+ if (mLock.load()) {
+ uint32_t old = FUTEX_LOCKED_UNCONTENDED;
+ mLock.compare_exchange_strong(old, FUTEX_LOCKED_CONTENDED);
+ if (old) {
+ return 0;
+ }
+ }
+ sched_yield();
+ }
+
+ (void) syscall(__NR_futex, &mLock, FUTEX_WAKE, 1, NULL, NULL, 0);
+ return 0;
+}
+
+void C2SyncVariables::setInitialDequeueCountLocked(
+ int32_t maxDequeueCount, int32_t curDequeueCount) {
+ mMaxDequeueCount = maxDequeueCount;
+ mCurDequeueCount = curDequeueCount;
+}
+
+uint32_t C2SyncVariables::getWaitIdLocked() {
+ return mCond.load();
+}
+
+bool C2SyncVariables::isDequeueableLocked(uint32_t *waitId) {
+ if (mMaxDequeueCount <= mCurDequeueCount) {
+ if (waitId) {
+ *waitId = getWaitIdLocked();
+ }
+ return false;
+ }
+ return true;
+}
+
+bool C2SyncVariables::notifyQueuedLocked(uint32_t *waitId) {
+ // Note. thundering herds may occur. Edge trigged signalling.
+ // But one waiter will guarantee to dequeue. others may wait again.
+ // Minimize futex syscall(trap) for the main use case(one waiter case).
+ if (mMaxDequeueCount == mCurDequeueCount--) {
+ broadcast();
+ return true;
+ }
+
+ if (mCurDequeueCount >= mMaxDequeueCount) {
+ if (waitId) {
+ *waitId = getWaitIdLocked();
+ }
+ ALOGV("dequeue blocked %d/%d", mCurDequeueCount, mMaxDequeueCount);
+ return false;
+ }
+ return true;
+}
+
+void C2SyncVariables::notifyDequeuedLocked() {
+ mCurDequeueCount++;
+ ALOGV("dequeue successful %d/%d", mCurDequeueCount, mMaxDequeueCount);
+}
+
+void C2SyncVariables::setSyncStatusLocked(SyncStatus status) {
+ mStatus = status;
+ if (mStatus == STATUS_ACTIVE) {
+ broadcast();
+ }
+}
+
+C2SyncVariables::SyncStatus C2SyncVariables::getSyncStatusLocked() {
+ return mStatus;
+}
+
+void C2SyncVariables::updateMaxDequeueCountLocked(int32_t maxDequeueCount) {
+ mMaxDequeueCount = maxDequeueCount;
+ if (mStatus == STATUS_ACTIVE) {
+ broadcast();
+ }
+}
+
+c2_status_t C2SyncVariables::waitForChange(uint32_t waitId, c2_nsecs_t timeoutNs) {
+ if (timeoutNs < 0) {
+ timeoutNs = 0;
+ }
+ struct timespec tv;
+ tv.tv_sec = timeoutNs / 1000000000;
+ tv.tv_nsec = timeoutNs % 1000000000;
+
+ int ret = syscall(__NR_futex, &mCond, FUTEX_WAIT, waitId, &tv, NULL, 0);
+ if (ret == 0 || ret == EAGAIN) {
+ return C2_OK;
+ }
+ if (ret == EINTR || ret == ETIMEDOUT) {
+ return C2_TIMED_OUT;
+ }
+ return C2_BAD_VALUE;
+}
+
+int C2SyncVariables::signal() {
+ mCond++;
+
+ (void) syscall(__NR_futex, &mCond, FUTEX_WAKE, 1, NULL, NULL, 0);
+ return 0;
+}
+
+int C2SyncVariables::broadcast() {
+ mCond++;
+
+ (void) syscall(__NR_futex, &mCond, FUTEX_REQUEUE, 1, (void *)INT_MAX, &mLock, 0);
+ return 0;
+}
+
+int C2SyncVariables::wait() {
+ uint32_t old = mCond.load();
+ unlock();
+
+ (void) syscall(__NR_futex, &mCond, FUTEX_WAIT, old, NULL, NULL, 0);
+ while (mLock.exchange(FUTEX_LOCKED_CONTENDED)) {
+ (void) syscall(__NR_futex, &mLock, FUTEX_WAIT, FUTEX_LOCKED_CONTENDED, NULL, NULL, 0);
+ }
+ return 0;
+}
diff --git a/media/codecs/amrnb/enc/src/pitch_fr.cpp b/media/codecs/amrnb/enc/src/pitch_fr.cpp
index 5a846fa..584f79b 100644
--- a/media/codecs/amrnb/enc/src/pitch_fr.cpp
+++ b/media/codecs/amrnb/enc/src/pitch_fr.cpp
@@ -570,12 +570,14 @@
Word16 corr[], /* i : normalized correlation */
Word16 flag3, /* i : subsample resolution
(3: =1 / 6: =0) */
- Flag *pOverflow
+ Flag *pOverflow,
+ enum Mode mode
)
{
Word16 i;
Word16 max;
Word16 corr_int;
+ Word16 minPitch;
/* Test the fractions around T0 and choose the one which maximizes */
/* the interpolated normalized correlation. */
@@ -593,14 +595,22 @@
}
}
+ minPitch = (mode == MR122) ? PIT_MIN_MR122 : PIT_MIN;
if (flag3 == 0)
{
/* Limit the fraction value in the interval [-2,-1,0,1,2,3] */
if (*frac == -3)
{
- *frac = 3;
- (*lag)--;
+ if (*lag > minPitch)
+ {
+ *frac = 3;
+ (*lag)--;
+ }
+ else
+ {
+ *frac = -2;
+ }
}
}
else
@@ -609,13 +619,27 @@
if (*frac == -2)
{
- *frac = 1;
- (*lag)--;
+ if (*lag > minPitch)
+ {
+ *frac = 1;
+ (*lag)--;
+ }
+ else
+ {
+ *frac = -1;
+ }
}
- if (*frac == 2)
+ else if (*frac == 2)
{
- *frac = -1;
- (*lag)++;
+ if (*lag < PIT_MAX)
+ {
+ *frac = -1;
+ (*lag)++;
+ }
+ else
+ {
+ *frac = 1;
+ }
}
}
}
@@ -1533,20 +1557,20 @@
/* normal search in fractions around T0 */
- searchFrac(&lag, &frac, last_frac, corr, flag3, pOverflow);
+ searchFrac(&lag, &frac, last_frac, corr, flag3, pOverflow, mode);
}
else if (lag == (tmp_lag - 2))
{
/* limit search around T0 to the right side */
frac = 0;
- searchFrac(&lag, &frac, last_frac, corr, flag3, pOverflow);
+ searchFrac(&lag, &frac, last_frac, corr, flag3, pOverflow, mode);
}
else if (lag == (tmp_lag + 1))
{
/* limit search around T0 to the left side */
last_frac = 0;
- searchFrac(&lag, &frac, last_frac, corr, flag3, pOverflow);
+ searchFrac(&lag, &frac, last_frac, corr, flag3, pOverflow, mode);
}
else
{
@@ -1556,7 +1580,7 @@
}
else
/* test the fractions around T0 */
- searchFrac(&lag, &frac, last_frac, corr, flag3, pOverflow);
+ searchFrac(&lag, &frac, last_frac, corr, flag3, pOverflow, mode);
}
/*-----------------------------------------------------------------------*
diff --git a/media/codecs/amrwb/dec/test/Android.bp b/media/codecs/amrwb/dec/test/Android.bp
index cc120d3..7d0c964 100644
--- a/media/codecs/amrwb/dec/test/Android.bp
+++ b/media/codecs/amrwb/dec/test/Android.bp
@@ -28,6 +28,7 @@
cc_test {
name: "AmrwbDecoderTest",
+ test_suites: ["device-tests"],
gtest: true,
srcs: [
diff --git a/media/codecs/amrwb/enc/test/Android.bp b/media/codecs/amrwb/enc/test/Android.bp
index e4b7771..942f6c9 100644
--- a/media/codecs/amrwb/enc/test/Android.bp
+++ b/media/codecs/amrwb/enc/test/Android.bp
@@ -27,6 +27,7 @@
cc_test {
name: "AmrwbEncoderTest",
+ test_suites: ["device-tests"],
gtest: true,
srcs: [
diff --git a/media/codecs/g711/decoder/Android.bp b/media/codecs/g711/decoder/Android.bp
index 07f7ed4..0cd9740 100644
--- a/media/codecs/g711/decoder/Android.bp
+++ b/media/codecs/g711/decoder/Android.bp
@@ -44,7 +44,13 @@
],
cfi: true,
},
- apex_available: ["com.android.media.swcodec"],
+
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media.swcodec",
+ "test_com.android.media.swcodec",
+ ],
+
min_sdk_version: "29",
target: {
diff --git a/media/codecs/m4v_h263/enc/src/motion_est.cpp b/media/codecs/m4v_h263/enc/src/motion_est.cpp
index 997b78d..9deb023 100644
--- a/media/codecs/m4v_h263/enc/src/motion_est.cpp
+++ b/media/codecs/m4v_h263/enc/src/motion_est.cpp
@@ -1576,7 +1576,7 @@
/* find the last refresh MB */
indx = 0;
- while (intraArray[indx] == 1 && indx < totalMB)
+ while (indx < totalMB && intraArray[indx] == 1)
indx++;
/* add more */
diff --git a/media/codecs/m4v_h263/enc/src/mp4enc_api.cpp b/media/codecs/m4v_h263/enc/src/mp4enc_api.cpp
index 4555203..234faef 100644
--- a/media/codecs/m4v_h263/enc/src/mp4enc_api.cpp
+++ b/media/codecs/m4v_h263/enc/src/mp4enc_api.cpp
@@ -491,6 +491,9 @@
}
for (i = 0; i < encParams->nLayers; i++)
{
+ if (encOption->encHeight[i] == 0 || encOption->encWidth[i] == 0 ||
+ encOption->encHeight[i] % 16 != 0 || encOption->encWidth[i] % 16 != 0)
+ goto CLEAN_UP;
encParams->LayerHeight[i] = encOption->encHeight[i];
encParams->LayerWidth[i] = encOption->encWidth[i];
}
diff --git a/media/codecs/mp3dec/src/pvmp3_framedecoder.cpp b/media/codecs/mp3dec/src/pvmp3_framedecoder.cpp
index a5c7f5e..5cf1ed3 100644
--- a/media/codecs/mp3dec/src/pvmp3_framedecoder.cpp
+++ b/media/codecs/mp3dec/src/pvmp3_framedecoder.cpp
@@ -219,6 +219,11 @@
if (info->error_protection)
{
+ if (!bitsAvailable(&pVars->inputStream, 16))
+ {
+ return SIDE_INFO_ERROR;
+ }
+
/*
* Get crc content
*/
@@ -593,18 +598,10 @@
}
else
{
- int32 tmp1 = *(ptr++);
- for (int32 nBytes = temp >> 1; nBytes != 0; nBytes--) /* read main data. */
+ for (int32 nBytes = temp; nBytes != 0; nBytes--) /* read main data. */
{
- int32 tmp2 = *(ptr++);
- fillDataBuf(&pVars->mainDataStream, tmp1);
- fillDataBuf(&pVars->mainDataStream, tmp2);
- tmp1 = *(ptr++);
- }
-
- if (temp&1)
- {
- fillDataBuf(&pVars->mainDataStream, tmp1);
+ int32 tmp = *(ptr++);
+ fillDataBuf(&pVars->mainDataStream, tmp);
}
/* adjust circular buffer counter */
@@ -613,14 +610,9 @@
}
else
{
- for (int32 nBytes = temp >> 1; nBytes != 0; nBytes--) /* read main data. */
+ for (int32 nBytes = temp; nBytes != 0; nBytes--) /* read main data. */
{
fillDataBuf(&pVars->mainDataStream, *(pVars->inputStream.pBuffer + module(offset++ , BUFSIZE)));
- fillDataBuf(&pVars->mainDataStream, *(pVars->inputStream.pBuffer + module(offset++ , BUFSIZE)));
- }
- if (temp&1)
- {
- fillDataBuf(&pVars->mainDataStream, *(pVars->inputStream.pBuffer + module(offset , BUFSIZE)));
}
}
diff --git a/media/codecs/mp3dec/src/pvmp3_get_side_info.cpp b/media/codecs/mp3dec/src/pvmp3_get_side_info.cpp
index d644207..1a3fca5 100644
--- a/media/codecs/mp3dec/src/pvmp3_get_side_info.cpp
+++ b/media/codecs/mp3dec/src/pvmp3_get_side_info.cpp
@@ -73,6 +73,7 @@
#include "pvmp3_get_side_info.h"
#include "pvmp3_crc.h"
+#include "pvmp3_getbits.h"
/*----------------------------------------------------------------------------
@@ -125,12 +126,22 @@
{
if (stereo == 1)
{
+ if (!bitsAvailable(inputStream, 14))
+ {
+ return SIDE_INFO_ERROR;
+ }
+
tmp = getbits_crc(inputStream, 14, crc, info->error_protection);
si->main_data_begin = (tmp << 18) >> 23; /* 9 */
si->private_bits = (tmp << 27) >> 27; /* 5 */
}
else
{
+ if (!bitsAvailable(inputStream, 12))
+ {
+ return SIDE_INFO_ERROR;
+ }
+
tmp = getbits_crc(inputStream, 12, crc, info->error_protection);
si->main_data_begin = (tmp << 20) >> 23; /* 9 */
si->private_bits = (tmp << 29) >> 29; /* 3 */
@@ -139,6 +150,11 @@
for (ch = 0; ch < stereo; ch++)
{
+ if (!bitsAvailable(inputStream, 4))
+ {
+ return SIDE_INFO_ERROR;
+ }
+
tmp = getbits_crc(inputStream, 4, crc, info->error_protection);
si->ch[ch].scfsi[0] = (tmp << 28) >> 31; /* 1 */
si->ch[ch].scfsi[1] = (tmp << 29) >> 31; /* 1 */
@@ -150,6 +166,11 @@
{
for (ch = 0; ch < stereo; ch++)
{
+ if (!bitsAvailable(inputStream, 34))
+ {
+ return SIDE_INFO_ERROR;
+ }
+
si->ch[ch].gran[gr].part2_3_length = getbits_crc(inputStream, 12, crc, info->error_protection);
tmp = getbits_crc(inputStream, 22, crc, info->error_protection);
@@ -160,6 +181,11 @@
if (si->ch[ch].gran[gr].window_switching_flag)
{
+ if (!bitsAvailable(inputStream, 22))
+ {
+ return SIDE_INFO_ERROR;
+ }
+
tmp = getbits_crc(inputStream, 22, crc, info->error_protection);
si->ch[ch].gran[gr].block_type = (tmp << 10) >> 30; /* 2 */;
@@ -192,6 +218,11 @@
}
else
{
+ if (!bitsAvailable(inputStream, 22))
+ {
+ return SIDE_INFO_ERROR;
+ }
+
tmp = getbits_crc(inputStream, 22, crc, info->error_protection);
si->ch[ch].gran[gr].table_select[0] = (tmp << 10) >> 27; /* 5 */;
@@ -204,6 +235,11 @@
si->ch[ch].gran[gr].block_type = 0;
}
+ if (!bitsAvailable(inputStream, 3))
+ {
+ return SIDE_INFO_ERROR;
+ }
+
tmp = getbits_crc(inputStream, 3, crc, info->error_protection);
si->ch[ch].gran[gr].preflag = (tmp << 29) >> 31; /* 1 */
si->ch[ch].gran[gr].scalefac_scale = (tmp << 30) >> 31; /* 1 */
@@ -213,11 +249,21 @@
}
else /* Layer 3 LSF */
{
+ if (!bitsAvailable(inputStream, 8 + stereo))
+ {
+ return SIDE_INFO_ERROR;
+ }
+
si->main_data_begin = getbits_crc(inputStream, 8, crc, info->error_protection);
si->private_bits = getbits_crc(inputStream, stereo, crc, info->error_protection);
for (ch = 0; ch < stereo; ch++)
{
+ if (!bitsAvailable(inputStream, 39))
+ {
+ return SIDE_INFO_ERROR;
+ }
+
tmp = getbits_crc(inputStream, 21, crc, info->error_protection);
si->ch[ch].gran[0].part2_3_length = (tmp << 11) >> 20; /* 12 */
si->ch[ch].gran[0].big_values = (tmp << 23) >> 23; /* 9 */
@@ -230,6 +276,11 @@
if (si->ch[ch].gran[0].window_switching_flag)
{
+ if (!bitsAvailable(inputStream, 22))
+ {
+ return SIDE_INFO_ERROR;
+ }
+
tmp = getbits_crc(inputStream, 22, crc, info->error_protection);
si->ch[ch].gran[0].block_type = (tmp << 10) >> 30; /* 2 */;
@@ -262,6 +313,11 @@
}
else
{
+ if (!bitsAvailable(inputStream, 22))
+ {
+ return SIDE_INFO_ERROR;
+ }
+
tmp = getbits_crc(inputStream, 22, crc, info->error_protection);
si->ch[ch].gran[0].table_select[0] = (tmp << 10) >> 27; /* 5 */;
@@ -274,6 +330,11 @@
si->ch[ch].gran[0].block_type = 0;
}
+ if (!bitsAvailable(inputStream, 2))
+ {
+ return SIDE_INFO_ERROR;
+ }
+
tmp = getbits_crc(inputStream, 2, crc, info->error_protection);
si->ch[ch].gran[0].scalefac_scale = tmp >> 1; /* 1 */
si->ch[ch].gran[0].count1table_select = tmp & 1; /* 1 */
diff --git a/media/codecs/mp3dec/src/pvmp3_getbits.cpp b/media/codecs/mp3dec/src/pvmp3_getbits.cpp
index 8ff7953..4d252ef 100644
--- a/media/codecs/mp3dec/src/pvmp3_getbits.cpp
+++ b/media/codecs/mp3dec/src/pvmp3_getbits.cpp
@@ -113,10 +113,11 @@
uint32 offset;
uint32 bitIndex;
- uint8 Elem; /* Needs to be same type as pInput->pBuffer */
- uint8 Elem1;
- uint8 Elem2;
- uint8 Elem3;
+ uint32 bytesToFetch;
+ uint8 Elem = 0; /* Needs to be same type as pInput->pBuffer */
+ uint8 Elem1 = 0;
+ uint8 Elem2 = 0;
+ uint8 Elem3 = 0;
uint32 returnValue = 0;
if (!neededBits)
@@ -126,10 +127,25 @@
offset = (ptBitStream->usedBits) >> INBUF_ARRAY_INDEX_SHIFT;
- Elem = *(ptBitStream->pBuffer + module(offset , BUFSIZE));
- Elem1 = *(ptBitStream->pBuffer + module(offset + 1, BUFSIZE));
- Elem2 = *(ptBitStream->pBuffer + module(offset + 2, BUFSIZE));
- Elem3 = *(ptBitStream->pBuffer + module(offset + 3, BUFSIZE));
+ /* Remove extra high bits by shifting up */
+ bitIndex = module(ptBitStream->usedBits, INBUF_BIT_WIDTH);
+
+ bytesToFetch = (bitIndex + neededBits + 7 ) >> 3 ;
+
+ switch (bytesToFetch)
+ {
+ case 4:
+ Elem3 = *(ptBitStream->pBuffer + module(offset + 3, BUFSIZE));
+ [[fallthrough]];
+ case 3:
+ Elem2 = *(ptBitStream->pBuffer + module(offset + 2, BUFSIZE));
+ [[fallthrough]];
+ case 2:
+ Elem1 = *(ptBitStream->pBuffer + module(offset + 1, BUFSIZE));
+ [[fallthrough]];
+ case 1:
+ Elem = *(ptBitStream->pBuffer + module(offset, BUFSIZE));
+ }
returnValue = (((uint32)(Elem)) << 24) |
@@ -137,9 +153,6 @@
(((uint32)(Elem2)) << 8) |
((uint32)(Elem3));
- /* Remove extra high bits by shifting up */
- bitIndex = module(ptBitStream->usedBits, INBUF_BIT_WIDTH);
-
/* This line is faster than to mask off the high bits. */
returnValue <<= bitIndex;
@@ -161,22 +174,32 @@
uint32 offset;
uint32 bitIndex;
- uint8 Elem; /* Needs to be same type as pInput->pBuffer */
- uint8 Elem1;
+ uint32 bytesToFetch;
+ uint8 Elem = 0; /* Needs to be same type as pInput->pBuffer */
+ uint8 Elem1 = 0;
uint16 returnValue;
offset = (ptBitStream->usedBits) >> INBUF_ARRAY_INDEX_SHIFT;
- Elem = *(ptBitStream->pBuffer + module(offset , BUFSIZE));
- Elem1 = *(ptBitStream->pBuffer + module(offset + 1, BUFSIZE));
+ /* Remove extra high bits by shifting up */
+ bitIndex = module(ptBitStream->usedBits, INBUF_BIT_WIDTH);
+
+ bytesToFetch = (bitIndex + neededBits + 7 ) >> 3 ;
+
+ if (bytesToFetch > 1)
+ {
+ Elem = *(ptBitStream->pBuffer + module(offset, BUFSIZE));
+ Elem1 = *(ptBitStream->pBuffer + module(offset + 1, BUFSIZE));
+ }
+ else if (bytesToFetch > 0)
+ {
+ Elem = *(ptBitStream->pBuffer + module(offset, BUFSIZE));
+ }
returnValue = (((uint16)(Elem)) << 8) |
((uint16)(Elem1));
- /* Remove extra high bits by shifting up */
- bitIndex = module(ptBitStream->usedBits, INBUF_BIT_WIDTH);
-
ptBitStream->usedBits += neededBits;
/* This line is faster than to mask off the high bits. */
returnValue = (returnValue << (bitIndex));
@@ -197,25 +220,40 @@
uint32 offset;
uint32 bitIndex;
- uint8 Elem; /* Needs to be same type as pInput->pBuffer */
- uint8 Elem1;
- uint8 Elem2;
+ uint32 bytesToFetch;
+ uint8 Elem = 0; /* Needs to be same type as pInput->pBuffer */
+ uint8 Elem1 = 0;
+ uint8 Elem2 = 0;
uint32 returnValue;
offset = (ptBitStream->usedBits) >> INBUF_ARRAY_INDEX_SHIFT;
- Elem = *(ptBitStream->pBuffer + module(offset , BUFSIZE));
- Elem1 = *(ptBitStream->pBuffer + module(offset + 1, BUFSIZE));
- Elem2 = *(ptBitStream->pBuffer + module(offset + 2, BUFSIZE));
+ /* Remove extra high bits by shifting up */
+ bitIndex = module(ptBitStream->usedBits, INBUF_BIT_WIDTH);
+
+ bytesToFetch = (bitIndex + neededBits + 7 ) >> 3 ;
+
+ if (bytesToFetch > 2)
+ {
+ Elem = *(ptBitStream->pBuffer + module(offset, BUFSIZE));
+ Elem1 = *(ptBitStream->pBuffer + module(offset + 1, BUFSIZE));
+ Elem2 = *(ptBitStream->pBuffer + module(offset + 2, BUFSIZE));
+ }
+ else if (bytesToFetch > 1)
+ {
+ Elem = *(ptBitStream->pBuffer + module(offset, BUFSIZE));
+ Elem1 = *(ptBitStream->pBuffer + module(offset + 1, BUFSIZE));
+ }
+ else if (bytesToFetch > 0)
+ {
+ Elem = *(ptBitStream->pBuffer + module(offset, BUFSIZE));
+ }
returnValue = (((uint32)(Elem)) << 16) |
(((uint32)(Elem1)) << 8) |
((uint32)(Elem2));
- /* Remove extra high bits by shifting up */
- bitIndex = module(ptBitStream->usedBits, INBUF_BIT_WIDTH);
-
ptBitStream->usedBits += neededBits;
/* This line is faster than to mask off the high bits. */
returnValue = 0xFFFFFF & (returnValue << (bitIndex));
diff --git a/media/codecs/mp3dec/src/pvmp3_getbits.h b/media/codecs/mp3dec/src/pvmp3_getbits.h
index b058b00..b04fe6d 100644
--- a/media/codecs/mp3dec/src/pvmp3_getbits.h
+++ b/media/codecs/mp3dec/src/pvmp3_getbits.h
@@ -104,6 +104,11 @@
; Function Prototype declaration
----------------------------------------------------------------------------*/
+static inline bool bitsAvailable(tmp3Bits *inputStream, uint32 neededBits)
+{
+ return (inputStream->inputBufferCurrentLength << 3) >= (neededBits + inputStream->usedBits);
+}
+
/*----------------------------------------------------------------------------
; END
----------------------------------------------------------------------------*/
diff --git a/media/extractors/TEST_MAPPING b/media/extractors/TEST_MAPPING
index abefb0f..4984b8f 100644
--- a/media/extractors/TEST_MAPPING
+++ b/media/extractors/TEST_MAPPING
@@ -1,5 +1,6 @@
{
"presubmit": [
+
// TODO(b/153661591) enable test once the bug is fixed
// This tests the extractor path
// {
@@ -13,5 +14,14 @@
// }
// ]
// }
+ ],
+
+ // tests which require dynamic content
+ // invoke with: atest -- --enable-module-dynamic-download=true
+ // TODO(b/148094059): unit tests not allowed to download content
+ "dynamic-presubmit": [
+ { "name": "ExtractorUnitTest" }
]
+
+
}
diff --git a/media/extractors/aac/AACExtractor.cpp b/media/extractors/aac/AACExtractor.cpp
index 8f60f6b..2fc4584 100644
--- a/media/extractors/aac/AACExtractor.cpp
+++ b/media/extractors/aac/AACExtractor.cpp
@@ -18,6 +18,8 @@
#define LOG_TAG "AACExtractor"
#include <utils/Log.h>
+#include <inttypes.h>
+
#include "AACExtractor.h"
#include <media/MediaExtractorPluginApi.h>
#include <media/stagefright/foundation/ABuffer.h>
@@ -277,7 +279,22 @@
ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
if (mFrameDurationUs > 0) {
- int64_t seekFrame = seekTimeUs / mFrameDurationUs;
+ int64_t seekFrame = 0;
+ switch(mode & 0x7) {
+ case ReadOptions::SEEK_NEXT_SYNC:
+ // "at or after"
+ seekFrame = (seekTimeUs + mFrameDurationUs - 1) / mFrameDurationUs;
+ break;
+ case ReadOptions::SEEK_CLOSEST_SYNC:
+ case ReadOptions::SEEK_CLOSEST:
+ seekFrame = (seekTimeUs + mFrameDurationUs/2) / mFrameDurationUs;
+ break;
+ case ReadOptions::SEEK_PREVIOUS_SYNC:
+ default:
+ // 'at or before'
+ seekFrame = seekTimeUs / mFrameDurationUs;
+ break;
+ }
if (seekFrame < 0 || seekFrame >= (int64_t)mOffsetVector.size()) {
android_errorWriteLog(0x534e4554, "70239507");
return AMEDIA_ERROR_MALFORMED;
diff --git a/media/extractors/amr/AMRExtractor.cpp b/media/extractors/amr/AMRExtractor.cpp
index 26431a4..e26ff0a 100644
--- a/media/extractors/amr/AMRExtractor.cpp
+++ b/media/extractors/amr/AMRExtractor.cpp
@@ -18,6 +18,8 @@
#define LOG_TAG "AMRExtractor"
#include <utils/Log.h>
+#include <inttypes.h>
+
#include "AMRExtractor.h"
#include <media/stagefright/foundation/ADebug.h>
@@ -283,8 +285,22 @@
ReadOptions::SeekMode mode;
if (mOffsetTableLength > 0 && options && options->getSeekTo(&seekTimeUs, &mode)) {
size_t size;
- int64_t seekFrame = seekTimeUs / 20000LL; // 20ms per frame.
- mCurrentTimeUs = seekFrame * 20000LL;
+ const int64_t frameDurationUs = 20000LL; // 20ms per frame.
+ int64_t seekFrame = 0;
+ switch(mode & 0x7) {
+ case ReadOptions::SEEK_NEXT_SYNC:
+ seekFrame = (seekTimeUs + frameDurationUs - 1) / frameDurationUs;
+ break;
+ case ReadOptions::SEEK_CLOSEST_SYNC:
+ case ReadOptions::SEEK_CLOSEST:
+ seekFrame = (seekTimeUs + frameDurationUs/2) / frameDurationUs;
+ break;
+ case ReadOptions::SEEK_PREVIOUS_SYNC:
+ default:
+ seekFrame = seekTimeUs / frameDurationUs;
+ break;
+ }
+ mCurrentTimeUs = seekFrame * frameDurationUs;
size_t index = seekFrame < 0 ? 0 : seekFrame / 50;
if (index >= mOffsetTableLength) {
diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp
index 0617e88..ec7cb24 100644
--- a/media/extractors/flac/FLACExtractor.cpp
+++ b/media/extractors/flac/FLACExtractor.cpp
@@ -561,6 +561,8 @@
AMediaFormat_setString(mFileMetadata,
AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_FLAC);
}
+ mMaxBufferSize = getMaxBlockSize() * getChannels() * getOutputSampleSize();
+ AMediaFormat_setInt32(mTrackMetadata, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, mMaxBufferSize);
return OK;
}
@@ -568,8 +570,6 @@
{
CHECK(mGroup == NULL);
mGroup = group;
- mMaxBufferSize = getMaxBlockSize() * getChannels() * getOutputSampleSize();
- AMediaFormat_setInt32(mTrackMetadata, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, mMaxBufferSize);
mGroup->add_buffer(mMaxBufferSize);
}
diff --git a/media/extractors/fuzzers/Android.bp b/media/extractors/fuzzers/Android.bp
index f9e82fb..b731292 100644
--- a/media/extractors/fuzzers/Android.bp
+++ b/media/extractors/fuzzers/Android.bp
@@ -319,3 +319,27 @@
dictionary: "flac_extractor_fuzzer.dict",
}
+
+cc_fuzz {
+ name: "midi_extractor_fuzzer",
+ defaults: ["extractor-fuzzer-defaults"],
+
+ srcs: [
+ "midi_extractor_fuzzer.cpp",
+ ],
+
+ include_dirs: [
+ "frameworks/av/media/extractors/midi",
+ ],
+
+ static_libs: [
+ "libsonivox",
+ "libmedia_midiiowrapper",
+ "libmidiextractor",
+ "libwatchdog",
+ ],
+
+ dictionary: "midi_extractor_fuzzer.dict",
+
+ host_supported: true,
+}
diff --git a/media/extractors/fuzzers/README.md b/media/extractors/fuzzers/README.md
index 4223b5e..fb1d52f 100644
--- a/media/extractors/fuzzers/README.md
+++ b/media/extractors/fuzzers/README.md
@@ -11,6 +11,7 @@
+ [libmp3extractor](#mp3ExtractorFuzzer)
+ [libaacextractor](#aacExtractorFuzzer)
+ [libflacextractor](#flacExtractor)
++ [libmidiextractor](#midiExtractorFuzzer)
# <a name="ExtractorFuzzerBase"></a> Fuzzer for libextractorfuzzerbase
All the extractors have a common API - creating a data source, extraction
@@ -321,6 +322,41 @@
$ adb shell /data/fuzz/arm64/flac_extractor_fuzzer/flac_extractor_fuzzer CORPUS_DIR
```
+# <a name="midiExtractorFuzzer"></a> Fuzzer for libmidiextractor
+
+## Plugin Design Considerations
+The fuzzer plugin for MIDI extractor uses the `ExtractorFuzzerBase` class and
+implements only the `createExtractor` to create the MIDI extractor class.
+
+##### Maximize code coverage
+Dict file (dictionary file) is created for MIDI to ensure that the required MIDI
+headers are present in every input file that goes to the fuzzer.
+This ensures that larger code gets covered as a range of MIDI headers will be
+present in the input data.
+
+
+## Build
+
+This describes steps to build midi_extractor_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+ $ mm -j$(nproc) midi_extractor_fuzzer
+```
+
+#### Steps to run
+Create a directory CORPUS_DIR and copy some MIDI files to that folder
+Push this directory to device.
+
+To run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/midi_extractor_fuzzer/midi_extractor_fuzzer CORPUS_DIR
+```
+
## References:
* http://llvm.org/docs/LibFuzzer.html
* https://github.com/google/oss-fuzz
diff --git a/media/extractors/fuzzers/midi_extractor_fuzzer.cpp b/media/extractors/fuzzers/midi_extractor_fuzzer.cpp
new file mode 100644
index 0000000..e02a12b
--- /dev/null
+++ b/media/extractors/fuzzers/midi_extractor_fuzzer.cpp
@@ -0,0 +1,54 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+
+#include "ExtractorFuzzerBase.h"
+
+#include "MidiExtractor.h"
+
+using namespace android;
+
+class MIDIExtractor : public ExtractorFuzzerBase {
+ public:
+ MIDIExtractor() = default;
+ ~MIDIExtractor() = default;
+
+ bool createExtractor();
+};
+
+bool MIDIExtractor::createExtractor() {
+ mExtractor = new MidiExtractor(mDataSource->wrap());
+ if (!mExtractor) {
+ return false;
+ }
+ mExtractor->name();
+ return true;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if ((!data) || (size == 0)) {
+ return 0;
+ }
+ MIDIExtractor* extractor = new MIDIExtractor();
+ if (extractor) {
+ extractor->processData(data, size);
+ delete extractor;
+ }
+ return 0;
+}
diff --git a/media/extractors/fuzzers/midi_extractor_fuzzer.dict b/media/extractors/fuzzers/midi_extractor_fuzzer.dict
new file mode 100644
index 0000000..5b6bb8b
--- /dev/null
+++ b/media/extractors/fuzzers/midi_extractor_fuzzer.dict
@@ -0,0 +1,3 @@
+# MIDI Chunks
+kw1="MThd"
+kw2="MTrk"
diff --git a/media/extractors/midi/Android.bp b/media/extractors/midi/Android.bp
index f82fc41..08a6fa0 100644
--- a/media/extractors/midi/Android.bp
+++ b/media/extractors/midi/Android.bp
@@ -24,7 +24,7 @@
srcs: ["MidiExtractor.cpp"],
header_libs: [
- "libmedia_headers",
+ "libmedia_datasource_headers",
],
static_libs: [
@@ -37,4 +37,12 @@
shared_libs: [
"libbase",
],
+
+ host_supported: true,
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
diff --git a/media/extractors/mp3/MP3Extractor.cpp b/media/extractors/mp3/MP3Extractor.cpp
index 5165822..5bbabdf 100644
--- a/media/extractors/mp3/MP3Extractor.cpp
+++ b/media/extractors/mp3/MP3Extractor.cpp
@@ -425,8 +425,7 @@
if (mInitCheck != OK || index != 0) {
return AMEDIA_ERROR_UNKNOWN;
}
- AMediaFormat_copy(meta, mMeta);
- return AMEDIA_OK;
+ return AMediaFormat_copy(meta, mMeta);
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp
index 2599c2c..444664c 100644
--- a/media/extractors/mp4/ItemTable.cpp
+++ b/media/extractors/mp4/ItemTable.cpp
@@ -76,16 +76,19 @@
size_t size;
sp<ABuffer> hvcc;
sp<ABuffer> icc;
+ sp<ABuffer> av1c;
Vector<uint32_t> thumbnails;
Vector<uint32_t> dimgRefs;
- Vector<uint32_t> cdscRefs;
+ Vector<uint32_t> exifRefs;
+ Vector<uint32_t> xmpRefs;
size_t nextTileIndex;
};
-struct ExifItem {
+struct ExternalMetaItem {
off64_t offset;
size_t size;
+ bool isExif;
};
/////////////////////////////////////////////////////////////////////
@@ -481,7 +484,7 @@
void apply(
KeyedVector<uint32_t, ImageItem> &itemIdToItemMap,
- KeyedVector<uint32_t, ExifItem> &itemIdToExifMap) const;
+ KeyedVector<uint32_t, ExternalMetaItem> &itemIdToMetaMap) const;
private:
uint32_t mItemId;
@@ -493,7 +496,7 @@
void ItemReference::apply(
KeyedVector<uint32_t, ImageItem> &itemIdToItemMap,
- KeyedVector<uint32_t, ExifItem> &itemIdToExifMap) const {
+ KeyedVector<uint32_t, ExternalMetaItem> &itemIdToMetaMap) const {
ALOGV("attach reference type 0x%x to item id %d)", type(), mItemId);
switch(type()) {
@@ -555,15 +558,15 @@
break;
}
case FOURCC("cdsc"): {
- ssize_t itemIndex = itemIdToExifMap.indexOfKey(mItemId);
+ ssize_t metaIndex = itemIdToMetaMap.indexOfKey(mItemId);
- // ignore non-exif block items
- if (itemIndex < 0) {
+ // ignore non-meta items
+ if (metaIndex < 0) {
return;
}
for (size_t i = 0; i < mRefs.size(); i++) {
- itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
+ ssize_t itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
// ignore non-image items
if (itemIndex < 0) {
@@ -571,7 +574,11 @@
}
ALOGV("Image item id %d uses metadata item id %d", mRefs[i], mItemId);
ImageItem &image = itemIdToItemMap.editValueAt(itemIndex);
- image.cdscRefs.push_back(mItemId);
+ if (itemIdToMetaMap[metaIndex].isExif) {
+ image.exifRefs.push_back(mItemId);
+ } else {
+ image.xmpRefs.push_back(mItemId);
+ }
}
break;
}
@@ -764,6 +771,39 @@
return OK;
}
+struct Av1cBox : public Box, public ItemProperty {
+ Av1cBox(DataSourceHelper *source) :
+ Box(source, FOURCC("av1C")) {}
+
+ status_t parse(off64_t offset, size_t size) override;
+
+ void attachTo(ImageItem &image) const override {
+ image.av1c = mAv1c;
+ }
+
+private:
+ sp<ABuffer> mAv1c;
+};
+
+status_t Av1cBox::parse(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ mAv1c = new ABuffer(size);
+
+ if (mAv1c->data() == NULL) {
+ ALOGE("b/28471206");
+ return NO_MEMORY;
+ }
+
+ if (source()->readAt(offset, mAv1c->data(), size) < (ssize_t)size) {
+ return ERROR_IO;
+ }
+
+ ALOGV("property av1C");
+
+ return OK;
+}
+
struct IrotBox : public Box, public ItemProperty {
IrotBox(DataSourceHelper *source) :
Box(source, FOURCC("irot")), mAngle(0) {}
@@ -957,6 +997,11 @@
itemProperty = new ColrBox(source());
break;
}
+ case FOURCC("av1C"):
+ {
+ itemProperty = new Av1cBox(source());
+ break;
+ }
default:
{
// push dummy to maintain correct item property index
@@ -1026,7 +1071,21 @@
struct ItemInfo {
uint32_t itemId;
uint32_t itemType;
+ String8 contentType;
bool hidden;
+
+ bool isXmp() const {
+ return itemType == FOURCC("mime") && contentType == String8("application/rdf+xml");
+ }
+ bool isExif() const {
+ return itemType == FOURCC("Exif");
+ }
+ bool isGrid() const {
+ return itemType == FOURCC("grid");
+ }
+ bool isSample() const {
+ return itemType == FOURCC("av01") || itemType == FOURCC("hvc1");
+ }
};
struct InfeBox : public FullBox {
@@ -1116,6 +1175,7 @@
if (!parseNullTerminatedString(&offset, &size, &content_type)) {
return ERROR_MALFORMED;
}
+ itemInfo->contentType = content_type;
// content_encoding is optional; can be omitted if would be empty
if (size > 0) {
@@ -1136,18 +1196,18 @@
struct IinfBox : public FullBox {
IinfBox(DataSourceHelper *source, Vector<ItemInfo> *itemInfos) :
- FullBox(source, FOURCC("iinf")), mItemInfos(itemInfos) {}
+ FullBox(source, FOURCC("iinf")), mItemInfos(itemInfos), mNeedIref(false) {}
status_t parse(off64_t offset, size_t size);
- bool hasFourCC(uint32_t type) { return mFourCCSeen.count(type) > 0; }
+ bool needIrefBox() { return mNeedIref; }
protected:
status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
private:
Vector<ItemInfo> *mItemInfos;
- std::unordered_set<uint32_t> mFourCCSeen;
+ bool mNeedIref;
};
status_t IinfBox::parse(off64_t offset, size_t size) {
@@ -1194,7 +1254,7 @@
status_t err = infeBox.parse(offset, size, &itemInfo);
if (err == OK) {
mItemInfos->push_back(itemInfo);
- mFourCCSeen.insert(itemInfo.itemType);
+ mNeedIref |= (itemInfo.isExif() || itemInfo.isXmp() || itemInfo.isGrid());
}
// InfeBox parse returns ERROR_UNSUPPORTED if the box if an unsupported
// version. Ignore this error as it's not fatal.
@@ -1203,8 +1263,9 @@
//////////////////////////////////////////////////////////////////
-ItemTable::ItemTable(DataSourceHelper *source)
+ItemTable::ItemTable(DataSourceHelper *source, bool isHeif)
: mDataSource(source),
+ mIsHeif(isHeif),
mPrimaryItemId(0),
mIdatOffset(0),
mIdatSize(0),
@@ -1283,7 +1344,7 @@
return err;
}
- if (iinfBox.hasFourCC(FOURCC("grid")) || iinfBox.hasFourCC(FOURCC("Exif"))) {
+ if (iinfBox.needIrefBox()) {
mRequiredBoxes.insert('iref');
}
@@ -1359,11 +1420,9 @@
// Only handle 3 types of items, all others are ignored:
// 'grid': derived image from tiles
- // 'hvc1': coded image (or tile)
- // 'Exif': EXIF metadata
- if (info.itemType != FOURCC("grid") &&
- info.itemType != FOURCC("hvc1") &&
- info.itemType != FOURCC("Exif")) {
+ // 'hvc1' or 'av01': coded image (or tile)
+ // 'Exif' or XMP: metadata
+ if (!info.isGrid() && !info.isSample() && !info.isExif() && !info.isXmp()) {
continue;
}
@@ -1386,15 +1445,18 @@
return ERROR_MALFORMED;
}
- if (info.itemType == FOURCC("Exif")) {
- // Only add if the Exif data is non-empty. The first 4 bytes contain
+ if (info.isExif() || info.isXmp()) {
+ // Only add if the meta is non-empty. For Exif, the first 4 bytes contain
// the offset to TIFF header, which the Exif parser doesn't use.
- if (size > 4) {
- ExifItem exifItem = {
+ ALOGV("adding meta to mItemIdToMetaMap: isExif %d, offset %lld, size %lld",
+ info.isExif(), (long long)offset, (long long)size);
+ if ((info.isExif() && size > 4) || (info.isXmp() && size > 0)) {
+ ExternalMetaItem metaItem = {
+ .isExif = info.isExif(),
.offset = offset,
.size = size,
};
- mItemIdToExifMap.add(info.itemId, exifItem);
+ mItemIdToMetaMap.add(info.itemId, metaItem);
}
continue;
}
@@ -1429,7 +1491,7 @@
}
for (size_t i = 0; i < mItemReferences.size(); i++) {
- mItemReferences[i]->apply(mItemIdToItemMap, mItemIdToExifMap);
+ mItemReferences[i]->apply(mItemIdToItemMap, mItemIdToMetaMap);
}
bool foundPrimary = false;
@@ -1509,7 +1571,9 @@
}
AMediaFormat *meta = AMediaFormat_new();
- AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
+ AMediaFormat_setString(
+ meta, AMEDIAFORMAT_KEY_MIME,
+ mIsHeif ? MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC : MEDIA_MIMETYPE_IMAGE_AVIF);
if (image->itemId == mPrimaryItemId) {
AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_DEFAULT, 1);
@@ -1539,15 +1603,24 @@
ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(image->thumbnails[0]);
if (thumbItemIndex >= 0) {
const ImageItem &thumbnail = mItemIdToItemMap[thumbItemIndex];
-
- if (thumbnail.hvcc != NULL) {
+ if (thumbnail.hvcc != NULL || thumbnail.av1c != NULL) {
AMediaFormat_setInt32(meta,
AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH, thumbnail.width);
AMediaFormat_setInt32(meta,
AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT, thumbnail.height);
- AMediaFormat_setBuffer(meta,
- AMEDIAFORMAT_KEY_THUMBNAIL_CSD_HEVC,
- thumbnail.hvcc->data(), thumbnail.hvcc->size());
+ if (thumbnail.hvcc != NULL) {
+ AMediaFormat_setBuffer(meta,
+ AMEDIAFORMAT_KEY_THUMBNAIL_CSD_HEVC,
+ thumbnail.hvcc->data(), thumbnail.hvcc->size());
+ } else {
+ // We use a hard-coded string here instead of
+ // AMEDIAFORMAT_KEY_THUMBNAIL_CSD_AV1C. The key is available only from SDK 31.
+ // The mp4 extractor is part of mainline and builds against SDK 29 as of
+ // writing. This hard-coded string can be replaced with the named constant once
+ // the mp4 extractor is built against SDK >= 31.
+ AMediaFormat_setBuffer(meta,
+ "thumbnail-csd-av1c", thumbnail.av1c->data(), thumbnail.av1c->size());
+ }
ALOGV("image[%u]: thumbnail: size %dx%d, item index %zd",
imageIndex, thumbnail.width, thumbnail.height, thumbItemIndex);
} else {
@@ -1574,12 +1647,21 @@
AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 3 / 2);
}
- if (image->hvcc == NULL) {
- ALOGE("%s: hvcc is missing for image[%u]!", __FUNCTION__, imageIndex);
- return NULL;
+ if (mIsHeif) {
+ if (image->hvcc == NULL) {
+ ALOGE("%s: hvcc is missing for image[%u]!", __FUNCTION__, imageIndex);
+ return NULL;
+ }
+ AMediaFormat_setBuffer(meta,
+ AMEDIAFORMAT_KEY_CSD_HEVC, image->hvcc->data(), image->hvcc->size());
+ } else {
+ if (image->av1c == NULL) {
+ ALOGE("%s: av1c is missing for image[%u]!", __FUNCTION__, imageIndex);
+ return NULL;
+ }
+ AMediaFormat_setBuffer(meta,
+ AMEDIAFORMAT_KEY_CSD_0, image->av1c->data(), image->av1c->size());
}
- AMediaFormat_setBuffer(meta,
- AMEDIAFORMAT_KEY_CSD_HEVC, image->hvcc->data(), image->hvcc->size());
if (image->icc != NULL) {
AMediaFormat_setBuffer(meta,
@@ -1686,11 +1768,11 @@
}
const ImageItem &image = mItemIdToItemMap[itemIndex];
- if (image.cdscRefs.size() == 0) {
+ if (image.exifRefs.size() == 0) {
return NAME_NOT_FOUND;
}
- ssize_t exifIndex = mItemIdToExifMap.indexOfKey(image.cdscRefs[0]);
+ ssize_t exifIndex = mItemIdToMetaMap.indexOfKey(image.exifRefs[0]);
if (exifIndex < 0) {
return NAME_NOT_FOUND;
}
@@ -1698,7 +1780,7 @@
// skip the first 4-byte of the offset to TIFF header
uint32_t tiffOffset;
if (!mDataSource->readAt(
- mItemIdToExifMap[exifIndex].offset, &tiffOffset, 4)) {
+ mItemIdToMetaMap[exifIndex].offset, &tiffOffset, 4)) {
return ERROR_IO;
}
@@ -1711,16 +1793,43 @@
// exif data. The size of the item should be > 4 for a non-empty exif (this
// was already checked when the item was added). Also check that the tiff
// header offset is valid.
- if (mItemIdToExifMap[exifIndex].size <= 4 ||
- tiffOffset > mItemIdToExifMap[exifIndex].size - 4) {
+ if (mItemIdToMetaMap[exifIndex].size <= 4 ||
+ tiffOffset > mItemIdToMetaMap[exifIndex].size - 4) {
return ERROR_MALFORMED;
}
// Offset of 'Exif\0\0' relative to the beginning of 'Exif' item
// (first 4-byte is the tiff header offset)
uint32_t exifOffset = 4 + tiffOffset - 6;
- *offset = mItemIdToExifMap[exifIndex].offset + exifOffset;
- *size = mItemIdToExifMap[exifIndex].size - exifOffset;
+ *offset = mItemIdToMetaMap[exifIndex].offset + exifOffset;
+ *size = mItemIdToMetaMap[exifIndex].size - exifOffset;
+ return OK;
+}
+
+status_t ItemTable::getXmpOffsetAndSize(off64_t *offset, size_t *size) {
+ if (!mImageItemsValid) {
+ return INVALID_OPERATION;
+ }
+
+ ssize_t itemIndex = mItemIdToItemMap.indexOfKey(mPrimaryItemId);
+
+ // this should not happen, something's seriously wrong.
+ if (itemIndex < 0) {
+ return INVALID_OPERATION;
+ }
+
+ const ImageItem &image = mItemIdToItemMap[itemIndex];
+ if (image.xmpRefs.size() == 0) {
+ return NAME_NOT_FOUND;
+ }
+
+ ssize_t xmpIndex = mItemIdToMetaMap.indexOfKey(image.xmpRefs[0]);
+ if (xmpIndex < 0) {
+ return NAME_NOT_FOUND;
+ }
+
+ *offset = mItemIdToMetaMap[xmpIndex].offset;
+ *size = mItemIdToMetaMap[xmpIndex].size;
return OK;
}
diff --git a/media/extractors/mp4/ItemTable.h b/media/extractors/mp4/ItemTable.h
index be81b59..62826b6 100644
--- a/media/extractors/mp4/ItemTable.h
+++ b/media/extractors/mp4/ItemTable.h
@@ -34,7 +34,7 @@
struct AssociationEntry;
struct ImageItem;
-struct ExifItem;
+struct ExternalMetaItem;
struct ItemLoc;
struct ItemInfo;
struct ItemProperty;
@@ -42,12 +42,12 @@
/*
* ItemTable keeps track of all image items (including coded images, grids and
- * tiles) inside a HEIF still image (ISO/IEC FDIS 23008-12.2:2017(E)).
+ * tiles) inside a HEIF/AVIF still image (ISO/IEC FDIS 23008-12.2:2017(E)).
*/
class ItemTable : public RefBase {
public:
- explicit ItemTable(DataSourceHelper *source);
+ ItemTable(DataSourceHelper *source, bool isHeif);
status_t parse(uint32_t type, off64_t offset, size_t size);
@@ -59,12 +59,15 @@
status_t getImageOffsetAndSize(
uint32_t *itemIndex, off64_t *offset, size_t *size);
status_t getExifOffsetAndSize(off64_t *offset, size_t *size);
+ status_t getXmpOffsetAndSize(off64_t *offset, size_t *size);
protected:
~ItemTable();
private:
DataSourceHelper *mDataSource;
+ // If this is true, then this item table is for a HEIF image. Otherwise it is for an AVIF image.
+ bool mIsHeif;
KeyedVector<uint32_t, ItemLoc> mItemLocs;
Vector<ItemInfo> mItemInfos;
@@ -82,7 +85,7 @@
bool mImageItemsValid;
uint32_t mCurrentItemIndex;
KeyedVector<uint32_t, ImageItem> mItemIdToItemMap;
- KeyedVector<uint32_t, ExifItem> mItemIdToExifMap;
+ KeyedVector<uint32_t, ExternalMetaItem> mItemIdToMetaMap;
Vector<uint32_t> mDisplayables;
status_t parseIlocBox(off64_t offset, size_t size);
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index f725a8c..b4e4c5d 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -160,6 +160,7 @@
uint8_t *mSrcBuffer;
bool mIsHeif;
+ bool mIsAvif;
bool mIsAudio;
bool mIsUsac = false;
sp<ItemTable> mItemTable;
@@ -429,6 +430,7 @@
mIsHeif(false),
mHasMoovBox(false),
mPreferHeif(mime != NULL && !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_HEIF)),
+ mIsAvif(false),
mFirstTrack(NULL),
mLastTrack(NULL) {
ALOGV("mime=%s, mPreferHeif=%d", mime, mPreferHeif);
@@ -653,8 +655,7 @@
}
}
- AMediaFormat_copy(meta, track->meta);
- return AMEDIA_OK;
+ return AMediaFormat_copy(meta, track->meta);
}
status_t MPEG4Extractor::readMetaData() {
@@ -685,7 +686,7 @@
}
}
- if (mIsHeif && (mItemTable != NULL) && (mItemTable->countImages() > 0)) {
+ if ((mIsAvif || mIsHeif) && (mItemTable != NULL) && (mItemTable->countImages() > 0)) {
off64_t exifOffset;
size_t exifSize;
if (mItemTable->getExifOffsetAndSize(&exifOffset, &exifSize) == OK) {
@@ -694,6 +695,19 @@
AMediaFormat_setInt64(mFileMetaData,
AMEDIAFORMAT_KEY_EXIF_SIZE, (int64_t)exifSize);
}
+ off64_t xmpOffset;
+ size_t xmpSize;
+ if (mItemTable->getXmpOffsetAndSize(&xmpOffset, &xmpSize) == OK) {
+ // TODO(chz): b/175717339
+ // Use a hard-coded string here instead of named keys. The keys are available
+ // only on API 31+. The mp4 extractor is part of mainline and has min_sdk_version
+ // of 29. This hard-coded string can be replaced with the named constant once
+ // the mp4 extractor is built against API 31+.
+ AMediaFormat_setInt64(mFileMetaData,
+ "xmp-offset" /*AMEDIAFORMAT_KEY_XMP_OFFSET*/, (int64_t)xmpOffset);
+ AMediaFormat_setInt64(mFileMetaData,
+ "xmp-size" /*AMEDIAFORMAT_KEY_XMP_SIZE*/, (int64_t)xmpSize);
+ }
for (uint32_t imageIndex = 0;
imageIndex < mItemTable->countImages(); imageIndex++) {
AMediaFormat *meta = mItemTable->getImageMeta(imageIndex);
@@ -711,7 +725,7 @@
}
mInitCheck = OK;
- ALOGV("adding HEIF image track %u", imageIndex);
+ ALOGV("adding %s image track %u", mIsHeif ? "HEIF" : "AVIF", imageIndex);
Track *track = new Track;
if (mLastTrack != NULL) {
mLastTrack->next = track;
@@ -737,6 +751,10 @@
MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC) != NULL) {
AMediaFormat_setString(mFileMetaData,
AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_CONTAINER_HEIF);
+ } else if (findTrackByMimePrefix(
+ MEDIA_MIMETYPE_IMAGE_AVIF) != NULL) {
+ AMediaFormat_setString(mFileMetaData,
+ AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_IMAGE_AVIF);
} else {
AMediaFormat_setString(mFileMetaData,
AMEDIAFORMAT_KEY_MIME, "application/octet-stream");
@@ -2431,7 +2449,7 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- AMediaFormat_setBuffer(mLastTrack->meta,
+ AMediaFormat_setBuffer(mLastTrack->meta,
AMEDIAFORMAT_KEY_ESDS, &buffer[4], chunk_data_size - 4);
if (mPath.size() >= 2
@@ -2513,7 +2531,7 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- AMediaFormat_setBuffer(mLastTrack->meta,
+ AMediaFormat_setBuffer(mLastTrack->meta,
AMEDIAFORMAT_KEY_CSD_AVC, buffer.get(), chunk_data_size);
break;
@@ -2535,7 +2553,7 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- AMediaFormat_setBuffer(mLastTrack->meta,
+ AMediaFormat_setBuffer(mLastTrack->meta,
AMEDIAFORMAT_KEY_CSD_HEVC, buffer.get(), chunk_data_size);
*offset += chunk_size;
@@ -2569,7 +2587,9 @@
case FOURCC("dvcC"):
case FOURCC("dvvC"): {
- CHECK_EQ(chunk_data_size, 24);
+ if (chunk_data_size != 24) {
+ return ERROR_MALFORMED;
+ }
auto buffer = heapbuffer<uint8_t>(chunk_data_size);
@@ -2680,9 +2700,9 @@
case FOURCC("iref"):
case FOURCC("ipro"):
{
- if (mIsHeif) {
+ if (mIsHeif || mIsAvif) {
if (mItemTable == NULL) {
- mItemTable = new ItemTable(mDataSource);
+ mItemTable = new ItemTable(mDataSource, mIsHeif);
}
status_t err = mItemTable->parse(
chunk_type, data_offset, chunk_data_size);
@@ -2984,6 +3004,21 @@
break;
}
+ case FOURCC("pasp"):
+ {
+ *offset += chunk_size;
+ // this must be in a VisualSampleEntry box under the Sample Description Box ('stsd')
+ // ignore otherwise
+ if (depth >= 2 && mPath[depth - 2] == FOURCC("stsd")) {
+ status_t err = parsePaspBox(data_offset, chunk_data_size);
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ break;
+ }
+
case FOURCC("titl"):
case FOURCC("perf"):
case FOURCC("auth"):
@@ -3108,14 +3143,20 @@
mIsHeif = true;
brandSet.erase(FOURCC("mif1"));
brandSet.erase(FOURCC("heic"));
+ } else if (brandSet.count(FOURCC("avif")) > 0 ||
+ brandSet.count(FOURCC("avis")) > 0) {
+ ALOGV("identified AVIF image");
+ mIsAvif = true;
+ brandSet.erase(FOURCC("avif"));
+ brandSet.erase(FOURCC("avis"));
}
if (!brandSet.empty()) {
// This means that the file should have moov box.
// It could be any iso files (mp4, heifs, etc.)
mHasMoovBox = true;
- if (mIsHeif) {
- ALOGV("identified HEIF image with other tracks");
+ if (mIsHeif || mIsAvif) {
+ ALOGV("identified %s image with other tracks", mIsHeif ? "HEIF" : "AVIF");
}
}
}
@@ -4086,13 +4127,13 @@
// custom genre string
buffer[size] = '\0';
- AMediaFormat_setString(mFileMetaData,
+ AMediaFormat_setString(mFileMetaData,
metadataKey, (const char *)buffer + 8);
}
} else {
buffer[size] = '\0';
- AMediaFormat_setString(mFileMetaData,
+ AMediaFormat_setString(mFileMetaData,
metadataKey, (const char *)buffer + 8);
}
}
@@ -4156,6 +4197,26 @@
return OK;
}
+status_t MPEG4Extractor::parsePaspBox(off64_t offset, size_t size) {
+ if (size < 8 || size == SIZE_MAX || mLastTrack == NULL) {
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t data[2]; // hSpacing, vSpacing
+ if (mDataSource->readAt(offset, data, 8) < 8) {
+ return ERROR_IO;
+ }
+ uint32_t hSpacing = ntohl(data[0]);
+ uint32_t vSpacing = ntohl(data[1]);
+
+ if (hSpacing != 0 && vSpacing != 0) {
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAR_WIDTH, hSpacing);
+ AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAR_HEIGHT, vSpacing);
+ }
+
+ return OK;
+}
+
status_t MPEG4Extractor::parse3GPPMetaData(off64_t offset, size_t size, int depth) {
if (size < 4 || size == SIZE_MAX) {
return ERROR_MALFORMED;
@@ -4433,7 +4494,8 @@
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)) {
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)
+ || !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_AVIF)) {
void *data;
size_t size;
if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_0, &data, &size)) {
@@ -4442,9 +4504,12 @@
const uint8_t *ptr = (const uint8_t *)data;
- if (size < 5 || ptr[0] != 0x81) { // configurationVersion == 1
+ if (size < 4 || ptr[0] != 0x81) { // configurationVersion == 1
return NULL;
}
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_AVIF)) {
+ itemTable = mItemTable;
+ }
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_VP9)) {
void *data;
size_t size;
@@ -5013,7 +5078,6 @@
mStarted(false),
mBuffer(NULL),
mSrcBuffer(NULL),
- mIsHeif(itemTable != NULL),
mItemTable(itemTable),
mElstShiftStartTicks(elstShiftStartTicks),
mElstInitialEmptyEditTicks(elstInitialEmptyEditTicks) {
@@ -5050,6 +5114,8 @@
!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
mIsAC4 = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC4);
mIsDolbyVision = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION);
+ mIsHeif = !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC) && mItemTable != NULL;
+ mIsAvif = !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_AVIF) && mItemTable != NULL;
if (mIsAVC) {
void *data;
@@ -6044,7 +6110,7 @@
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
ALOGV("seekTimeUs:%" PRId64, seekTimeUs);
- if (mIsHeif) {
+ if (mIsHeif || mIsAvif) {
CHECK(mSampleTable == NULL);
CHECK(mItemTable != NULL);
int32_t imageIndex;
@@ -6190,7 +6256,7 @@
newBuffer = true;
status_t err;
- if (!mIsHeif) {
+ if (!mIsHeif && !mIsAvif) {
err = mSampleTable->getMetaDataForSample(mCurrentSampleIndex, &offset, &size,
(uint64_t*)&cts, &isSyncSample, &stts);
if(err == OK) {
@@ -6237,9 +6303,13 @@
if (newBuffer) {
if (mIsPcm) {
// The twos' PCM block reader assumes that all samples has the same size.
-
- uint32_t samplesToRead = mSampleTable->getLastSampleIndexInChunk()
- - mCurrentSampleIndex + 1;
+ uint32_t lastSampleIndexInChunk = mSampleTable->getLastSampleIndexInChunk();
+ if (lastSampleIndexInChunk < mCurrentSampleIndex) {
+ mBuffer->release();
+ mBuffer = nullptr;
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ uint32_t samplesToRead = lastSampleIndexInChunk - mCurrentSampleIndex + 1;
if (samplesToRead > kMaxPcmFrameSize) {
samplesToRead = kMaxPcmFrameSize;
}
@@ -6248,13 +6318,17 @@
samplesToRead, size, mCurrentSampleIndex,
mSampleTable->getLastSampleIndexInChunk());
- size_t totalSize = samplesToRead * size;
+ size_t totalSize = samplesToRead * size;
+ if (mBuffer->size() < totalSize) {
+ mBuffer->release();
+ mBuffer = nullptr;
+ return AMEDIA_ERROR_UNKNOWN;
+ }
uint8_t* buf = (uint8_t *)mBuffer->data();
ssize_t bytesRead = mDataSource->readAt(offset, buf, totalSize);
if (bytesRead < (ssize_t)totalSize) {
mBuffer->release();
mBuffer = NULL;
-
return AMEDIA_ERROR_IO;
}
@@ -6308,7 +6382,19 @@
if (isSyncSample) {
AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1);
}
-
+
+ AMediaFormat_setInt64(
+ meta, "sample-file-offset" /*AMEDIAFORMAT_KEY_SAMPLE_FILE_OFFSET*/,
+ offset);
+
+ if (mSampleTable != nullptr &&
+ mCurrentSampleIndex == mSampleTable->getLastSampleIndexInChunk()) {
+ AMediaFormat_setInt64(
+ meta,
+ "last-sample-index-in-chunk" /*AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK*/,
+ mSampleTable->getLastSampleIndexInChunk());
+ }
+
++mCurrentSampleIndex;
}
}
@@ -6458,6 +6544,17 @@
AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1);
}
+ AMediaFormat_setInt64(
+ meta, "sample-file-offset" /*AMEDIAFORMAT_KEY_SAMPLE_FILE_OFFSET*/, offset);
+
+ if (mSampleTable != nullptr &&
+ mCurrentSampleIndex == mSampleTable->getLastSampleIndexInChunk()) {
+ AMediaFormat_setInt64(
+ meta,
+ "last-sample-index-in-chunk" /*AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK*/,
+ mSampleTable->getLastSampleIndexInChunk());
+ }
+
++mCurrentSampleIndex;
*out = mBuffer;
@@ -6829,7 +6926,8 @@
|| !memcmp(header, "ftypM4A ", 8) || !memcmp(header, "ftypf4v ", 8)
|| !memcmp(header, "ftypkddi", 8) || !memcmp(header, "ftypM4VP", 8)
|| !memcmp(header, "ftypmif1", 8) || !memcmp(header, "ftypheic", 8)
- || !memcmp(header, "ftypmsf1", 8) || !memcmp(header, "ftyphevc", 8)) {
+ || !memcmp(header, "ftypmsf1", 8) || !memcmp(header, "ftyphevc", 8)
+ || !memcmp(header, "ftypavif", 8) || !memcmp(header, "ftypavis", 8)) {
*confidence = 0.4;
return true;
@@ -6865,6 +6963,8 @@
FOURCC("heic"), // HEIF image
FOURCC("msf1"), // HEIF image sequence
FOURCC("hevc"), // HEIF image sequence
+ FOURCC("avif"), // AVIF image
+ FOURCC("avis"), // AVIF image sequence
};
for (size_t i = 0;
diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h
index 1e49d50..542a3e6 100644
--- a/media/extractors/mp4/MPEG4Extractor.h
+++ b/media/extractors/mp4/MPEG4Extractor.h
@@ -144,6 +144,7 @@
bool mIsHeif;
bool mHasMoovBox;
bool mPreferHeif;
+ bool mIsAvif;
Track *mFirstTrack, *mLastTrack;
@@ -160,6 +161,7 @@
status_t parseChunk(off64_t *offset, int depth);
status_t parseITunesMetaData(off64_t offset, size_t size);
status_t parseColorInfo(off64_t offset, size_t size);
+ status_t parsePaspBox(off64_t offset, size_t size);
status_t parse3GPPMetaData(off64_t offset, size_t size, int depth);
void parseID3v2MetaData(off64_t offset, uint64_t size);
status_t parseQTMetaKey(off64_t data_offset, size_t data_size);
diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.cpp b/media/extractors/mpeg2/MPEG2TSExtractor.cpp
index 9e093eb..2e68809 100644
--- a/media/extractors/mpeg2/MPEG2TSExtractor.cpp
+++ b/media/extractors/mpeg2/MPEG2TSExtractor.cpp
@@ -268,6 +268,9 @@
media_status_t MPEG2TSExtractor::getTrackMetaData(
AMediaFormat *meta,
size_t index, uint32_t /* flags */) {
+ if (meta == nullptr) {
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
sp<MetaData> implMeta = index < mSourceImpls.size()
? mSourceImpls.editItemAt(index)->getFormat() : NULL;
if (implMeta == NULL) {
diff --git a/media/extractors/tests/Android.bp b/media/extractors/tests/Android.bp
index f46fa7b..e17893e 100644
--- a/media/extractors/tests/Android.bp
+++ b/media/extractors/tests/Android.bp
@@ -26,6 +26,7 @@
cc_test {
name: "ExtractorUnitTest",
gtest: true,
+ test_suites: ["device-tests"],
srcs: ["ExtractorUnitTest.cpp"],
diff --git a/media/extractors/tests/ExtractorUnitTest.cpp b/media/extractors/tests/ExtractorUnitTest.cpp
index d91fffa..84ec1f2 100644
--- a/media/extractors/tests/ExtractorUnitTest.cpp
+++ b/media/extractors/tests/ExtractorUnitTest.cpp
@@ -18,6 +18,8 @@
#define LOG_TAG "ExtractorUnitTest"
#include <utils/Log.h>
+#include <inttypes.h>
+
#include <datasource/FileSource.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaCodecConstants.h>
@@ -503,7 +505,7 @@
&trackSampleRate));
ASSERT_EQ(exChannelCount, trackChannelCount) << "ChannelCount not as expected";
ASSERT_EQ(exSampleRate, trackSampleRate) << "SampleRate not as expected";
- } else {
+ } else if (!strncmp(extractorMime, "video/", 6)) {
int32_t exWidth, exHeight;
int32_t trackWidth, trackHeight;
ASSERT_TRUE(AMediaFormat_getInt32(extractorFormat, AMEDIAFORMAT_KEY_WIDTH, &exWidth));
@@ -512,6 +514,8 @@
ASSERT_TRUE(AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_HEIGHT, &trackHeight));
ASSERT_EQ(exWidth, trackWidth) << "Width not as expected";
ASSERT_EQ(exHeight, trackHeight) << "Height not as expected";
+ } else {
+ ALOGV("non a/v track");
}
status = cTrack->stop(track);
ASSERT_EQ(OK, status) << "Failed to stop the track";
@@ -568,8 +572,9 @@
TEST_P(ExtractorFunctionalityTest, SeekTest) {
if (mDisableTest) return;
- ALOGV("Validates %s Extractor behaviour for different seek modes", mContainer.c_str());
string inputFileName = gEnv->getRes() + get<1>(GetParam());
+ ALOGV("Validates %s Extractor behaviour for different seek modes filename %s",
+ mContainer.c_str(), inputFileName.c_str());
int32_t status = setDataSource(inputFileName);
ASSERT_EQ(status, 0) << "SetDataSource failed for" << mContainer << "extractor";
@@ -680,7 +685,8 @@
if (seekIdx >= seekablePointsSize) seekIdx = seekablePointsSize - 1;
int64_t seekToTimeStamp = seekablePoints[seekIdx];
- if (seekablePointsSize > 1) {
+ if (seekIdx > 1) {
+ // pick a time just earlier than this seek point
int64_t prevTimeStamp = seekablePoints[seekIdx - 1];
seekToTimeStamp = seekToTimeStamp - ((seekToTimeStamp - prevTimeStamp) >> 3);
}
@@ -711,11 +717,7 @@
// CMediaTrackReadOptions::SEEK is 8. Using mask 0111b to get true modes
switch (mode & 0x7) {
case CMediaTrackReadOptions::SEEK_PREVIOUS_SYNC:
- if (seekablePointsSize == 1) {
- EXPECT_EQ(timeStamp, seekablePoints[seekIdx]);
- } else {
- EXPECT_EQ(timeStamp, seekablePoints[seekIdx - 1]);
- }
+ EXPECT_EQ(timeStamp, seekablePoints[seekIdx > 0 ? (seekIdx - 1) : 0]);
break;
case CMediaTrackReadOptions::SEEK_NEXT_SYNC:
case CMediaTrackReadOptions::SEEK_CLOSEST_SYNC:
@@ -743,8 +745,9 @@
// TODO(b/155630778): Enable test for wav extractors
if (mExtractorName == WAV) return;
- ALOGV("Validates %s Extractor behaviour for invalid seek points", mContainer.c_str());
string inputFileName = gEnv->getRes() + get<1>(GetParam());
+ ALOGV("Validates %s Extractor behaviour for invalid seek points, filename %s",
+ mContainer.c_str(), inputFileName.c_str());
int32_t status = setDataSource(inputFileName);
ASSERT_EQ(status, 0) << "SetDataSource failed for" << mContainer << "extractor";
@@ -832,8 +835,9 @@
// TODO(b/155626946): Enable test for MPEG2 TS/PS extractors
if (mExtractorName == MPEG2TS || mExtractorName == MPEG2PS) return;
- ALOGV("Validates %s Extractor behaviour for invalid tracks", mContainer.c_str());
string inputFileName = gEnv->getRes() + get<1>(GetParam());
+ ALOGV("Validates %s Extractor behaviour for invalid tracks - file %s",
+ mContainer.c_str(), inputFileName.c_str());
int32_t status = setDataSource(inputFileName);
ASSERT_EQ(status, 0) << "SetDataSource failed for" << mContainer << "extractor";
@@ -872,13 +876,17 @@
TEST_P(ConfigParamTest, ConfigParamValidation) {
if (mDisableTest) return;
+ const int trackNumber = 0;
+
string container = GetParam().first;
- ALOGV("Validates %s Extractor for input's file properties", container.c_str());
string inputFileName = gEnv->getRes();
inputID inputFileId = GetParam().second;
configFormat configParam;
getFileProperties(inputFileId, inputFileName, configParam);
+ ALOGV("Validates %s Extractor for input's file properties, file %s",
+ container.c_str(), inputFileName.c_str());
+
int32_t status = setDataSource(inputFileName);
ASSERT_EQ(status, 0) << "SetDataSource failed for " << container << "extractor";
@@ -888,7 +896,7 @@
int32_t numTracks = mExtractor->countTracks();
ASSERT_GT(numTracks, 0) << "Extractor didn't find any track for the given clip";
- MediaTrackHelper *track = mExtractor->getTrack(0);
+ MediaTrackHelper *track = mExtractor->getTrack(trackNumber);
ASSERT_NE(track, nullptr) << "Failed to get track for index 0";
AMediaFormat *trackFormat = AMediaFormat_new();
@@ -910,7 +918,7 @@
AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_SAMPLE_RATE, &trackSampleRate));
ASSERT_EQ(configParam.sampleRate, trackSampleRate) << "SampleRate not as expected";
ASSERT_EQ(configParam.channelCount, trackChannelCount) << "ChannelCount not as expected";
- } else {
+ } else if (!strncmp(trackMime, "video/", 6)) {
int32_t trackWidth, trackHeight;
ASSERT_TRUE(AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_WIDTH, &trackWidth));
ASSERT_TRUE(AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_HEIGHT, &trackHeight));
diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp
index 901b29d..9e94587 100644
--- a/media/extractors/wav/WAVExtractor.cpp
+++ b/media/extractors/wav/WAVExtractor.cpp
@@ -440,19 +440,22 @@
int64_t seekTimeUs;
ReadOptions::SeekMode mode;
if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) {
- int64_t pos = 0;
-
+ int64_t pos;
+ int64_t sampleNumber;
+ bool overflowed = __builtin_mul_overflow(seekTimeUs, mSampleRate, &sampleNumber);
+ sampleNumber /= 1000000;
if (mWaveFormat == WAVE_FORMAT_MSGSM) {
// 65 bytes decode to 320 8kHz samples
- int64_t samplenumber = (seekTimeUs * mSampleRate) / 1000000;
- int64_t framenumber = samplenumber / 320;
- pos = framenumber * 65;
+ pos = sampleNumber / 320 * 65;
} else {
- pos = (seekTimeUs * mSampleRate) / 1000000 * mNumChannels * (mBitsPerSample >> 3);
+ int64_t bytesPerFrame;
+ overflowed |= __builtin_mul_overflow(mNumChannels, mBitsPerSample >> 3, &bytesPerFrame);
+ overflowed |= __builtin_mul_overflow(bytesPerFrame, sampleNumber, &pos);
}
- if (pos > (off64_t)mSize) {
- pos = mSize;
+ if (overflowed) {
+ return AMEDIA_ERROR_MALFORMED;
}
+ pos = std::clamp(pos, (int64_t) 0, (int64_t) mSize);
mCurrentPos = pos + mOffset;
}
diff --git a/media/libaaudio/Android.bp b/media/libaaudio/Android.bp
index 05c788c..add28e0 100644
--- a/media/libaaudio/Android.bp
+++ b/media/libaaudio/Android.bp
@@ -41,4 +41,6 @@
cc_library_headers {
name: "libaaudio_headers",
export_include_dirs: ["include"],
+ export_shared_lib_headers: ["aaudio-aidl-cpp"],
+ shared_libs: ["aaudio-aidl-cpp"],
}
diff --git a/media/libaaudio/Doxyfile.orig b/media/libaaudio/Doxyfile.orig
deleted file mode 100644
index 137facb..0000000
--- a/media/libaaudio/Doxyfile.orig
+++ /dev/null
@@ -1,2303 +0,0 @@
-# Doxyfile 1.8.6
-
-# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project.
-#
-# All text after a double hash (##) is considered a comment and is placed in
-# front of the TAG it is preceding.
-#
-# All text after a single hash (#) is considered a comment and will be ignored.
-# The format is:
-# TAG = value [value, ...]
-# For lists, items can also be appended using:
-# TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (\" \").
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
-
-# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all text
-# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
-# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
-# for the list of possible encodings.
-# The default value is: UTF-8.
-
-DOXYFILE_ENCODING = UTF-8
-
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
-# double-quotes, unless you are using Doxywizard) that should identify the
-# project for which the documentation is generated. This name is used in the
-# title of most generated pages and in a few other places.
-# The default value is: My Project.
-
-PROJECT_NAME = "My Project"
-
-# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
-# could be handy for archiving the generated documentation or if some version
-# control system is used.
-
-PROJECT_NUMBER =
-
-# Using the PROJECT_BRIEF tag one can provide an optional one line description
-# for a project that appears at the top of each page and should give viewer a
-# quick idea about the purpose of the project. Keep the description short.
-
-PROJECT_BRIEF =
-
-# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
-# the documentation. The maximum height of the logo should not exceed 55 pixels
-# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
-# to the output directory.
-
-PROJECT_LOGO =
-
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
-# into which the generated documentation will be written. If a relative path is
-# entered, it will be relative to the location where doxygen was started. If
-# left blank the current directory will be used.
-
-OUTPUT_DIRECTORY =
-
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
-# directories (in 2 levels) under the output directory of each output format and
-# will distribute the generated files over these directories. Enabling this
-# option can be useful when feeding doxygen a huge amount of source files, where
-# putting all generated files in the same directory would otherwise causes
-# performance problems for the file system.
-# The default value is: NO.
-
-CREATE_SUBDIRS = NO
-
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all constant output in the proper language.
-# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
-# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
-# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
-# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
-# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
-# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
-# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
-# Ukrainian and Vietnamese.
-# The default value is: English.
-
-OUTPUT_LANGUAGE = English
-
-# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
-# descriptions after the members that are listed in the file and class
-# documentation (similar to Javadoc). Set to NO to disable this.
-# The default value is: YES.
-
-BRIEF_MEMBER_DESC = YES
-
-# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
-# description of a member or function before the detailed description
-#
-# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
-# brief descriptions will be completely suppressed.
-# The default value is: YES.
-
-REPEAT_BRIEF = YES
-
-# This tag implements a quasi-intelligent brief description abbreviator that is
-# used to form the text in various listings. Each string in this list, if found
-# as the leading text of the brief description, will be stripped from the text
-# and the result, after processing the whole list, is used as the annotated
-# text. Otherwise, the brief description is used as-is. If left blank, the
-# following values are used ($name is automatically replaced with the name of
-# the entity):The $name class, The $name widget, The $name file, is, provides,
-# specifies, contains, represents, a, an and the.
-
-ABBREVIATE_BRIEF =
-
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# doxygen will generate a detailed section even if there is only a brief
-# description.
-# The default value is: NO.
-
-ALWAYS_DETAILED_SEC = NO
-
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
-# inherited members of a class in the documentation of that class as if those
-# members were ordinary class members. Constructors, destructors and assignment
-# operators of the base classes will not be shown.
-# The default value is: NO.
-
-INLINE_INHERITED_MEMB = NO
-
-# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
-# before files name in the file list and in the header files. If set to NO the
-# shortest path that makes the file name unique will be used
-# The default value is: YES.
-
-FULL_PATH_NAMES = YES
-
-# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
-# Stripping is only done if one of the specified strings matches the left-hand
-# part of the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the path to
-# strip.
-#
-# Note that you can specify absolute paths here, but also relative paths, which
-# will be relative from the directory where doxygen is started.
-# This tag requires that the tag FULL_PATH_NAMES is set to YES.
-
-STRIP_FROM_PATH =
-
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
-# path mentioned in the documentation of a class, which tells the reader which
-# header file to include in order to use a class. If left blank only the name of
-# the header file containing the class definition is used. Otherwise one should
-# specify the list of include paths that are normally passed to the compiler
-# using the -I flag.
-
-STRIP_FROM_INC_PATH =
-
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
-# less readable) file names. This can be useful is your file systems doesn't
-# support long names like on DOS, Mac, or CD-ROM.
-# The default value is: NO.
-
-SHORT_NAMES = NO
-
-# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
-# first line (until the first dot) of a Javadoc-style comment as the brief
-# description. If set to NO, the Javadoc-style will behave just like regular Qt-
-# style comments (thus requiring an explicit @brief command for a brief
-# description.)
-# The default value is: NO.
-
-JAVADOC_AUTOBRIEF = NO
-
-# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
-# line (until the first dot) of a Qt-style comment as the brief description. If
-# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
-# requiring an explicit \brief command for a brief description.)
-# The default value is: NO.
-
-QT_AUTOBRIEF = NO
-
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
-# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
-# a brief description. This used to be the default behavior. The new default is
-# to treat a multi-line C++ comment block as a detailed description. Set this
-# tag to YES if you prefer the old behavior instead.
-#
-# Note that setting this tag to YES also means that rational rose comments are
-# not recognized any more.
-# The default value is: NO.
-
-MULTILINE_CPP_IS_BRIEF = NO
-
-# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
-# documentation from any documented member that it re-implements.
-# The default value is: YES.
-
-INHERIT_DOCS = YES
-
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
-# new page for each member. If set to NO, the documentation of a member will be
-# part of the file/class/namespace that contains it.
-# The default value is: NO.
-
-SEPARATE_MEMBER_PAGES = NO
-
-# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
-# uses this value to replace tabs by spaces in code fragments.
-# Minimum value: 1, maximum value: 16, default value: 4.
-
-TAB_SIZE = 4
-
-# This tag can be used to specify a number of aliases that act as commands in
-# the documentation. An alias has the form:
-# name=value
-# For example adding
-# "sideeffect=@par Side Effects:\n"
-# will allow you to put the command \sideeffect (or @sideeffect) in the
-# documentation, which will result in a user-defined paragraph with heading
-# "Side Effects:". You can put \n's in the value part of an alias to insert
-# newlines.
-
-ALIASES =
-
-# This tag can be used to specify a number of word-keyword mappings (TCL only).
-# A mapping has the form "name=value". For example adding "class=itcl::class"
-# will allow you to use the command class in the itcl::class meaning.
-
-TCL_SUBST =
-
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
-# only. Doxygen will then generate output that is more tailored for C. For
-# instance, some of the names that are used will be different. The list of all
-# members will be omitted, etc.
-# The default value is: NO.
-
-OPTIMIZE_OUTPUT_FOR_C = NO
-
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
-# Python sources only. Doxygen will then generate output that is more tailored
-# for that language. For instance, namespaces will be presented as packages,
-# qualified scopes will look different, etc.
-# The default value is: NO.
-
-OPTIMIZE_OUTPUT_JAVA = NO
-
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources. Doxygen will then generate output that is tailored for Fortran.
-# The default value is: NO.
-
-OPTIMIZE_FOR_FORTRAN = NO
-
-# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for VHDL.
-# The default value is: NO.
-
-OPTIMIZE_OUTPUT_VHDL = NO
-
-# Doxygen selects the parser to use depending on the extension of the files it
-# parses. With this tag you can assign which parser to use for a given
-# extension. Doxygen has a built-in mapping, but you can override or extend it
-# using this tag. The format is ext=language, where ext is a file extension, and
-# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
-# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
-# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
-# (default is Fortran), use: inc=Fortran f=C.
-#
-# Note For files without extension you can use no_extension as a placeholder.
-#
-# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
-# the files are not read by doxygen.
-
-EXTENSION_MAPPING =
-
-# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
-# according to the Markdown format, which allows for more readable
-# documentation. See http://daringfireball.net/projects/markdown/ for details.
-# The output of markdown processing is further processed by doxygen, so you can
-# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
-# case of backward compatibilities issues.
-# The default value is: YES.
-
-MARKDOWN_SUPPORT = YES
-
-# When enabled doxygen tries to link words that correspond to documented
-# classes, or namespaces to their corresponding documentation. Such a link can
-# be prevented in individual cases by by putting a % sign in front of the word
-# or globally by setting AUTOLINK_SUPPORT to NO.
-# The default value is: YES.
-
-AUTOLINK_SUPPORT = YES
-
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
-# to include (a tag file for) the STL sources as input, then you should set this
-# tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string);
-# versus func(std::string) {}). This also make the inheritance and collaboration
-# diagrams that involve STL classes more complete and accurate.
-# The default value is: NO.
-
-BUILTIN_STL_SUPPORT = NO
-
-# If you use Microsoft's C++/CLI language, you should set this option to YES to
-# enable parsing support.
-# The default value is: NO.
-
-CPP_CLI_SUPPORT = NO
-
-# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
-# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
-# will parse them like normal C++ but will assume all classes use public instead
-# of private inheritance when no explicit protection keyword is present.
-# The default value is: NO.
-
-SIP_SUPPORT = NO
-
-# For Microsoft's IDL there are propget and propput attributes to indicate
-# getter and setter methods for a property. Setting this option to YES will make
-# doxygen to replace the get and set methods by a property in the documentation.
-# This will only work if the methods are indeed getting or setting a simple
-# type. If this is not the case, or you want to show the methods anyway, you
-# should set this option to NO.
-# The default value is: YES.
-
-IDL_PROPERTY_SUPPORT = YES
-
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES, then doxygen will reuse the documentation of the first
-# member in the group (if any) for the other members of the group. By default
-# all members of a group must be documented explicitly.
-# The default value is: NO.
-
-DISTRIBUTE_GROUP_DOC = NO
-
-# Set the SUBGROUPING tag to YES to allow class member groups of the same type
-# (for instance a group of public functions) to be put as a subgroup of that
-# type (e.g. under the Public Functions section). Set it to NO to prevent
-# subgrouping. Alternatively, this can be done per class using the
-# \nosubgrouping command.
-# The default value is: YES.
-
-SUBGROUPING = YES
-
-# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
-# are shown inside the group in which they are included (e.g. using \ingroup)
-# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
-# and RTF).
-#
-# Note that this feature does not work in combination with
-# SEPARATE_MEMBER_PAGES.
-# The default value is: NO.
-
-INLINE_GROUPED_CLASSES = NO
-
-# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
-# with only public data fields or simple typedef fields will be shown inline in
-# the documentation of the scope in which they are defined (i.e. file,
-# namespace, or group documentation), provided this scope is documented. If set
-# to NO, structs, classes, and unions are shown on a separate page (for HTML and
-# Man pages) or section (for LaTeX and RTF).
-# The default value is: NO.
-
-INLINE_SIMPLE_STRUCTS = NO
-
-# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
-# enum is documented as struct, union, or enum with the name of the typedef. So
-# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
-# with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically be
-# useful for C code in case the coding convention dictates that all compound
-# types are typedef'ed and only the typedef is referenced, never the tag name.
-# The default value is: NO.
-
-TYPEDEF_HIDES_STRUCT = NO
-
-# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
-# cache is used to resolve symbols given their name and scope. Since this can be
-# an expensive process and often the same symbol appears multiple times in the
-# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
-# doxygen will become slower. If the cache is too large, memory is wasted. The
-# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
-# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
-# symbols. At the end of a run doxygen will report the cache usage and suggest
-# the optimal cache size from a speed point of view.
-# Minimum value: 0, maximum value: 9, default value: 0.
-
-LOOKUP_CACHE_SIZE = 0
-
-#---------------------------------------------------------------------------
-# Build related configuration options
-#---------------------------------------------------------------------------
-
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
-# documentation are documented, even if no documentation was available. Private
-# class members and static file members will be hidden unless the
-# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
-# Note: This will also disable the warnings about undocumented members that are
-# normally produced when WARNINGS is set to YES.
-# The default value is: NO.
-
-EXTRACT_ALL = NO
-
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
-# be included in the documentation.
-# The default value is: NO.
-
-EXTRACT_PRIVATE = NO
-
-# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
-# scope will be included in the documentation.
-# The default value is: NO.
-
-EXTRACT_PACKAGE = NO
-
-# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
-# included in the documentation.
-# The default value is: NO.
-
-EXTRACT_STATIC = NO
-
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
-# locally in source files will be included in the documentation. If set to NO
-# only classes defined in header files are included. Does not have any effect
-# for Java sources.
-# The default value is: YES.
-
-EXTRACT_LOCAL_CLASSES = YES
-
-# This flag is only useful for Objective-C code. When set to YES local methods,
-# which are defined in the implementation section but not in the interface are
-# included in the documentation. If set to NO only methods in the interface are
-# included.
-# The default value is: NO.
-
-EXTRACT_LOCAL_METHODS = NO
-
-# If this flag is set to YES, the members of anonymous namespaces will be
-# extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base name of
-# the file that contains the anonymous namespace. By default anonymous namespace
-# are hidden.
-# The default value is: NO.
-
-EXTRACT_ANON_NSPACES = NO
-
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
-# undocumented members inside documented classes or files. If set to NO these
-# members will be included in the various overviews, but no documentation
-# section is generated. This option has no effect if EXTRACT_ALL is enabled.
-# The default value is: NO.
-
-HIDE_UNDOC_MEMBERS = NO
-
-# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy. If set
-# to NO these classes will be included in the various overviews. This option has
-# no effect if EXTRACT_ALL is enabled.
-# The default value is: NO.
-
-HIDE_UNDOC_CLASSES = NO
-
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
-# (class|struct|union) declarations. If set to NO these declarations will be
-# included in the documentation.
-# The default value is: NO.
-
-HIDE_FRIEND_COMPOUNDS = NO
-
-# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
-# documentation blocks found inside the body of a function. If set to NO these
-# blocks will be appended to the function's detailed documentation block.
-# The default value is: NO.
-
-HIDE_IN_BODY_DOCS = NO
-
-# The INTERNAL_DOCS tag determines if documentation that is typed after a
-# \internal command is included. If the tag is set to NO then the documentation
-# will be excluded. Set it to YES to include the internal documentation.
-# The default value is: NO.
-
-INTERNAL_DOCS = NO
-
-# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
-# names in lower-case letters. If set to YES upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
-# and Mac users are advised to set this option to NO.
-# The default value is: system dependent.
-
-CASE_SENSE_NAMES = YES
-
-# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
-# their full class and namespace scopes in the documentation. If set to YES the
-# scope will be hidden.
-# The default value is: NO.
-
-HIDE_SCOPE_NAMES = NO
-
-# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
-# the files that are included by a file in the documentation of that file.
-# The default value is: YES.
-
-SHOW_INCLUDE_FILES = YES
-
-# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
-# grouped member an include statement to the documentation, telling the reader
-# which file to include in order to use the member.
-# The default value is: NO.
-
-SHOW_GROUPED_MEMB_INC = NO
-
-# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
-# files with double quotes in the documentation rather than with sharp brackets.
-# The default value is: NO.
-
-FORCE_LOCAL_INCLUDES = NO
-
-# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
-# documentation for inline members.
-# The default value is: YES.
-
-INLINE_INFO = YES
-
-# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
-# (detailed) documentation of file and class members alphabetically by member
-# name. If set to NO the members will appear in declaration order.
-# The default value is: YES.
-
-SORT_MEMBER_DOCS = YES
-
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
-# descriptions of file, namespace and class members alphabetically by member
-# name. If set to NO the members will appear in declaration order. Note that
-# this will also influence the order of the classes in the class list.
-# The default value is: NO.
-
-SORT_BRIEF_DOCS = NO
-
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
-# (brief and detailed) documentation of class members so that constructors and
-# destructors are listed first. If set to NO the constructors will appear in the
-# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
-# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
-# member documentation.
-# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
-# detailed member documentation.
-# The default value is: NO.
-
-SORT_MEMBERS_CTORS_1ST = NO
-
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
-# of group names into alphabetical order. If set to NO the group names will
-# appear in their defined order.
-# The default value is: NO.
-
-SORT_GROUP_NAMES = NO
-
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
-# fully-qualified names, including namespaces. If set to NO, the class list will
-# be sorted only by class name, not including the namespace part.
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the alphabetical
-# list.
-# The default value is: NO.
-
-SORT_BY_SCOPE_NAME = NO
-
-# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
-# type resolution of all parameters of a function it will reject a match between
-# the prototype and the implementation of a member function even if there is
-# only one candidate or it is obvious which candidate to choose by doing a
-# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
-# accept a match between prototype and implementation in such cases.
-# The default value is: NO.
-
-STRICT_PROTO_MATCHING = NO
-
-# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
-# todo list. This list is created by putting \todo commands in the
-# documentation.
-# The default value is: YES.
-
-GENERATE_TODOLIST = YES
-
-# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
-# test list. This list is created by putting \test commands in the
-# documentation.
-# The default value is: YES.
-
-GENERATE_TESTLIST = YES
-
-# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
-# list. This list is created by putting \bug commands in the documentation.
-# The default value is: YES.
-
-GENERATE_BUGLIST = YES
-
-# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
-# the deprecated list. This list is created by putting \deprecated commands in
-# the documentation.
-# The default value is: YES.
-
-GENERATE_DEPRECATEDLIST= YES
-
-# The ENABLED_SECTIONS tag can be used to enable conditional documentation
-# sections, marked by \if <section_label> ... \endif and \cond <section_label>
-# ... \endcond blocks.
-
-ENABLED_SECTIONS =
-
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
-# initial value of a variable or macro / define can have for it to appear in the
-# documentation. If the initializer consists of more lines than specified here
-# it will be hidden. Use a value of 0 to hide initializers completely. The
-# appearance of the value of individual variables and macros / defines can be
-# controlled using \showinitializer or \hideinitializer command in the
-# documentation regardless of this setting.
-# Minimum value: 0, maximum value: 10000, default value: 30.
-
-MAX_INITIALIZER_LINES = 30
-
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
-# the bottom of the documentation of classes and structs. If set to YES the list
-# will mention the files that were used to generate the documentation.
-# The default value is: YES.
-
-SHOW_USED_FILES = YES
-
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
-# will remove the Files entry from the Quick Index and from the Folder Tree View
-# (if specified).
-# The default value is: YES.
-
-SHOW_FILES = YES
-
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
-# page. This will remove the Namespaces entry from the Quick Index and from the
-# Folder Tree View (if specified).
-# The default value is: YES.
-
-SHOW_NAMESPACES = YES
-
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that
-# doxygen should invoke to get the current version for each file (typically from
-# the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command command input-file, where command is the value of the
-# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
-# by doxygen. Whatever the program writes to standard output is used as the file
-# version. For an example see the documentation.
-
-FILE_VERSION_FILTER =
-
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
-# by doxygen. The layout file controls the global structure of the generated
-# output files in an output format independent way. To create the layout file
-# that represents doxygen's defaults, run doxygen with the -l option. You can
-# optionally specify a file name after the option, if omitted DoxygenLayout.xml
-# will be used as the name of the layout file.
-#
-# Note that if you run doxygen from a directory containing a file called
-# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
-# tag is left empty.
-
-LAYOUT_FILE =
-
-# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
-# the reference definitions. This must be a list of .bib files. The .bib
-# extension is automatically appended if omitted. This requires the bibtex tool
-# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
-# For LaTeX the style of the bibliography can be controlled using
-# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
-# search path. Do not use file names with spaces, bibtex cannot handle them. See
-# also \cite for info how to create references.
-
-CITE_BIB_FILES =
-
-#---------------------------------------------------------------------------
-# Configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-
-# The QUIET tag can be used to turn on/off the messages that are generated to
-# standard output by doxygen. If QUIET is set to YES this implies that the
-# messages are off.
-# The default value is: NO.
-
-QUIET = NO
-
-# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
-# this implies that the warnings are on.
-#
-# Tip: Turn warnings on while writing the documentation.
-# The default value is: YES.
-
-WARNINGS = YES
-
-# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
-# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
-# will automatically be disabled.
-# The default value is: YES.
-
-WARN_IF_UNDOCUMENTED = YES
-
-# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some parameters
-# in a documented function, or documenting parameters that don't exist or using
-# markup commands wrongly.
-# The default value is: YES.
-
-WARN_IF_DOC_ERROR = YES
-
-# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
-# are documented, but have no documentation for their parameters or return
-# value. If set to NO doxygen will only warn about wrong or incomplete parameter
-# documentation, but not about the absence of documentation.
-# The default value is: NO.
-
-WARN_NO_PARAMDOC = NO
-
-# The WARN_FORMAT tag determines the format of the warning messages that doxygen
-# can produce. The string should contain the $file, $line, and $text tags, which
-# will be replaced by the file and line number from which the warning originated
-# and the warning text. Optionally the format may contain $version, which will
-# be replaced by the version of the file (if it could be obtained via
-# FILE_VERSION_FILTER)
-# The default value is: $file:$line: $text.
-
-WARN_FORMAT = "$file:$line: $text"
-
-# The WARN_LOGFILE tag can be used to specify a file to which warning and error
-# messages should be written. If left blank the output is written to standard
-# error (stderr).
-
-WARN_LOGFILE =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the input files
-#---------------------------------------------------------------------------
-
-# The INPUT tag is used to specify the files and/or directories that contain
-# documented source files. You may enter file names like myfile.cpp or
-# directories like /usr/src/myproject. Separate the files or directories with
-# spaces.
-# Note: If this tag is empty the current directory is searched.
-
-INPUT =
-
-# This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
-# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
-# documentation (see: http://www.gnu.org/software/libiconv) for the list of
-# possible encodings.
-# The default value is: UTF-8.
-
-INPUT_ENCODING = UTF-8
-
-# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
-# *.h) to filter out the source-files in the directories. If left blank the
-# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
-# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
-# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
-# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
-# *.qsf, *.as and *.js.
-
-FILE_PATTERNS =
-
-# The RECURSIVE tag can be used to specify whether or not subdirectories should
-# be searched for input files as well.
-# The default value is: NO.
-
-RECURSIVE = NO
-
-# The EXCLUDE tag can be used to specify files and/or directories that should be
-# excluded from the INPUT source files. This way you can easily exclude a
-# subdirectory from a directory tree whose root is specified with the INPUT tag.
-#
-# Note that relative paths are relative to the directory from which doxygen is
-# run.
-
-EXCLUDE =
-
-# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
-# directories that are symbolic links (a Unix file system feature) are excluded
-# from the input.
-# The default value is: NO.
-
-EXCLUDE_SYMLINKS = NO
-
-# If the value of the INPUT tag contains directories, you can use the
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories.
-#
-# Note that the wildcards are matched against the file with absolute path, so to
-# exclude all test directories for example use the pattern */test/*
-
-EXCLUDE_PATTERNS =
-
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
-# (namespaces, classes, functions, etc.) that should be excluded from the
-# output. The symbol name can be a fully qualified name, a word, or if the
-# wildcard * is used, a substring. Examples: ANamespace, AClass,
-# AClass::ANamespace, ANamespace::*Test
-#
-# Note that the wildcards are matched against the file with absolute path, so to
-# exclude all test directories use the pattern */test/*
-
-EXCLUDE_SYMBOLS =
-
-# The EXAMPLE_PATH tag can be used to specify one or more files or directories
-# that contain example code fragments that are included (see the \include
-# command).
-
-EXAMPLE_PATH =
-
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
-# *.h) to filter out the source-files in the directories. If left blank all
-# files are included.
-
-EXAMPLE_PATTERNS =
-
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude commands
-# irrespective of the value of the RECURSIVE tag.
-# The default value is: NO.
-
-EXAMPLE_RECURSIVE = NO
-
-# The IMAGE_PATH tag can be used to specify one or more files or directories
-# that contain images that are to be included in the documentation (see the
-# \image command).
-
-IMAGE_PATH =
-
-# The INPUT_FILTER tag can be used to specify a program that doxygen should
-# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command:
-#
-# <filter> <input-file>
-#
-# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
-# name of an input file. Doxygen will then use the output that the filter
-# program writes to standard output. If FILTER_PATTERNS is specified, this tag
-# will be ignored.
-#
-# Note that the filter must not add or remove lines; it is applied before the
-# code is scanned, but not when the output code is generated. If lines are added
-# or removed, the anchors will not be placed correctly.
-
-INPUT_FILTER =
-
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis. Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match. The filters are a list of the form: pattern=filter
-# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
-# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
-# patterns match the file name, INPUT_FILTER is applied.
-
-FILTER_PATTERNS =
-
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER ) will also be used to filter the input files that are used for
-# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
-# The default value is: NO.
-
-FILTER_SOURCE_FILES = NO
-
-# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
-# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
-# it is also possible to disable source filtering for a specific pattern using
-# *.ext= (so without naming a filter).
-# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
-
-FILTER_SOURCE_PATTERNS =
-
-# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
-# is part of the input, its contents will be placed on the main page
-# (index.html). This can be useful if you have a project on for instance GitHub
-# and want to reuse the introduction page also for the doxygen output.
-
-USE_MDFILE_AS_MAINPAGE =
-
-#---------------------------------------------------------------------------
-# Configuration options related to source browsing
-#---------------------------------------------------------------------------
-
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
-# generated. Documented entities will be cross-referenced with these sources.
-#
-# Note: To get rid of all source code in the generated output, make sure that
-# also VERBATIM_HEADERS is set to NO.
-# The default value is: NO.
-
-SOURCE_BROWSER = NO
-
-# Setting the INLINE_SOURCES tag to YES will include the body of functions,
-# classes and enums directly into the documentation.
-# The default value is: NO.
-
-INLINE_SOURCES = NO
-
-# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
-# special comment blocks from generated source code fragments. Normal C, C++ and
-# Fortran comments will always remain visible.
-# The default value is: YES.
-
-STRIP_CODE_COMMENTS = YES
-
-# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
-# function all documented functions referencing it will be listed.
-# The default value is: NO.
-
-REFERENCED_BY_RELATION = NO
-
-# If the REFERENCES_RELATION tag is set to YES then for each documented function
-# all documented entities called/used by that function will be listed.
-# The default value is: NO.
-
-REFERENCES_RELATION = NO
-
-# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
-# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
-# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
-# link to the documentation.
-# The default value is: YES.
-
-REFERENCES_LINK_SOURCE = YES
-
-# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
-# source code will show a tooltip with additional information such as prototype,
-# brief description and links to the definition and documentation. Since this
-# will make the HTML file larger and loading of large files a bit slower, you
-# can opt to disable this feature.
-# The default value is: YES.
-# This tag requires that the tag SOURCE_BROWSER is set to YES.
-
-SOURCE_TOOLTIPS = YES
-
-# If the USE_HTAGS tag is set to YES then the references to source code will
-# point to the HTML generated by the htags(1) tool instead of doxygen built-in
-# source browser. The htags tool is part of GNU's global source tagging system
-# (see http://www.gnu.org/software/global/global.html). You will need version
-# 4.8.6 or higher.
-#
-# To use it do the following:
-# - Install the latest version of global
-# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
-# - Make sure the INPUT points to the root of the source tree
-# - Run doxygen as normal
-#
-# Doxygen will invoke htags (and that will in turn invoke gtags), so these
-# tools must be available from the command line (i.e. in the search path).
-#
-# The result: instead of the source browser generated by doxygen, the links to
-# source code will now point to the output of htags.
-# The default value is: NO.
-# This tag requires that the tag SOURCE_BROWSER is set to YES.
-
-USE_HTAGS = NO
-
-# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
-# verbatim copy of the header file for each class for which an include is
-# specified. Set to NO to disable this.
-# See also: Section \class.
-# The default value is: YES.
-
-VERBATIM_HEADERS = YES
-
-#---------------------------------------------------------------------------
-# Configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
-
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
-# compounds will be generated. Enable this if the project contains a lot of
-# classes, structs, unions or interfaces.
-# The default value is: YES.
-
-ALPHABETICAL_INDEX = YES
-
-# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
-# which the alphabetical index list will be split.
-# Minimum value: 1, maximum value: 20, default value: 5.
-# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
-
-COLS_IN_ALPHA_INDEX = 5
-
-# In case all classes in a project start with a common prefix, all classes will
-# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
-# can be used to specify a prefix (or a list of prefixes) that should be ignored
-# while generating the index headers.
-# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
-
-IGNORE_PREFIX =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the HTML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
-# The default value is: YES.
-
-GENERATE_HTML = YES
-
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
-# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
-# it.
-# The default directory is: html.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_OUTPUT = html
-
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
-# generated HTML page (for example: .htm, .php, .asp).
-# The default value is: .html.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_FILE_EXTENSION = .html
-
-# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
-# each generated HTML page. If the tag is left blank doxygen will generate a
-# standard header.
-#
-# To get valid HTML the header file that includes any scripts and style sheets
-# that doxygen needs, which is dependent on the configuration options used (e.g.
-# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
-# default header using
-# doxygen -w html new_header.html new_footer.html new_stylesheet.css
-# YourConfigFile
-# and then modify the file new_header.html. See also section "Doxygen usage"
-# for information on how to generate the default header that doxygen normally
-# uses.
-# Note: The header is subject to change so you typically have to regenerate the
-# default header when upgrading to a newer version of doxygen. For a description
-# of the possible markers and block names see the documentation.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_HEADER =
-
-# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
-# generated HTML page. If the tag is left blank doxygen will generate a standard
-# footer. See HTML_HEADER for more information on how to generate a default
-# footer and what special commands can be used inside the footer. See also
-# section "Doxygen usage" for information on how to generate the default footer
-# that doxygen normally uses.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_FOOTER =
-
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
-# sheet that is used by each HTML page. It can be used to fine-tune the look of
-# the HTML output. If left blank doxygen will generate a default style sheet.
-# See also section "Doxygen usage" for information on how to generate the style
-# sheet that doxygen normally uses.
-# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
-# it is more robust and this tag (HTML_STYLESHEET) will in the future become
-# obsolete.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_STYLESHEET =
-
-# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
-# defined cascading style sheet that is included after the standard style sheets
-# created by doxygen. Using this option one can overrule certain style aspects.
-# This is preferred over using HTML_STYLESHEET since it does not replace the
-# standard style sheet and is therefor more robust against future updates.
-# Doxygen will copy the style sheet file to the output directory. For an example
-# see the documentation.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_EXTRA_STYLESHEET =
-
-# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
-# other source files which should be copied to the HTML output directory. Note
-# that these files will be copied to the base HTML output directory. Use the
-# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
-# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
-# files will be copied as-is; there are no commands or markers available.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_EXTRA_FILES =
-
-# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
-# will adjust the colors in the stylesheet and background images according to
-# this color. Hue is specified as an angle on a colorwheel, see
-# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
-# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
-# purple, and 360 is red again.
-# Minimum value: 0, maximum value: 359, default value: 220.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_COLORSTYLE_HUE = 220
-
-# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
-# in the HTML output. For a value of 0 the output will use grayscales only. A
-# value of 255 will produce the most vivid colors.
-# Minimum value: 0, maximum value: 255, default value: 100.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_COLORSTYLE_SAT = 100
-
-# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
-# luminance component of the colors in the HTML output. Values below 100
-# gradually make the output lighter, whereas values above 100 make the output
-# darker. The value divided by 100 is the actual gamma applied, so 80 represents
-# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
-# change the gamma.
-# Minimum value: 40, maximum value: 240, default value: 80.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_COLORSTYLE_GAMMA = 80
-
-# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
-# page will contain the date and time when the page was generated. Setting this
-# to NO can help when comparing the output of multiple runs.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_TIMESTAMP = YES
-
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
-# documentation will contain sections that can be hidden and shown after the
-# page has loaded.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_DYNAMIC_SECTIONS = NO
-
-# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
-# shown in the various tree structured indices initially; the user can expand
-# and collapse entries dynamically later on. Doxygen will expand the tree to
-# such a level that at most the specified number of entries are visible (unless
-# a fully collapsed tree already exceeds this amount). So setting the number of
-# entries 1 will produce a full collapsed tree by default. 0 is a special value
-# representing an infinite number of entries and will result in a full expanded
-# tree by default.
-# Minimum value: 0, maximum value: 9999, default value: 100.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_INDEX_NUM_ENTRIES = 100
-
-# If the GENERATE_DOCSET tag is set to YES, additional index files will be
-# generated that can be used as input for Apple's Xcode 3 integrated development
-# environment (see: http://developer.apple.com/tools/xcode/), introduced with
-# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
-# Makefile in the HTML output directory. Running make will produce the docset in
-# that directory and running make install will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
-# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
-# for more information.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_DOCSET = NO
-
-# This tag determines the name of the docset feed. A documentation feed provides
-# an umbrella under which multiple documentation sets from a single provider
-# (such as a company or product suite) can be grouped.
-# The default value is: Doxygen generated docs.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_FEEDNAME = "Doxygen generated docs"
-
-# This tag specifies a string that should uniquely identify the documentation
-# set bundle. This should be a reverse domain-name style string, e.g.
-# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
-# The default value is: org.doxygen.Project.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_BUNDLE_ID = org.doxygen.Project
-
-# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
-# the documentation publisher. This should be a reverse domain-name style
-# string, e.g. com.mycompany.MyDocSet.documentation.
-# The default value is: org.doxygen.Publisher.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_PUBLISHER_ID = org.doxygen.Publisher
-
-# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
-# The default value is: Publisher.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_PUBLISHER_NAME = Publisher
-
-# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
-# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
-# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
-# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
-# Windows.
-#
-# The HTML Help Workshop contains a compiler that can convert all HTML output
-# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
-# files are now used as the Windows 98 help format, and will replace the old
-# Windows help format (.hlp) on all Windows platforms in the future. Compressed
-# HTML files also contain an index, a table of contents, and you can search for
-# words in the documentation. The HTML workshop also contains a viewer for
-# compressed HTML files.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_HTMLHELP = NO
-
-# The CHM_FILE tag can be used to specify the file name of the resulting .chm
-# file. You can add a path in front of the file if the result should not be
-# written to the html output directory.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-CHM_FILE =
-
-# The HHC_LOCATION tag can be used to specify the location (absolute path
-# including file name) of the HTML help compiler ( hhc.exe). If non-empty
-# doxygen will try to run the HTML help compiler on the generated index.hhp.
-# The file has to be specified with full path.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-HHC_LOCATION =
-
-# The GENERATE_CHI flag controls if a separate .chi index file is generated (
-# YES) or that it should be included in the master .chm file ( NO).
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-GENERATE_CHI = NO
-
-# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
-# and project file content.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-CHM_INDEX_ENCODING =
-
-# The BINARY_TOC flag controls whether a binary table of contents is generated (
-# YES) or a normal table of contents ( NO) in the .chm file.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-BINARY_TOC = NO
-
-# The TOC_EXPAND flag can be set to YES to add extra items for group members to
-# the table of contents of the HTML help documentation and to the tree view.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-TOC_EXPAND = NO
-
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
-# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
-# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
-# (.qch) of the generated HTML documentation.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_QHP = NO
-
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
-# the file name of the resulting .qch file. The path specified is relative to
-# the HTML output folder.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QCH_FILE =
-
-# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
-# Project output. For more information please see Qt Help Project / Namespace
-# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
-# The default value is: org.doxygen.Project.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_NAMESPACE = org.doxygen.Project
-
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
-# Help Project output. For more information please see Qt Help Project / Virtual
-# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
-# folders).
-# The default value is: doc.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_VIRTUAL_FOLDER = doc
-
-# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
-# filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
-# filters).
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_CUST_FILTER_NAME =
-
-# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
-# custom filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
-# filters).
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_CUST_FILTER_ATTRS =
-
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
-# project's filter section matches. Qt Help Project / Filter Attributes (see:
-# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_SECT_FILTER_ATTRS =
-
-# The QHG_LOCATION tag can be used to specify the location of Qt's
-# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
-# generated .qhp file.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHG_LOCATION =
-
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
-# generated, together with the HTML files, they form an Eclipse help plugin. To
-# install this plugin and make it available under the help contents menu in
-# Eclipse, the contents of the directory containing the HTML and XML files needs
-# to be copied into the plugins directory of eclipse. The name of the directory
-# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
-# After copying Eclipse needs to be restarted before the help appears.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_ECLIPSEHELP = NO
-
-# A unique identifier for the Eclipse help plugin. When installing the plugin
-# the directory name containing the HTML and XML files should also have this
-# name. Each documentation set should have its own identifier.
-# The default value is: org.doxygen.Project.
-# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
-
-ECLIPSE_DOC_ID = org.doxygen.Project
-
-# If you want full control over the layout of the generated HTML pages it might
-# be necessary to disable the index and replace it with your own. The
-# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
-# of each HTML page. A value of NO enables the index and the value YES disables
-# it. Since the tabs in the index contain the same information as the navigation
-# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-DISABLE_INDEX = NO
-
-# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information. If the tag
-# value is set to YES, a side panel will be generated containing a tree-like
-# index structure (just like the one that is generated for HTML Help). For this
-# to work a browser that supports JavaScript, DHTML, CSS and frames is required
-# (i.e. any modern browser). Windows users are probably better off using the
-# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
-# further fine-tune the look of the index. As an example, the default style
-# sheet generated by doxygen has an example that shows how to put an image at
-# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
-# the same information as the tab index, you could consider setting
-# DISABLE_INDEX to YES when enabling this option.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_TREEVIEW = NO
-
-# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
-# doxygen will group on one line in the generated HTML documentation.
-#
-# Note that a value of 0 will completely suppress the enum values from appearing
-# in the overview section.
-# Minimum value: 0, maximum value: 20, default value: 4.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-ENUM_VALUES_PER_LINE = 4
-
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
-# to set the initial width (in pixels) of the frame in which the tree is shown.
-# Minimum value: 0, maximum value: 1500, default value: 250.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-TREEVIEW_WIDTH = 250
-
-# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
-# external symbols imported via tag files in a separate window.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-EXT_LINKS_IN_WINDOW = NO
-
-# Use this tag to change the font size of LaTeX formulas included as images in
-# the HTML documentation. When you change the font size after a successful
-# doxygen run you need to manually remove any form_*.png images from the HTML
-# output directory to force them to be regenerated.
-# Minimum value: 8, maximum value: 50, default value: 10.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-FORMULA_FONTSIZE = 10
-
-# Use the FORMULA_TRANPARENT tag to determine whether or not the images
-# generated for formulas are transparent PNGs. Transparent PNGs are not
-# supported properly for IE 6.0, but are supported on all modern browsers.
-#
-# Note that when changing this option you need to delete any form_*.png files in
-# the HTML output directory before the changes have effect.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-FORMULA_TRANSPARENT = YES
-
-# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
-# http://www.mathjax.org) which uses client side Javascript for the rendering
-# instead of using prerendered bitmaps. Use this if you do not have LaTeX
-# installed or if you want to formulas look prettier in the HTML output. When
-# enabled you may also need to install MathJax separately and configure the path
-# to it using the MATHJAX_RELPATH option.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-USE_MATHJAX = NO
-
-# When MathJax is enabled you can set the default output format to be used for
-# the MathJax output. See the MathJax site (see:
-# http://docs.mathjax.org/en/latest/output.html) for more details.
-# Possible values are: HTML-CSS (which is slower, but has the best
-# compatibility), NativeMML (i.e. MathML) and SVG.
-# The default value is: HTML-CSS.
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_FORMAT = HTML-CSS
-
-# When MathJax is enabled you need to specify the location relative to the HTML
-# output directory using the MATHJAX_RELPATH option. The destination directory
-# should contain the MathJax.js script. For instance, if the mathjax directory
-# is located at the same level as the HTML output directory, then
-# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
-# Content Delivery Network so you can quickly see the result without installing
-# MathJax. However, it is strongly recommended to install a local copy of
-# MathJax from http://www.mathjax.org before deployment.
-# The default value is: http://cdn.mathjax.org/mathjax/latest.
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
-
-# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
-# extension names that should be enabled during MathJax rendering. For example
-# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_EXTENSIONS =
-
-# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
-# of code that will be used on startup of the MathJax code. See the MathJax site
-# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
-# example see the documentation.
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_CODEFILE =
-
-# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
-# the HTML output. The underlying search engine uses javascript and DHTML and
-# should work on any modern browser. Note that when using HTML help
-# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
-# there is already a search function so this one should typically be disabled.
-# For large projects the javascript based search engine can be slow, then
-# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
-# search using the keyboard; to jump to the search box use <access key> + S
-# (what the <access key> is depends on the OS and browser, but it is typically
-# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
-# key> to jump into the search results window, the results can be navigated
-# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
-# the search. The filter options can be selected when the cursor is inside the
-# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
-# to select a filter and <Enter> or <escape> to activate or cancel the filter
-# option.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-SEARCHENGINE = YES
-
-# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a web server instead of a web client using Javascript. There
-# are two flavours of web server based searching depending on the
-# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
-# searching and an index file used by the script. When EXTERNAL_SEARCH is
-# enabled the indexing and searching needs to be provided by external tools. See
-# the section "External Indexing and Searching" for details.
-# The default value is: NO.
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-SERVER_BASED_SEARCH = NO
-
-# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
-# script for searching. Instead the search results are written to an XML file
-# which needs to be processed by an external indexer. Doxygen will invoke an
-# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
-# search results.
-#
-# Doxygen ships with an example indexer ( doxyindexer) and search engine
-# (doxysearch.cgi) which are based on the open source search engine library
-# Xapian (see: http://xapian.org/).
-#
-# See the section "External Indexing and Searching" for details.
-# The default value is: NO.
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-EXTERNAL_SEARCH = NO
-
-# The SEARCHENGINE_URL should point to a search engine hosted by a web server
-# which will return the search results when EXTERNAL_SEARCH is enabled.
-#
-# Doxygen ships with an example indexer ( doxyindexer) and search engine
-# (doxysearch.cgi) which are based on the open source search engine library
-# Xapian (see: http://xapian.org/). See the section "External Indexing and
-# Searching" for details.
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-SEARCHENGINE_URL =
-
-# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
-# search data is written to a file for indexing by an external tool. With the
-# SEARCHDATA_FILE tag the name of this file can be specified.
-# The default file is: searchdata.xml.
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-SEARCHDATA_FILE = searchdata.xml
-
-# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
-# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
-# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
-# projects and redirect the results back to the right project.
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-EXTERNAL_SEARCH_ID =
-
-# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
-# projects other than the one defined by this configuration file, but that are
-# all added to the same external search index. Each project needs to have a
-# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
-# to a relative location where the documentation can be found. The format is:
-# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-EXTRA_SEARCH_MAPPINGS =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
-# The default value is: YES.
-
-GENERATE_LATEX = YES
-
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
-# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
-# it.
-# The default directory is: latex.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_OUTPUT = latex
-
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
-# invoked.
-#
-# Note that when enabling USE_PDFLATEX this option is only used for generating
-# bitmaps for formulas in the HTML output, but not in the Makefile that is
-# written to the output directory.
-# The default file is: latex.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_CMD_NAME = latex
-
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
-# index for LaTeX.
-# The default file is: makeindex.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-MAKEINDEX_CMD_NAME = makeindex
-
-# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
-# documents. This may be useful for small projects and may help to save some
-# trees in general.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-COMPACT_LATEX = NO
-
-# The PAPER_TYPE tag can be used to set the paper type that is used by the
-# printer.
-# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
-# 14 inches) and executive (7.25 x 10.5 inches).
-# The default value is: a4.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-PAPER_TYPE = a4
-
-# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
-# that should be included in the LaTeX output. To get the times font for
-# instance you can specify
-# EXTRA_PACKAGES=times
-# If left blank no extra packages will be included.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-EXTRA_PACKAGES =
-
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
-# generated LaTeX document. The header should contain everything until the first
-# chapter. If it is left blank doxygen will generate a standard header. See
-# section "Doxygen usage" for information on how to let doxygen write the
-# default header to a separate file.
-#
-# Note: Only use a user-defined header if you know what you are doing! The
-# following commands have a special meaning inside the header: $title,
-# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
-# replace them by respectively the title of the page, the current date and time,
-# only the current date, the version number of doxygen, the project name (see
-# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_HEADER =
-
-# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
-# generated LaTeX document. The footer should contain everything after the last
-# chapter. If it is left blank doxygen will generate a standard footer.
-#
-# Note: Only use a user-defined footer if you know what you are doing!
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_FOOTER =
-
-# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
-# other source files which should be copied to the LATEX_OUTPUT output
-# directory. Note that the files will be copied as-is; there are no commands or
-# markers available.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_EXTRA_FILES =
-
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
-# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
-# contain links (just like the HTML output) instead of page references. This
-# makes the output suitable for online browsing using a PDF viewer.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-PDF_HYPERLINKS = YES
-
-# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
-# the PDF file directly from the LaTeX files. Set this option to YES to get a
-# higher quality PDF documentation.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-USE_PDFLATEX = YES
-
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
-# command to the generated LaTeX files. This will instruct LaTeX to keep running
-# if errors occur, instead of asking the user for help. This option is also used
-# when generating formulas in HTML.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_BATCHMODE = NO
-
-# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
-# index chapters (such as File Index, Compound Index, etc.) in the output.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_HIDE_INDICES = NO
-
-# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
-# code with syntax highlighting in the LaTeX output.
-#
-# Note that which sources are shown also depends on other settings such as
-# SOURCE_BROWSER.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_SOURCE_CODE = NO
-
-# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
-# bibliography, e.g. plainnat, or ieeetr. See
-# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
-# The default value is: plain.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_BIB_STYLE = plain
-
-#---------------------------------------------------------------------------
-# Configuration options related to the RTF output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
-# RTF output is optimized for Word 97 and may not look too pretty with other RTF
-# readers/editors.
-# The default value is: NO.
-
-GENERATE_RTF = NO
-
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
-# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
-# it.
-# The default directory is: rtf.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-RTF_OUTPUT = rtf
-
-# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
-# documents. This may be useful for small projects and may help to save some
-# trees in general.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-COMPACT_RTF = NO
-
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
-# contain hyperlink fields. The RTF file will contain links (just like the HTML
-# output) instead of page references. This makes the output suitable for online
-# browsing using Word or some other Word compatible readers that support those
-# fields.
-#
-# Note: WordPad (write) and others do not support links.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-RTF_HYPERLINKS = NO
-
-# Load stylesheet definitions from file. Syntax is similar to doxygen's config
-# file, i.e. a series of assignments. You only have to provide replacements,
-# missing definitions are set to their default value.
-#
-# See also section "Doxygen usage" for information on how to generate the
-# default style sheet that doxygen normally uses.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-RTF_STYLESHEET_FILE =
-
-# Set optional variables used in the generation of an RTF document. Syntax is
-# similar to doxygen's config file. A template extensions file can be generated
-# using doxygen -e rtf extensionFile.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-RTF_EXTENSIONS_FILE =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the man page output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
-# classes and files.
-# The default value is: NO.
-
-GENERATE_MAN = NO
-
-# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
-# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
-# it. A directory man3 will be created inside the directory specified by
-# MAN_OUTPUT.
-# The default directory is: man.
-# This tag requires that the tag GENERATE_MAN is set to YES.
-
-MAN_OUTPUT = man
-
-# The MAN_EXTENSION tag determines the extension that is added to the generated
-# man pages. In case the manual section does not start with a number, the number
-# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
-# optional.
-# The default value is: .3.
-# This tag requires that the tag GENERATE_MAN is set to YES.
-
-MAN_EXTENSION = .3
-
-# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
-# will generate one additional man file for each entity documented in the real
-# man page(s). These additional files only source the real man page, but without
-# them the man command would be unable to find the correct page.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_MAN is set to YES.
-
-MAN_LINKS = NO
-
-#---------------------------------------------------------------------------
-# Configuration options related to the XML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
-# captures the structure of the code including all documentation.
-# The default value is: NO.
-
-GENERATE_XML = NO
-
-# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
-# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
-# it.
-# The default directory is: xml.
-# This tag requires that the tag GENERATE_XML is set to YES.
-
-XML_OUTPUT = xml
-
-# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a
-# validating XML parser to check the syntax of the XML files.
-# This tag requires that the tag GENERATE_XML is set to YES.
-
-XML_SCHEMA =
-
-# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
-# validating XML parser to check the syntax of the XML files.
-# This tag requires that the tag GENERATE_XML is set to YES.
-
-XML_DTD =
-
-# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
-# listings (including syntax highlighting and cross-referencing information) to
-# the XML output. Note that enabling this will significantly increase the size
-# of the XML output.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_XML is set to YES.
-
-XML_PROGRAMLISTING = YES
-
-#---------------------------------------------------------------------------
-# Configuration options related to the DOCBOOK output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
-# that can be used to generate PDF.
-# The default value is: NO.
-
-GENERATE_DOCBOOK = NO
-
-# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
-# front of it.
-# The default directory is: docbook.
-# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
-
-DOCBOOK_OUTPUT = docbook
-
-#---------------------------------------------------------------------------
-# Configuration options for the AutoGen Definitions output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
-# Definitions (see http://autogen.sf.net) file that captures the structure of
-# the code including all documentation. Note that this feature is still
-# experimental and incomplete at the moment.
-# The default value is: NO.
-
-GENERATE_AUTOGEN_DEF = NO
-
-#---------------------------------------------------------------------------
-# Configuration options related to the Perl module output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
-# file that captures the structure of the code including all documentation.
-#
-# Note that this feature is still experimental and incomplete at the moment.
-# The default value is: NO.
-
-GENERATE_PERLMOD = NO
-
-# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
-# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
-# output from the Perl module output.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_PERLMOD is set to YES.
-
-PERLMOD_LATEX = NO
-
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
-# formatted so it can be parsed by a human reader. This is useful if you want to
-# understand what is going on. On the other hand, if this tag is set to NO the
-# size of the Perl module output will be much smaller and Perl will parse it
-# just the same.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_PERLMOD is set to YES.
-
-PERLMOD_PRETTY = YES
-
-# The names of the make variables in the generated doxyrules.make file are
-# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
-# so different doxyrules.make files included by the same Makefile don't
-# overwrite each other's variables.
-# This tag requires that the tag GENERATE_PERLMOD is set to YES.
-
-PERLMOD_MAKEVAR_PREFIX =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor
-#---------------------------------------------------------------------------
-
-# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
-# C-preprocessor directives found in the sources and include files.
-# The default value is: YES.
-
-ENABLE_PREPROCESSING = YES
-
-# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
-# in the source code. If set to NO only conditional compilation will be
-# performed. Macro expansion can be done in a controlled way by setting
-# EXPAND_ONLY_PREDEF to YES.
-# The default value is: NO.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-MACRO_EXPANSION = NO
-
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
-# the macro expansion is limited to the macros specified with the PREDEFINED and
-# EXPAND_AS_DEFINED tags.
-# The default value is: NO.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-EXPAND_ONLY_PREDEF = NO
-
-# If the SEARCH_INCLUDES tag is set to YES the includes files in the
-# INCLUDE_PATH will be searched if a #include is found.
-# The default value is: YES.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-SEARCH_INCLUDES = YES
-
-# The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by the
-# preprocessor.
-# This tag requires that the tag SEARCH_INCLUDES is set to YES.
-
-INCLUDE_PATH =
-
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
-# patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will be
-# used.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-INCLUDE_FILE_PATTERNS =
-
-# The PREDEFINED tag can be used to specify one or more macro names that are
-# defined before the preprocessor is started (similar to the -D option of e.g.
-# gcc). The argument of the tag is a list of macros of the form: name or
-# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
-# is assumed. To prevent a macro definition from being undefined via #undef or
-# recursively expanded use the := operator instead of the = operator.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-PREDEFINED =
-
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
-# tag can be used to specify a list of macro names that should be expanded. The
-# macro definition that is found in the sources will be used. Use the PREDEFINED
-# tag if you want to use a different macro definition that overrules the
-# definition found in the source code.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-EXPAND_AS_DEFINED =
-
-# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
-# remove all refrences to function-like macros that are alone on a line, have an
-# all uppercase name, and do not end with a semicolon. Such function macros are
-# typically used for boiler-plate code, and will confuse the parser if not
-# removed.
-# The default value is: YES.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-SKIP_FUNCTION_MACROS = YES
-
-#---------------------------------------------------------------------------
-# Configuration options related to external references
-#---------------------------------------------------------------------------
-
-# The TAGFILES tag can be used to specify one or more tag files. For each tag
-# file the location of the external documentation should be added. The format of
-# a tag file without this location is as follows:
-# TAGFILES = file1 file2 ...
-# Adding location for the tag files is done as follows:
-# TAGFILES = file1=loc1 "file2 = loc2" ...
-# where loc1 and loc2 can be relative or absolute paths or URLs. See the
-# section "Linking to external documentation" for more information about the use
-# of tag files.
-# Note: Each tag file must have an unique name (where the name does NOT include
-# the path). If a tag file is not located in the directory in which doxygen is
-# run, you must also specify the path to the tagfile here.
-
-TAGFILES =
-
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
-# tag file that is based on the input files it reads. See section "Linking to
-# external documentation" for more information about the usage of tag files.
-
-GENERATE_TAGFILE =
-
-# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
-# class index. If set to NO only the inherited external classes will be listed.
-# The default value is: NO.
-
-ALLEXTERNALS = NO
-
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
-# the modules index. If set to NO, only the current project's groups will be
-# listed.
-# The default value is: YES.
-
-EXTERNAL_GROUPS = YES
-
-# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
-# the related pages index. If set to NO, only the current project's pages will
-# be listed.
-# The default value is: YES.
-
-EXTERNAL_PAGES = YES
-
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of 'which perl').
-# The default file (with absolute path) is: /usr/bin/perl.
-
-PERL_PATH = /usr/bin/perl
-
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool
-#---------------------------------------------------------------------------
-
-# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
-# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
-# NO turns the diagrams off. Note that this option also works with HAVE_DOT
-# disabled, but it is recommended to install and use dot, since it yields more
-# powerful graphs.
-# The default value is: YES.
-
-CLASS_DIAGRAMS = YES
-
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see:
-# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
-# default search path.
-
-MSCGEN_PATH =
-
-# You can include diagrams made with dia in doxygen documentation. Doxygen will
-# then run dia to produce the diagram and insert it in the documentation. The
-# DIA_PATH tag allows you to specify the directory where the dia binary resides.
-# If left empty dia is assumed to be found in the default search path.
-
-DIA_PATH =
-
-# If set to YES, the inheritance and collaboration graphs will hide inheritance
-# and usage relations if the target is undocumented or is not a class.
-# The default value is: YES.
-
-HIDE_UNDOC_RELATIONS = YES
-
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz (see:
-# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
-# Bell Labs. The other options in this section have no effect if this option is
-# set to NO
-# The default value is: NO.
-
-HAVE_DOT = NO
-
-# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
-# to run in parallel. When set to 0 doxygen will base this on the number of
-# processors available in the system. You can set it explicitly to a value
-# larger than 0 to get control over the balance between CPU load and processing
-# speed.
-# Minimum value: 0, maximum value: 32, default value: 0.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_NUM_THREADS = 0
-
-# When you want a differently looking font n the dot files that doxygen
-# generates you can specify the font name using DOT_FONTNAME. You need to make
-# sure dot is able to find the font, which can be done by putting it in a
-# standard location or by setting the DOTFONTPATH environment variable or by
-# setting DOT_FONTPATH to the directory containing the font.
-# The default value is: Helvetica.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_FONTNAME = Helvetica
-
-# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
-# dot graphs.
-# Minimum value: 4, maximum value: 24, default value: 10.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_FONTSIZE = 10
-
-# By default doxygen will tell dot to use the default font as specified with
-# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
-# the path where dot can find it using this tag.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_FONTPATH =
-
-# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
-# each documented class showing the direct and indirect inheritance relations.
-# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-CLASS_GRAPH = YES
-
-# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
-# graph for each documented class showing the direct and indirect implementation
-# dependencies (inheritance, containment, and class references variables) of the
-# class with other documented classes.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-COLLABORATION_GRAPH = YES
-
-# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
-# groups, showing the direct groups dependencies.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-GROUP_GRAPHS = YES
-
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
-# collaboration diagrams in a style similar to the OMG's Unified Modeling
-# Language.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-UML_LOOK = NO
-
-# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
-# class node. If there are many fields or methods and many nodes the graph may
-# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
-# number of items for each type to make the size more manageable. Set this to 0
-# for no limit. Note that the threshold may be exceeded by 50% before the limit
-# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
-# but if the number exceeds 15, the total amount of fields shown is limited to
-# 10.
-# Minimum value: 0, maximum value: 100, default value: 10.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-UML_LIMIT_NUM_FIELDS = 10
-
-# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
-# collaboration graphs will show the relations between templates and their
-# instances.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-TEMPLATE_RELATIONS = NO
-
-# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
-# YES then doxygen will generate a graph for each documented file showing the
-# direct and indirect include dependencies of the file with other documented
-# files.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-INCLUDE_GRAPH = YES
-
-# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
-# set to YES then doxygen will generate a graph for each documented file showing
-# the direct and indirect include dependencies of the file with other documented
-# files.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-INCLUDED_BY_GRAPH = YES
-
-# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
-# dependency graph for every global function or class method.
-#
-# Note that enabling this option will significantly increase the time of a run.
-# So in most cases it will be better to enable call graphs for selected
-# functions only using the \callgraph command.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-CALL_GRAPH = NO
-
-# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
-# dependency graph for every global function or class method.
-#
-# Note that enabling this option will significantly increase the time of a run.
-# So in most cases it will be better to enable caller graphs for selected
-# functions only using the \callergraph command.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-CALLER_GRAPH = NO
-
-# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
-# hierarchy of all classes instead of a textual one.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-GRAPHICAL_HIERARCHY = YES
-
-# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
-# dependencies a directory has on other directories in a graphical way. The
-# dependency relations are determined by the #include relations between the
-# files in the directories.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DIRECTORY_GRAPH = YES
-
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot.
-# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
-# to make the SVG files visible in IE 9+ (other browsers do not have this
-# requirement).
-# Possible values are: png, jpg, gif and svg.
-# The default value is: png.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_IMAGE_FORMAT = png
-
-# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
-# enable generation of interactive SVG images that allow zooming and panning.
-#
-# Note that this requires a modern browser other than Internet Explorer. Tested
-# and working are Firefox, Chrome, Safari, and Opera.
-# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
-# the SVG files visible. Older versions of IE do not have SVG support.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-INTERACTIVE_SVG = NO
-
-# The DOT_PATH tag can be used to specify the path where the dot tool can be
-# found. If left blank, it is assumed the dot tool can be found in the path.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_PATH =
-
-# The DOTFILE_DIRS tag can be used to specify one or more directories that
-# contain dot files that are included in the documentation (see the \dotfile
-# command).
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOTFILE_DIRS =
-
-# The MSCFILE_DIRS tag can be used to specify one or more directories that
-# contain msc files that are included in the documentation (see the \mscfile
-# command).
-
-MSCFILE_DIRS =
-
-# The DIAFILE_DIRS tag can be used to specify one or more directories that
-# contain dia files that are included in the documentation (see the \diafile
-# command).
-
-DIAFILE_DIRS =
-
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
-# that will be shown in the graph. If the number of nodes in a graph becomes
-# larger than this value, doxygen will truncate the graph, which is visualized
-# by representing a node as a red box. Note that doxygen if the number of direct
-# children of the root node in a graph is already larger than
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
-# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
-# Minimum value: 0, maximum value: 10000, default value: 50.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_GRAPH_MAX_NODES = 50
-
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
-# generated by dot. A depth value of 3 means that only nodes reachable from the
-# root by following a path via at most 3 edges will be shown. Nodes that lay
-# further from the root node will be omitted. Note that setting this option to 1
-# or 2 may greatly reduce the computation time needed for large code bases. Also
-# note that the size of a graph can be further restricted by
-# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
-# Minimum value: 0, maximum value: 1000, default value: 0.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-MAX_DOT_GRAPH_DEPTH = 0
-
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is disabled by default, because dot on Windows does not seem
-# to support this out of the box.
-#
-# Warning: Depending on the platform used, enabling this option may lead to
-# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
-# read).
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_TRANSPARENT = NO
-
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
-# files in one run (i.e. multiple -o and -T options on the command line). This
-# makes dot run faster, but since only newer versions of dot (>1.8.10) support
-# this, this feature is disabled by default.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_MULTI_TARGETS = YES
-
-# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
-# explaining the meaning of the various boxes and arrows in the dot generated
-# graphs.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-GENERATE_LEGEND = YES
-
-# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
-# files that are used to generate the various graphs.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_CLEANUP = YES
diff --git a/media/libaaudio/examples/loopback/Android.bp b/media/libaaudio/examples/loopback/Android.bp
index 8d86e95..b18aeec 100644
--- a/media/libaaudio/examples/loopback/Android.bp
+++ b/media/libaaudio/examples/loopback/Android.bp
@@ -13,9 +13,11 @@
srcs: ["src/loopback.cpp"],
cflags: ["-Wall", "-Werror"],
static_libs: ["libsndfile"],
+ include_dirs: ["external/oboe/apps/OboeTester/app/src/main/cpp"],
shared_libs: [
"libaaudio",
"libaudioutils",
+ "liblog"
],
header_libs: ["libaaudio_example_utils"],
}
diff --git a/media/libaaudio/examples/loopback/src/analyzer/GlitchAnalyzer.h b/media/libaaudio/examples/loopback/src/analyzer/GlitchAnalyzer.h
deleted file mode 100644
index 04435d1..0000000
--- a/media/libaaudio/examples/loopback/src/analyzer/GlitchAnalyzer.h
+++ /dev/null
@@ -1,445 +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 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
deleted file mode 100644
index e506791..0000000
--- a/media/libaaudio/examples/loopback/src/analyzer/LatencyAnalyzer.h
+++ /dev/null
@@ -1,606 +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 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
deleted file mode 100644
index 0a4bd5b..0000000
--- a/media/libaaudio/examples/loopback/src/analyzer/ManchesterEncoder.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 4b3b4e7..0000000
--- a/media/libaaudio/examples/loopback/src/analyzer/PeakDetector.h
+++ /dev/null
@@ -1,68 +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 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
deleted file mode 100644
index 1c4938c..0000000
--- a/media/libaaudio/examples/loopback/src/analyzer/PseudoRandom.h
+++ /dev/null
@@ -1,57 +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 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
deleted file mode 100644
index 030050b..0000000
--- a/media/libaaudio/examples/loopback/src/analyzer/RandomPulseGenerator.h
+++ /dev/null
@@ -1,43 +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 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
deleted file mode 100644
index f2eba84..0000000
--- a/media/libaaudio/examples/loopback/src/analyzer/RoundedManchesterEncoder.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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 0d2ec70..6fff568 100644
--- a/media/libaaudio/examples/loopback/src/loopback.cpp
+++ b/media/libaaudio/examples/loopback/src/loopback.cpp
@@ -36,8 +36,12 @@
#include "AAudioSimpleRecorder.h"
#include "AAudioExampleUtils.h"
+// Get logging macros from OboeTester
+#include "android_debug.h"
+// Get signal analyzers from OboeTester
#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
@@ -45,8 +49,9 @@
// fix -n option to set output buffer for -tm
// plot first glitch
// V0.4.02 = allow -n0 for minimal buffer size
-// V0.5.00 = use latency analyzer from OboeTester, uses random noise for latency
-#define APP_VERSION "0.5.00"
+// V0.5.00 = use latency analyzer copied from OboeTester, uses random noise for latency
+// V0.5.01 = use latency analyzer directly from OboeTester in external/oboe
+#define APP_VERSION "0.5.01"
// Tag for machine readable results as property = value pairs
#define RESULT_TAG "RESULT: "
diff --git a/media/libaaudio/examples/utils/AAudioArgsParser.h b/media/libaaudio/examples/utils/AAudioArgsParser.h
index 4bba436..e670642 100644
--- a/media/libaaudio/examples/utils/AAudioArgsParser.h
+++ b/media/libaaudio/examples/utils/AAudioArgsParser.h
@@ -421,7 +421,9 @@
printf(" -f{0|1|2} set format\n");
printf(" 0 = UNSPECIFIED\n");
printf(" 1 = PCM_I16\n");
- printf(" 2 = FLOAT\n");
+ printf(" 2 = PCM_FLOAT\n");
+ printf(" 3 = PCM_I24_PACKED\n");
+ printf(" 4 = PCM_I32\n");
printf(" -i{inputPreset} eg. 5 for AAUDIO_INPUT_PRESET_CAMCORDER\n");
printf(" -m{0|1|2|3} set MMAP policy\n");
printf(" 0 = _UNSPECIFIED, use aaudio.mmap_policy system property, default\n");
diff --git a/media/libaaudio/examples/utils/AAudioExampleUtils.h b/media/libaaudio/examples/utils/AAudioExampleUtils.h
index 46b8895..5819dfd 100644
--- a/media/libaaudio/examples/utils/AAudioExampleUtils.h
+++ b/media/libaaudio/examples/utils/AAudioExampleUtils.h
@@ -32,6 +32,7 @@
#define NANOS_PER_MILLISECOND (NANOS_PER_MICROSECOND * 1000)
#define NANOS_PER_SECOND (NANOS_PER_MILLISECOND * 1000)
+// Use template functions to avoid warning of unused static functions.
template <class T = aaudio_sharing_mode_t>
const char *getSharingModeText(aaudio_sharing_mode_t mode) {
const char *text = "unknown";
@@ -48,6 +49,7 @@
return text;
}
+template <class T = aaudio_performance_mode_t>
const char *getPerformanceModeText(aaudio_performance_mode_t mode) {
const char *text = "unknown";
switch (mode) {
@@ -66,6 +68,7 @@
return text;
}
+template <class T = aaudio_direction_t>
const char *getDirectionText(aaudio_direction_t direction) {
const char *text = "unknown";
switch (direction) {
@@ -81,6 +84,29 @@
return text;
}
+template <class T = aaudio_direction_t>
+constexpr int32_t getBytesPerSample(aaudio_format_t format) {
+ switch (format) {
+ case AAUDIO_FORMAT_PCM_I16:
+ return 2;
+ case AAUDIO_FORMAT_PCM_FLOAT:
+ return 4;
+ case AAUDIO_FORMAT_PCM_I24_PACKED:
+ return 3;
+ case AAUDIO_FORMAT_PCM_I32:
+ return 4;
+ default:
+ return -1;
+ }
+}
+
+// Return true if CPU is native Little Endian
+inline bool isNativeLittleEndian() {
+ // If the first byte of the data word in memory is 1 then Little Endian.
+ constexpr union { unsigned u; unsigned char c[sizeof(unsigned)]; } one = {1};
+ return one.c[0] != 0;
+}
+
template <class T = int64_t>
void convertNanosecondsToTimespec(int64_t nanoseconds, struct timespec *time) {
time->tv_sec = nanoseconds / NANOS_PER_SECOND;
diff --git a/media/libaaudio/examples/utils/AAudioSimplePlayer.h b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
index fd1fc45..7daac20 100644
--- a/media/libaaudio/examples/utils/AAudioSimplePlayer.h
+++ b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
@@ -359,22 +359,38 @@
int32_t samplesPerFrame = AAudioStream_getChannelCount(stream);
-
- int numActiveOscilators = (samplesPerFrame > MAX_CHANNELS) ? MAX_CHANNELS : samplesPerFrame;
+ int numActiveOscillators = std::min(samplesPerFrame, MAX_CHANNELS);
switch (AAudioStream_getFormat(stream)) {
case AAUDIO_FORMAT_PCM_I16: {
int16_t *audioBuffer = (int16_t *) audioData;
- for (int i = 0; i < numActiveOscilators; ++i) {
- sineData->sineOscillators[i].render(&audioBuffer[i], samplesPerFrame,
- numFrames);
+ for (int i = 0; i < numActiveOscillators; ++i) {
+ sineData->sineOscillators[i].render(&audioBuffer[i],
+ samplesPerFrame, numFrames);
}
}
break;
case AAUDIO_FORMAT_PCM_FLOAT: {
float *audioBuffer = (float *) audioData;
- for (int i = 0; i < numActiveOscilators; ++i) {
- sineData->sineOscillators[i].render(&audioBuffer[i], samplesPerFrame,
- numFrames);
+ for (int i = 0; i < numActiveOscillators; ++i) {
+ sineData->sineOscillators[i].render(&audioBuffer[i],
+ samplesPerFrame, numFrames);
+ }
+ }
+ break;
+ case AAUDIO_FORMAT_PCM_I24_PACKED: {
+ uint8_t *audioBuffer = (uint8_t *) audioData;
+ for (int i = 0; i < numActiveOscillators; ++i) {
+ static const int bytesPerSample = getBytesPerSample(AAUDIO_FORMAT_PCM_I24_PACKED);
+ sineData->sineOscillators[i].render24(&audioBuffer[i * bytesPerSample],
+ samplesPerFrame, numFrames);
+ }
+ }
+ break;
+ case AAUDIO_FORMAT_PCM_I32: {
+ int32_t *audioBuffer = (int32_t *) audioData;
+ for (int i = 0; i < numActiveOscillators; ++i) {
+ sineData->sineOscillators[i].render(&audioBuffer[i],
+ samplesPerFrame, numFrames);
}
}
break;
diff --git a/media/libaaudio/examples/utils/SineGenerator.h b/media/libaaudio/examples/utils/SineGenerator.h
index 9e6d46d..66a08fd 100644
--- a/media/libaaudio/examples/utils/SineGenerator.h
+++ b/media/libaaudio/examples/utils/SineGenerator.h
@@ -41,20 +41,54 @@
}
}
+ float next() {
+ float value = sinf(mPhase) * mAmplitude;
+ advancePhase();
+ return value;
+ }
+
void render(int16_t *buffer, int32_t channelStride, int32_t numFrames) {
int sampleIndex = 0;
for (int i = 0; i < numFrames; i++) {
- buffer[sampleIndex] = (int16_t) (INT16_MAX * sin(mPhase) * mAmplitude);
+ buffer[sampleIndex] = (int16_t) (INT16_MAX * next());
sampleIndex += channelStride;
- advancePhase();
}
}
+
void render(float *buffer, int32_t channelStride, int32_t numFrames) {
int sampleIndex = 0;
for (int i = 0; i < numFrames; i++) {
- buffer[sampleIndex] = sin(mPhase) * mAmplitude;
+ buffer[sampleIndex] = next();
sampleIndex += channelStride;
- advancePhase();
+ }
+ }
+
+ void render(int32_t *buffer, int32_t channelStride, int32_t numFrames) {
+ int sampleIndex = 0;
+ for (int i = 0; i < numFrames; i++) {
+ buffer[sampleIndex] = (int32_t) (INT32_MAX * next());
+ sampleIndex += channelStride;
+ }
+ }
+
+ void render24(uint8_t *buffer, int32_t channelStride, int32_t numFrames) {
+ int sampleIndex = 0;
+ constexpr int32_t INT24_MAX = (1 << 23) - 1;
+ constexpr int bytesPerSample = getBytesPerSample(AAUDIO_FORMAT_PCM_I24_PACKED);
+ const bool isLittleEndian = isNativeLittleEndian();
+ for (int i = 0; i < numFrames; i++) {
+ int32_t sample = (int32_t) (INT24_MAX * next());
+ uint32_t usample = (uint32_t) sample;
+ if (isLittleEndian) {
+ buffer[sampleIndex] = usample; // little end first
+ buffer[sampleIndex + 1] = usample >> 8;
+ buffer[sampleIndex + 2] = usample >> 16;
+ } else {
+ buffer[sampleIndex] = usample >> 16; // big end first
+ buffer[sampleIndex + 1] = usample >> 8;
+ buffer[sampleIndex + 2] = usample;
+ }
+ sampleIndex += channelStride * bytesPerSample;
}
}
@@ -100,4 +134,3 @@
};
#endif /* SINE_GENERATOR_H */
-
diff --git a/media/libaaudio/examples/write_sine/src/write_sine.cpp b/media/libaaudio/examples/write_sine/src/write_sine.cpp
index 8e33a31..33d07f0 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine.cpp
@@ -47,9 +47,11 @@
int32_t framesToPlay = 0;
int32_t framesLeft = 0;
int32_t xRunCount = 0;
- int numActiveOscilators = 0;
+ int numActiveOscillators = 0;
float *floatData = nullptr;
int16_t *shortData = nullptr;
+ int32_t *int32Data = nullptr;
+ uint8_t *byteData = nullptr;
int testFd = -1;
@@ -57,7 +59,7 @@
// in a buffer if we hang or crash.
setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
- printf("%s - Play a sine wave using AAudio V0.1.3\n", argv[0]);
+ printf("%s - Play a sine wave using AAudio V0.1.4\n", argv[0]);
if (argParser.parseArgs(argc, argv)) {
return EXIT_FAILURE;
@@ -91,13 +93,23 @@
printf("Buffer: framesPerWrite = %d\n",framesPerWrite);
// Allocate a buffer for the audio data.
- if (actualDataFormat == AAUDIO_FORMAT_PCM_FLOAT) {
- floatData = new float[framesPerWrite * actualChannelCount];
- } else if (actualDataFormat == AAUDIO_FORMAT_PCM_I16) {
- shortData = new int16_t[framesPerWrite * actualChannelCount];
- } else {
- printf("ERROR Unsupported data format!\n");
- goto finish;
+ switch (actualDataFormat) {
+ case AAUDIO_FORMAT_PCM_FLOAT:
+ floatData = new float[framesPerWrite * actualChannelCount];
+ break;
+ case AAUDIO_FORMAT_PCM_I16:
+ shortData = new int16_t[framesPerWrite * actualChannelCount];
+ break;
+ case AAUDIO_FORMAT_PCM_I24_PACKED:
+ byteData = new uint8_t[framesPerWrite * actualChannelCount
+ * getBytesPerSample(AAUDIO_FORMAT_PCM_I24_PACKED)];
+ break;
+ case AAUDIO_FORMAT_PCM_I32:
+ int32Data = new int32_t[framesPerWrite * actualChannelCount];
+ break;
+ default:
+ printf("ERROR Unsupported data format!\n");
+ goto finish;
}
testFd = open("/data/aaudio_temp.raw", O_CREAT | O_RDWR, S_IRWXU);
@@ -117,29 +129,56 @@
// Play for a while.
framesToPlay = actualSampleRate * argParser.getDurationSeconds();
framesLeft = framesToPlay;
- numActiveOscilators = (actualChannelCount > MAX_CHANNELS) ? MAX_CHANNELS : actualChannelCount;
+ numActiveOscillators = (actualChannelCount > MAX_CHANNELS) ? MAX_CHANNELS : actualChannelCount;
while (framesLeft > 0) {
// Render as FLOAT or PCM
- if (actualDataFormat == AAUDIO_FORMAT_PCM_FLOAT) {
- for (int i = 0; i < numActiveOscilators; ++i) {
- myData.sineOscillators[i].render(&floatData[i], actualChannelCount,
- framesPerWrite);
- }
- } else if (actualDataFormat == AAUDIO_FORMAT_PCM_I16) {
- for (int i = 0; i < numActiveOscilators; ++i) {
- myData.sineOscillators[i].render(&shortData[i], actualChannelCount,
- framesPerWrite);
- }
+ switch (actualDataFormat) {
+ case AAUDIO_FORMAT_PCM_FLOAT:
+ for (int i = 0; i < numActiveOscillators; ++i) {
+ myData.sineOscillators[i].render(&floatData[i], actualChannelCount,
+ framesPerWrite);
+ }
+ break;
+ case AAUDIO_FORMAT_PCM_I16:
+ for (int i = 0; i < numActiveOscillators; ++i) {
+ myData.sineOscillators[i].render(&shortData[i], actualChannelCount,
+ framesPerWrite);
+ }
+ break;
+ case AAUDIO_FORMAT_PCM_I32:
+ for (int i = 0; i < numActiveOscillators; ++i) {
+ myData.sineOscillators[i].render(&int32Data[i], actualChannelCount,
+ framesPerWrite);
+ }
+ break;
+ case AAUDIO_FORMAT_PCM_I24_PACKED:
+ for (int i = 0; i < numActiveOscillators; ++i) {
+ static const int
+ bytesPerSample = getBytesPerSample(AAUDIO_FORMAT_PCM_I24_PACKED);
+ myData.sineOscillators[i].render24(&byteData[i * bytesPerSample],
+ actualChannelCount,
+ framesPerWrite);
+ }
+ break;
}
// Write audio data to the stream.
int64_t timeoutNanos = 1000 * NANOS_PER_MILLISECOND;
int32_t minFrames = (framesToPlay < framesPerWrite) ? framesToPlay : framesPerWrite;
int32_t actual = 0;
- if (actualDataFormat == AAUDIO_FORMAT_PCM_FLOAT) {
- actual = AAudioStream_write(aaudioStream, floatData, minFrames, timeoutNanos);
- } else if (actualDataFormat == AAUDIO_FORMAT_PCM_I16) {
- actual = AAudioStream_write(aaudioStream, shortData, minFrames, timeoutNanos);
+ switch (actualDataFormat) {
+ case AAUDIO_FORMAT_PCM_FLOAT:
+ actual = AAudioStream_write(aaudioStream, floatData, minFrames, timeoutNanos);
+ break;
+ case AAUDIO_FORMAT_PCM_I16:
+ actual = AAudioStream_write(aaudioStream, shortData, minFrames, timeoutNanos);
+ break;
+ case AAUDIO_FORMAT_PCM_I32:
+ actual = AAudioStream_write(aaudioStream, int32Data, minFrames, timeoutNanos);
+ break;
+ case AAUDIO_FORMAT_PCM_I24_PACKED:
+ actual = AAudioStream_write(aaudioStream, byteData, minFrames, timeoutNanos);
+ break;
}
if (actual < 0) {
fprintf(stderr, "ERROR - AAudioStream_write() returned %d\n", actual);
@@ -196,6 +235,8 @@
delete[] floatData;
delete[] shortData;
+ delete[] int32Data;
+ delete[] byteData;
printf("exiting - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
return (result != AAUDIO_OK) ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
index ca60233..cdc987b 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
@@ -31,7 +31,7 @@
#include "AAudioSimplePlayer.h"
#include "AAudioArgsParser.h"
-#define APP_VERSION "0.1.7"
+#define APP_VERSION "0.1.8"
constexpr int32_t kDefaultHangTimeMSec = 10;
diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h
index 709c656..3333925 100644
--- a/media/libaaudio/include/aaudio/AAudio.h
+++ b/media/libaaudio/include/aaudio/AAudio.h
@@ -65,7 +65,7 @@
/**
* This format uses the int16_t data type.
- * The maximum range of the data is -32768 to 32767.
+ * The maximum range of the data is -32768 (0x8000) to 32767 (0x7FFF).
*/
AAUDIO_FORMAT_PCM_I16,
@@ -74,10 +74,35 @@
* The nominal range of the data is [-1.0f, 1.0f).
* Values outside that range may be clipped.
*
- * See also 'floatData' at
- * https://developer.android.com/reference/android/media/AudioTrack#write(float[],%20int,%20int,%20int)
+ * See also the float Data in
+ * <a href="/reference/android/media/AudioTrack#write(float[],%20int,%20int,%20int)">
+ * write(float[], int, int, int)</a>.
*/
- AAUDIO_FORMAT_PCM_FLOAT
+ AAUDIO_FORMAT_PCM_FLOAT,
+
+ /**
+ * This format uses 24-bit samples packed into 3 bytes.
+ * The bytes are in the native endian order.
+ * The maximum range of the data is -8388608 (0x800000)
+ * to 8388607 (0x7FFFFF).
+ *
+ * Note that the lower precision bits may be ignored by the device.
+ *
+ * Available since API level 31.
+ */
+ AAUDIO_FORMAT_PCM_I24_PACKED,
+
+ /**
+ * This format uses 32-bit samples stored in an int32_t data type.
+ * The maximum range of the data is -2147483648 (0x80000000)
+ * to 2147483647 (0x7FFFFFFF).
+ *
+ * Note that the lower precision bits may be ignored by the device.
+ *
+ * Available since API level 31.
+ */
+ AAUDIO_FORMAT_PCM_I32
+
};
typedef int32_t aaudio_format_t;
@@ -172,21 +197,69 @@
};
typedef int32_t aaudio_result_t;
+/**
+ * AAudio Stream states, for details, refer to
+ * <a href="/ndk/guides/audio/aaudio/aaudio#using-streams">Using an Audio Stream</a>
+ */
enum
{
+
+ /**
+ * The stream is created but not initialized yet.
+ */
AAUDIO_STREAM_STATE_UNINITIALIZED = 0,
+ /**
+ * The stream is in an unrecognized state.
+ */
AAUDIO_STREAM_STATE_UNKNOWN,
+
+ /**
+ * The stream is open and ready to use.
+ */
AAUDIO_STREAM_STATE_OPEN,
+ /**
+ * The stream is just starting up.
+ */
AAUDIO_STREAM_STATE_STARTING,
+ /**
+ * The stream has started.
+ */
AAUDIO_STREAM_STATE_STARTED,
+ /**
+ * The stream is pausing.
+ */
AAUDIO_STREAM_STATE_PAUSING,
+ /**
+ * The stream has paused, could be restarted or flushed.
+ */
AAUDIO_STREAM_STATE_PAUSED,
+ /**
+ * The stream is being flushed.
+ */
AAUDIO_STREAM_STATE_FLUSHING,
+ /**
+ * The stream is flushed, ready to be restarted.
+ */
AAUDIO_STREAM_STATE_FLUSHED,
+ /**
+ * The stream is stopping.
+ */
AAUDIO_STREAM_STATE_STOPPING,
+ /**
+ * The stream has been stopped.
+ */
AAUDIO_STREAM_STATE_STOPPED,
+ /**
+ * The stream is closing.
+ */
AAUDIO_STREAM_STATE_CLOSING,
+ /**
+ * The stream has been closed.
+ */
AAUDIO_STREAM_STATE_CLOSED,
+ /**
+ * The stream is disconnected from audio device.
+ */
AAUDIO_STREAM_STATE_DISCONNECTED
};
typedef int32_t aaudio_stream_state_t;
@@ -236,7 +309,8 @@
* This information is used by certain platforms or routing policies
* to make more refined volume or routing decisions.
*
- * Note that these match the equivalent values in {@link android.media.AudioAttributes}
+ * Note that these match the equivalent values in
+ * <a href="/reference/android/media/AudioAttributes">AudioAttributes</a>
* in the Android Java API.
*
* Added in API level 28.
@@ -337,7 +411,8 @@
* an audio book application) this information might be used by the audio framework to
* enforce audio focus.
*
- * Note that these match the equivalent values in {@link android.media.AudioAttributes}
+ * Note that these match the equivalent values in
+ * <a href="/reference/android/media/AudioAttributes">AudioAttributes</a>
* in the Android Java API.
*
* Added in API level 28.
@@ -417,7 +492,8 @@
/**
* Specifying if audio may or may not be captured by other apps or the system.
*
- * Note that these match the equivalent values in {@link android.media.AudioAttributes}
+ * Note that these match the equivalent values in
+ * <a href="/reference/android/media/AudioAttributes">AudioAttributes</a>
* in the Android Java API.
*
* Added in API level 29.
@@ -429,10 +505,11 @@
* For privacy, the following usages can not be recorded: AAUDIO_VOICE_COMMUNICATION*,
* AAUDIO_USAGE_NOTIFICATION*, AAUDIO_USAGE_ASSISTANCE* and {@link #AAUDIO_USAGE_ASSISTANT}.
*
- * On {@link android.os.Build.VERSION_CODES#Q}, this means only {@link #AAUDIO_USAGE_MEDIA}
- * and {@link #AAUDIO_USAGE_GAME} may be captured.
+ * On <a href="/reference/android/os/Build.VERSION_CODES#Q">Build.VERSION_CODES</a>,
+ * this means only {@link #AAUDIO_USAGE_MEDIA} and {@link #AAUDIO_USAGE_GAME} may be captured.
*
- * See {@link android.media.AudioAttributes#ALLOW_CAPTURE_BY_ALL}.
+ * See <a href="/reference/android/media/AudioAttributes.html#ALLOW_CAPTURE_BY_ALL">
+ * ALLOW_CAPTURE_BY_ALL</a>.
*/
AAUDIO_ALLOW_CAPTURE_BY_ALL = 1,
/**
@@ -440,8 +517,9 @@
*
* System apps can capture for many purposes like accessibility, user guidance...
* but have strong restriction. See
- * {@link android.media.AudioAttributes#ALLOW_CAPTURE_BY_SYSTEM} for what the system apps
- * can do with the capture audio.
+ * <a href="/reference/android/media/AudioAttributes.html#ALLOW_CAPTURE_BY_SYSTEM">
+ * ALLOW_CAPTURE_BY_SYSTEM</a>
+ * for what the system apps can do with the capture audio.
*/
AAUDIO_ALLOW_CAPTURE_BY_SYSTEM = 2,
/**
@@ -449,7 +527,8 @@
*
* It is encouraged to use {@link #AAUDIO_ALLOW_CAPTURE_BY_SYSTEM} instead of this value as system apps
* provide significant and useful features for the user (eg. accessibility).
- * See {@link android.media.AudioAttributes#ALLOW_CAPTURE_BY_NONE}.
+ * See <a href="/reference/android/media/AudioAttributes.html#ALLOW_CAPTURE_BY_NONE">
+ * ALLOW_CAPTURE_BY_NONE</a>.
*/
AAUDIO_ALLOW_CAPTURE_BY_NONE = 3,
};
@@ -556,6 +635,37 @@
AAUDIO_API void AAudioStreamBuilder_setDeviceId(AAudioStreamBuilder* builder,
int32_t deviceId) __INTRODUCED_IN(26);
+// TODO b/182392769: reexamine if Identity can be used
+/**
+ * Declare the name of the package creating the stream.
+ *
+ * This is usually {@code Context#getPackageName()}.
+ *
+ * The default, if you do not call this function, is a random package in the calling uid.
+ *
+ * Available since API level 31.
+ *
+ * @param builder reference provided by AAudio_createStreamBuilder()
+ * @param packageName packageName of the calling app.
+ */
+AAUDIO_API void AAudioStreamBuilder_setPackageName(AAudioStreamBuilder* builder,
+ const char * packageName) __INTRODUCED_IN(31);
+
+/**
+ * Declare the attribution tag of the context creating the stream.
+ *
+ * This is usually {@code Context#getAttributionTag()}.
+ *
+ * The default, if you do not call this function, is the default attribution tag.
+ *
+ * Available since API level 31.
+ *
+ * @param builder reference provided by AAudio_createStreamBuilder()
+ * @param attributionTag attributionTag of the calling context.
+ */
+AAUDIO_API void AAudioStreamBuilder_setAttributionTag(AAudioStreamBuilder* builder,
+ const char * attributionTag) __INTRODUCED_IN(31);
+
/**
* Request a sample rate in Hertz.
*
@@ -689,7 +799,7 @@
aaudio_performance_mode_t mode) __INTRODUCED_IN(26);
/**
- * Set the intended use case for the stream.
+ * Set the intended use case for the output stream.
*
* The AAudio system will use this information to optimize the
* behavior of the stream.
@@ -706,7 +816,7 @@
aaudio_usage_t usage) __INTRODUCED_IN(28);
/**
- * Set the type of audio data that the stream will carry.
+ * Set the type of audio data that the output stream will carry.
*
* The AAudio system will use this information to optimize the
* behavior of the stream.
@@ -748,7 +858,9 @@
* The default is {@link #AAUDIO_ALLOW_CAPTURE_BY_ALL}.
*
* Note that an application can also set its global policy, in which case the most restrictive
- * policy is always applied. See {@link android.media.AudioAttributes#setAllowedCapturePolicy(int)}
+ * policy is always applied. See
+ * <a href="/reference/android/media/AudioManager#setAllowedCapturePolicy(int)">
+ * setAllowedCapturePolicy(int)</a>
*
* Available since API level 29.
*
@@ -896,8 +1008,9 @@
* It will stop being called after AAudioStream_requestPause() or
* AAudioStream_requestStop() is called.
*
- * This callback function will be called on a real-time thread owned by AAudio. See
- * {@link #AAudioStream_dataCallback} for more information.
+ * This callback function will be called on a real-time thread owned by AAudio.
+ * The low latency streams may have callback threads with higher priority than normal streams.
+ * See {@link #AAudioStream_dataCallback} for more information.
*
* Note that the AAudio callbacks will never be called simultaneously from multiple threads.
*
diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp
index fdbc9c5..fe2d98e 100644
--- a/media/libaaudio/src/Android.bp
+++ b/media/libaaudio/src/Android.bp
@@ -30,6 +30,7 @@
],
cflags: [
+ "-Wthread-safety",
"-Wno-unused-parameter",
"-Wall",
"-Werror",
@@ -84,6 +85,10 @@
],
export_header_lib_headers: ["libaaudio_headers"],
+ export_shared_lib_headers: [
+ "media_permission-aidl-cpp",
+ ],
+
shared_libs: [
"libaudioclient",
"libaudioutils",
@@ -94,6 +99,13 @@
"libcutils",
"libutils",
"libbinder",
+ "aaudio-aidl-cpp",
+ "media_permission-aidl-cpp",
+ "libaudioclient_aidl_conversion",
+ ],
+
+ static_libs: [
+ "media_permission-aidl-cpp",
],
cflags: [
@@ -123,11 +135,10 @@
"client/AudioStreamInternalPlay.cpp",
"client/IsochronousClockModel.cpp",
"binding/AudioEndpointParcelable.cpp",
+ "binding/AAudioBinderAdapter.cpp",
"binding/AAudioBinderClient.cpp",
"binding/AAudioStreamRequest.cpp",
"binding/AAudioStreamConfiguration.cpp",
- "binding/IAAudioClient.cpp",
- "binding/IAAudioService.cpp",
"binding/RingBufferParcelable.cpp",
"binding/SharedMemoryParcelable.cpp",
"binding/SharedRegionParcelable.cpp",
@@ -138,12 +149,45 @@
"flowgraph/SinkFloat.cpp",
"flowgraph/SinkI16.cpp",
"flowgraph/SinkI24.cpp",
+ "flowgraph/SinkI32.cpp",
"flowgraph/SourceFloat.cpp",
"flowgraph/SourceI16.cpp",
"flowgraph/SourceI24.cpp",
+ "flowgraph/SourceI32.cpp",
],
sanitize: {
integer_overflow: true,
misc_undefined: ["bounds"],
},
}
+
+aidl_interface {
+ name: "aaudio-aidl",
+ unstable: true,
+ local_include_dir: "binding/aidl",
+ srcs: [
+ "binding/aidl/aaudio/Endpoint.aidl",
+ "binding/aidl/aaudio/RingBuffer.aidl",
+ "binding/aidl/aaudio/SharedRegion.aidl",
+ "binding/aidl/aaudio/StreamParameters.aidl",
+ "binding/aidl/aaudio/StreamRequest.aidl",
+ "binding/aidl/aaudio/IAAudioClient.aidl",
+ "binding/aidl/aaudio/IAAudioService.aidl",
+ ],
+ imports: [
+ "audio_common-aidl",
+ "shared-file-region-aidl",
+ "media_permission-aidl",
+ ],
+ backend:
+ {
+ cpp: {
+ enabled: true,
+ },
+ java: {
+ // TODO: need to have audio_common-aidl available in Java to enable
+ // this.
+ enabled: false,
+ },
+ },
+}
diff --git a/media/libaaudio/src/binding/AAudioBinderAdapter.cpp b/media/libaaudio/src/binding/AAudioBinderAdapter.cpp
new file mode 100644
index 0000000..6e3a1c8
--- /dev/null
+++ b/media/libaaudio/src/binding/AAudioBinderAdapter.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binding/AAudioBinderAdapter.h>
+#include <media/AidlConversionUtil.h>
+#include <utility/AAudioUtilities.h>
+
+namespace aaudio {
+
+using android::aidl_utils::statusTFromBinderStatus;
+using android::binder::Status;
+
+AAudioBinderAdapter::AAudioBinderAdapter(IAAudioService* delegate)
+ : mDelegate(delegate) {}
+
+void AAudioBinderAdapter::registerClient(const android::sp<IAAudioClient>& client) {
+ mDelegate->registerClient(client);
+}
+
+aaudio_handle_t AAudioBinderAdapter::openStream(const AAudioStreamRequest& request,
+ AAudioStreamConfiguration& config) {
+ aaudio_handle_t result;
+ StreamParameters params;
+ Status status = mDelegate->openStream(request.parcelable(),
+ ¶ms,
+ &result);
+ if (!status.isOk()) {
+ result = AAudioConvert_androidToAAudioResult(statusTFromBinderStatus(status));
+ }
+ config = params;
+ return result;
+}
+
+aaudio_result_t AAudioBinderAdapter::closeStream(aaudio_handle_t streamHandle) {
+ aaudio_result_t result;
+ Status status = mDelegate->closeStream(streamHandle, &result);
+ if (!status.isOk()) {
+ result = AAudioConvert_androidToAAudioResult(statusTFromBinderStatus(status));
+ }
+ return result;
+}
+
+aaudio_result_t AAudioBinderAdapter::getStreamDescription(aaudio_handle_t streamHandle,
+ AudioEndpointParcelable& endpointOut) {
+ aaudio_result_t result;
+ Endpoint endpoint;
+ Status status = mDelegate->getStreamDescription(streamHandle,
+ &endpoint,
+ &result);
+ if (!status.isOk()) {
+ result = AAudioConvert_androidToAAudioResult(statusTFromBinderStatus(status));
+ }
+ endpointOut = std::move(endpoint);
+ return result;
+}
+
+aaudio_result_t AAudioBinderAdapter::startStream(aaudio_handle_t streamHandle) {
+ aaudio_result_t result;
+ Status status = mDelegate->startStream(streamHandle, &result);
+ if (!status.isOk()) {
+ result = AAudioConvert_androidToAAudioResult(statusTFromBinderStatus(status));
+ }
+ return result;
+}
+
+aaudio_result_t AAudioBinderAdapter::pauseStream(aaudio_handle_t streamHandle) {
+ aaudio_result_t result;
+ Status status = mDelegate->pauseStream(streamHandle, &result);
+ if (!status.isOk()) {
+ result = AAudioConvert_androidToAAudioResult(statusTFromBinderStatus(status));
+ }
+ return result;
+}
+
+aaudio_result_t AAudioBinderAdapter::stopStream(aaudio_handle_t streamHandle) {
+ aaudio_result_t result;
+ Status status = mDelegate->stopStream(streamHandle, &result);
+ if (!status.isOk()) {
+ result = AAudioConvert_androidToAAudioResult(statusTFromBinderStatus(status));
+ }
+ return result;
+}
+
+aaudio_result_t AAudioBinderAdapter::flushStream(aaudio_handle_t streamHandle) {
+ aaudio_result_t result;
+ Status status = mDelegate->flushStream(streamHandle, &result);
+ if (!status.isOk()) {
+ result = AAudioConvert_androidToAAudioResult(statusTFromBinderStatus(status));
+ }
+ return result;
+}
+
+aaudio_result_t AAudioBinderAdapter::registerAudioThread(aaudio_handle_t streamHandle,
+ pid_t clientThreadId,
+ int64_t periodNanoseconds) {
+ aaudio_result_t result;
+ Status status = mDelegate->registerAudioThread(streamHandle, clientThreadId, periodNanoseconds, &result);
+ if (!status.isOk()) {
+ result = AAudioConvert_androidToAAudioResult(statusTFromBinderStatus(status));
+ }
+ return result;
+}
+
+aaudio_result_t AAudioBinderAdapter::unregisterAudioThread(aaudio_handle_t streamHandle,
+ pid_t clientThreadId) {
+ aaudio_result_t result;
+ Status status = mDelegate->unregisterAudioThread(streamHandle, clientThreadId, &result);
+ if (!status.isOk()) {
+ result = AAudioConvert_androidToAAudioResult(statusTFromBinderStatus(status));
+ }
+ return result;
+}
+
+} // namespace aaudio
diff --git a/media/libaaudio/src/binding/AAudioBinderAdapter.h b/media/libaaudio/src/binding/AAudioBinderAdapter.h
new file mode 100644
index 0000000..5e9ab57
--- /dev/null
+++ b/media/libaaudio/src/binding/AAudioBinderAdapter.h
@@ -0,0 +1,64 @@
+/*
+ * 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 <aaudio/IAAudioService.h>
+#include <binding/AAudioServiceInterface.h>
+
+namespace aaudio {
+
+/**
+ * An adapter that takes in an underlying IAAudioService and exposes an
+ * AAudioServiceInterface.
+ *
+ * This class is abstract: the client is expected to inherit from this class and implement those
+ * methods from AAudioServiceInterface that don't have counterparts in IAAudioService.
+ */
+class AAudioBinderAdapter : public AAudioServiceInterface {
+public:
+ explicit AAudioBinderAdapter(IAAudioService* delegate);
+
+ void registerClient(const android::sp<IAAudioClient>& client) override;
+
+ aaudio_handle_t openStream(const AAudioStreamRequest& request,
+ AAudioStreamConfiguration& configuration) override;
+
+ aaudio_result_t closeStream(aaudio_handle_t streamHandle) override;
+
+ aaudio_result_t getStreamDescription(aaudio_handle_t streamHandle,
+ AudioEndpointParcelable& endpoint) override;
+
+ aaudio_result_t startStream(aaudio_handle_t streamHandle) override;
+
+ aaudio_result_t pauseStream(aaudio_handle_t streamHandle) override;
+
+ aaudio_result_t stopStream(aaudio_handle_t streamHandle) override;
+
+ aaudio_result_t flushStream(aaudio_handle_t streamHandle) override;
+
+ aaudio_result_t registerAudioThread(aaudio_handle_t streamHandle,
+ pid_t clientThreadId,
+ int64_t periodNanoseconds) override;
+
+ aaudio_result_t unregisterAudioThread(aaudio_handle_t streamHandle,
+ pid_t clientThreadId) override;
+
+private:
+ IAAudioService* const mDelegate;
+};
+
+} // namespace aaudio
diff --git a/media/libaaudio/src/binding/AAudioBinderClient.cpp b/media/libaaudio/src/binding/AAudioBinderClient.cpp
index 7b0d31f..fa5a2da 100644
--- a/media/libaaudio/src/binding/AAudioBinderClient.cpp
+++ b/media/libaaudio/src/binding/AAudioBinderClient.cpp
@@ -19,35 +19,30 @@
//#define LOG_NDEBUG 0
#include <utils/Log.h>
-#include <binder/IInterface.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <utils/Mutex.h>
#include <utils/RefBase.h>
#include <utils/Singleton.h>
-#include <media/AudioSystem.h>
-
#include <aaudio/AAudio.h>
#include "AudioEndpointParcelable.h"
-#include "binding/AAudioBinderClient.h"
-//#include "binding/AAudioStreamRequest.h"
-//#include "binding/AAudioStreamConfiguration.h"
-//#include "binding/IAAudioService.h"
-//#include "binding/AAudioServiceMessage.h"
-//#include "AAudioServiceInterface.h"
+#include "binding/AAudioBinderClient.h"
+
+#define AAUDIO_SERVICE_NAME "media.aaudio"
using android::String16;
using android::IServiceManager;
using android::defaultServiceManager;
using android::interface_cast;
using android::IInterface;
-using android::IAAudioService;
using android::Mutex;
using android::ProcessState;
using android::sp;
+using android::status_t;
using android::wp;
+using android::binder::Status;
using namespace aaudio;
@@ -67,20 +62,18 @@
AAudioBinderClient::~AAudioBinderClient() {
ALOGV("%s - destroying %p", __func__, this);
Mutex::Autolock _l(mServiceLock);
- if (mAAudioService != 0) {
- IInterface::asBinder(mAAudioService)->unlinkToDeath(mAAudioClient);
- }
}
// TODO Share code with other service clients.
// Helper function to get access to the "AAudioService" service.
// This code was modeled after frameworks/av/media/libaudioclient/AudioSystem.cpp
-const sp<IAAudioService> AAudioBinderClient::getAAudioService() {
+std::shared_ptr<AAudioServiceInterface> AAudioBinderClient::getAAudioService() {
+ std::shared_ptr<AAudioServiceInterface> result;
sp<IAAudioService> aaudioService;
bool needToRegister = false;
{
Mutex::Autolock _l(mServiceLock);
- if (mAAudioService.get() == nullptr) {
+ if (mAdapter == nullptr) {
sp<IBinder> binder;
sp<IServiceManager> sm = defaultServiceManager();
// Try several times to get the service.
@@ -99,7 +92,8 @@
if (status != NO_ERROR) {
ALOGE("%s() - linkToDeath() returned %d", __func__, status);
}
- mAAudioService = interface_cast<IAAudioService>(binder);
+ aaudioService = interface_cast<IAAudioService>(binder);
+ mAdapter.reset(new Adapter(aaudioService, mAAudioClient));
needToRegister = true;
// Make sure callbacks can be received by mAAudioClient
ProcessState::self()->startThreadPool();
@@ -107,18 +101,18 @@
ALOGE("AAudioBinderClient could not connect to %s", AAUDIO_SERVICE_NAME);
}
}
- aaudioService = mAAudioService;
+ result = mAdapter;
}
// Do this outside the mutex lock.
if (needToRegister && aaudioService.get() != nullptr) { // new client?
aaudioService->registerClient(mAAudioClient);
}
- return aaudioService;
+ return result;
}
void AAudioBinderClient::dropAAudioService() {
Mutex::Autolock _l(mServiceLock);
- mAAudioService.clear(); // force a reconnect
+ mAdapter.reset();
}
/**
@@ -127,13 +121,13 @@
* @return handle to the stream or a negative error
*/
aaudio_handle_t AAudioBinderClient::openStream(const AAudioStreamRequest &request,
- AAudioStreamConfiguration &configurationOutput) {
+ AAudioStreamConfiguration &configuration) {
aaudio_handle_t stream;
for (int i = 0; i < 2; i++) {
- const sp<IAAudioService> &service = getAAudioService();
+ std::shared_ptr<AAudioServiceInterface> service = getAAudioService();
if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
- stream = service->openStream(request, configurationOutput);
+ stream = service->openStream(request, configuration);
if (stream == AAUDIO_ERROR_NO_SERVICE) {
ALOGE("openStream lost connection to AAudioService.");
@@ -146,8 +140,9 @@
}
aaudio_result_t AAudioBinderClient::closeStream(aaudio_handle_t streamHandle) {
- const sp<IAAudioService> service = getAAudioService();
+ std::shared_ptr<AAudioServiceInterface> service = getAAudioService();
if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
+
return service->closeStream(streamHandle);
}
@@ -155,33 +150,38 @@
* used to communicate with the underlying HAL or Service.
*/
aaudio_result_t AAudioBinderClient::getStreamDescription(aaudio_handle_t streamHandle,
- AudioEndpointParcelable &parcelable) {
- const sp<IAAudioService> service = getAAudioService();
+ AudioEndpointParcelable& endpointOut) {
+ std::shared_ptr<AAudioServiceInterface> service = getAAudioService();
if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
- return service->getStreamDescription(streamHandle, parcelable);
+
+ return service->getStreamDescription(streamHandle, endpointOut);
}
aaudio_result_t AAudioBinderClient::startStream(aaudio_handle_t streamHandle) {
- const sp<IAAudioService> service = getAAudioService();
+ std::shared_ptr<AAudioServiceInterface> service = getAAudioService();
if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
+
return service->startStream(streamHandle);
}
aaudio_result_t AAudioBinderClient::pauseStream(aaudio_handle_t streamHandle) {
- const sp<IAAudioService> service = getAAudioService();
+ std::shared_ptr<AAudioServiceInterface> service = getAAudioService();
if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
+
return service->pauseStream(streamHandle);
}
aaudio_result_t AAudioBinderClient::stopStream(aaudio_handle_t streamHandle) {
- const sp<IAAudioService> service = getAAudioService();
+ std::shared_ptr<AAudioServiceInterface> service = getAAudioService();
if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
+
return service->stopStream(streamHandle);
}
aaudio_result_t AAudioBinderClient::flushStream(aaudio_handle_t streamHandle) {
- const sp<IAAudioService> service = getAAudioService();
+ std::shared_ptr<AAudioServiceInterface> service = getAAudioService();
if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
+
return service->flushStream(streamHandle);
}
@@ -191,17 +191,16 @@
aaudio_result_t AAudioBinderClient::registerAudioThread(aaudio_handle_t streamHandle,
pid_t clientThreadId,
int64_t periodNanoseconds) {
- const sp<IAAudioService> service = getAAudioService();
+ std::shared_ptr<AAudioServiceInterface> service = getAAudioService();
if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
- return service->registerAudioThread(streamHandle,
- clientThreadId,
- periodNanoseconds);
+
+ return service->registerAudioThread(streamHandle, clientThreadId, periodNanoseconds);
}
aaudio_result_t AAudioBinderClient::unregisterAudioThread(aaudio_handle_t streamHandle,
pid_t clientThreadId) {
- const sp<IAAudioService> service = getAAudioService();
+ std::shared_ptr<AAudioServiceInterface> service = getAAudioService();
if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
- return service->unregisterAudioThread(streamHandle,
- clientThreadId);
+
+ return service->unregisterAudioThread(streamHandle, clientThreadId);
}
diff --git a/media/libaaudio/src/binding/AAudioBinderClient.h b/media/libaaudio/src/binding/AAudioBinderClient.h
index e8c91fc..6a7b639 100644
--- a/media/libaaudio/src/binding/AAudioBinderClient.h
+++ b/media/libaaudio/src/binding/AAudioBinderClient.h
@@ -21,12 +21,15 @@
#include <utils/Singleton.h>
#include <aaudio/AAudio.h>
-#include "AAudioServiceDefinitions.h"
+#include <binder/IInterface.h>
+
+#include "aaudio/BnAAudioClient.h"
+#include "aaudio/IAAudioService.h"
#include "AAudioServiceInterface.h"
+#include "binding/AAudioBinderAdapter.h"
#include "binding/AAudioStreamRequest.h"
-#include "binding/AAudioStreamConfiguration.h"
#include "binding/AudioEndpointParcelable.h"
-#include "binding/IAAudioService.h"
+#include "core/AAudioStreamParameters.h"
/**
* Implements the AAudioServiceInterface by talking to the service through Binder.
@@ -44,11 +47,7 @@
virtual ~AAudioBinderClient();
- const android::sp<android::IAAudioService> getAAudioService();
-
- void dropAAudioService();
-
- void registerClient(const android::sp<android::IAAudioClient>& client __unused) override {}
+ void registerClient(const android::sp<IAAudioClient>& client __unused) override {}
/**
* @param request info needed to create the stream
@@ -64,7 +63,7 @@
* used to communicate with the underlying HAL or Service.
*/
aaudio_result_t getStreamDescription(aaudio_handle_t streamHandle,
- AudioEndpointParcelable &parcelable) override;
+ AudioEndpointParcelable &endpointOut) override;
/**
* Start the flow of data.
@@ -115,8 +114,7 @@
ALOGW("onStreamChange called!");
}
- class AAudioClient : public android::IBinder::DeathRecipient , public android::BnAAudioClient
- {
+ class AAudioClient : public android::IBinder::DeathRecipient, public BnAAudioClient {
public:
AAudioClient(android::wp<AAudioBinderClient> aaudioBinderClient)
: mBinderClient(aaudioBinderClient) {
@@ -132,21 +130,66 @@
}
// implement BnAAudioClient
- void onStreamChange(aaudio_handle_t handle, int32_t opcode, int32_t value) {
+ android::binder::Status onStreamChange(int32_t handle, int32_t opcode, int32_t value) {
+ static_assert(std::is_same_v<aaudio_handle_t, int32_t>);
android::sp<AAudioBinderClient> client = mBinderClient.promote();
if (client.get() != nullptr) {
client->onStreamChange(handle, opcode, value);
}
+ return android::binder::Status::ok();
}
private:
android::wp<AAudioBinderClient> mBinderClient;
};
-private:
+ // This adapter is used to convert the binder interface (delegate) to the AudioServiceInterface
+ // conventions (translating between data types and respective parcelables, translating error
+ // codes and calling conventions).
+ // The adapter also owns the underlying service object and is responsible to unlink its death
+ // listener when destroyed.
+ class Adapter : public AAudioBinderAdapter {
+ public:
+ Adapter(const android::sp<IAAudioService>& delegate,
+ const android::sp<AAudioClient>& aaudioClient)
+ : AAudioBinderAdapter(delegate.get()),
+ mDelegate(delegate),
+ mAAudioClient(aaudioClient) {}
- android::Mutex mServiceLock;
- android::sp<android::IAAudioService> mAAudioService;
- android::sp<AAudioClient> mAAudioClient;
+ virtual ~Adapter() {
+ if (mDelegate != nullptr) {
+ android::IInterface::asBinder(mDelegate)->unlinkToDeath(mAAudioClient);
+ }
+ }
+
+ // This should never be called (call is rejected at the AudioBinderClient level).
+ aaudio_result_t startClient(aaudio_handle_t streamHandle __unused,
+ const android::AudioClient& client __unused,
+ const audio_attributes_t* attr __unused,
+ audio_port_handle_t* clientHandle __unused) override {
+ LOG_ALWAYS_FATAL("Shouldn't get here");
+ return AAUDIO_ERROR_UNAVAILABLE;
+ }
+
+ // This should never be called (call is rejected at the AudioBinderClient level).
+ aaudio_result_t stopClient(aaudio_handle_t streamHandle __unused,
+ audio_port_handle_t clientHandle __unused) override {
+ LOG_ALWAYS_FATAL("Shouldn't get here");
+ return AAUDIO_ERROR_UNAVAILABLE;
+ }
+
+ private:
+ android::sp<IAAudioService> mDelegate;
+ android::sp<AAudioClient> mAAudioClient;
+ };
+
+private:
+ android::Mutex mServiceLock;
+ std::shared_ptr<AAudioServiceInterface> mAdapter;
+ android::sp<AAudioClient> mAAudioClient;
+
+ std::shared_ptr<AAudioServiceInterface> getAAudioService();
+
+ void dropAAudioService();
};
diff --git a/media/libaaudio/src/binding/AAudioServiceInterface.h b/media/libaaudio/src/binding/AAudioServiceInterface.h
index 9c28cc7..5d11512 100644
--- a/media/libaaudio/src/binding/AAudioServiceInterface.h
+++ b/media/libaaudio/src/binding/AAudioServiceInterface.h
@@ -20,11 +20,11 @@
#include <utils/StrongPointer.h>
#include <media/AudioClient.h>
+#include "aaudio/IAAudioClient.h"
#include "binding/AAudioServiceDefinitions.h"
#include "binding/AAudioStreamRequest.h"
#include "binding/AAudioStreamConfiguration.h"
#include "binding/AudioEndpointParcelable.h"
-#include "binding/IAAudioClient.h"
/**
* This has the same methods as IAAudioService but without the Binder features.
@@ -40,7 +40,7 @@
AAudioServiceInterface() {};
virtual ~AAudioServiceInterface() = default;
- virtual void registerClient(const android::sp<android::IAAudioClient>& client) = 0;
+ virtual void registerClient(const android::sp<IAAudioClient>& client) = 0;
/**
* @param request info needed to create the stream
diff --git a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
index b785f88..2d501ef 100644
--- a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
@@ -23,101 +23,66 @@
#include <sys/mman.h>
#include <aaudio/AAudio.h>
-#include <binder/Parcel.h>
-#include <binder/Parcelable.h>
-
#include "binding/AAudioStreamConfiguration.h"
-using android::NO_ERROR;
-using android::status_t;
-using android::Parcel;
-using android::Parcelable;
-
using namespace aaudio;
-AAudioStreamConfiguration::AAudioStreamConfiguration() {}
-AAudioStreamConfiguration::~AAudioStreamConfiguration() {}
+using android::media::audio::common::AudioFormat;
-status_t AAudioStreamConfiguration::writeToParcel(Parcel* parcel) const {
- status_t status;
-
- status = parcel->writeInt32(getDeviceId());
- if (status != NO_ERROR) goto error;
- status = parcel->writeInt32(getSampleRate());
- if (status != NO_ERROR) goto error;
- status = parcel->writeInt32(getSamplesPerFrame());
- if (status != NO_ERROR) goto error;
- status = parcel->writeInt32((int32_t) getSharingMode());
- if (status != NO_ERROR) goto error;
- status = parcel->writeInt32((int32_t) getFormat());
- if (status != NO_ERROR) goto error;
-
- status = parcel->writeInt32((int32_t) getDirection());
- if (status != NO_ERROR) goto error;
- status = parcel->writeInt32(getBufferCapacity());
- if (status != NO_ERROR) goto error;
- status = parcel->writeInt32((int32_t) getUsage());
- if (status != NO_ERROR) goto error;
- status = parcel->writeInt32((int32_t) getContentType());
- if (status != NO_ERROR) goto error;
- status = parcel->writeInt32((int32_t) getInputPreset());
- if (status != NO_ERROR) goto error;
- status = parcel->writeInt32((int32_t) getAllowedCapturePolicy());
- 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);
- return status;
+AAudioStreamConfiguration::AAudioStreamConfiguration(const StreamParameters& parcelable) {
+ setSamplesPerFrame(parcelable.samplesPerFrame);
+ setSampleRate(parcelable.sampleRate);
+ setDeviceId(parcelable.deviceId);
+ static_assert(sizeof(aaudio_sharing_mode_t) == sizeof(parcelable.sharingMode));
+ setSharingMode(parcelable.sharingMode);
+ static_assert(sizeof(audio_format_t) == sizeof(parcelable.audioFormat));
+ setFormat(static_cast<audio_format_t>(parcelable.audioFormat));
+ static_assert(sizeof(aaudio_direction_t) == sizeof(parcelable.direction));
+ setDirection(parcelable.direction);
+ static_assert(sizeof(audio_usage_t) == sizeof(parcelable.usage));
+ setUsage(parcelable.usage);
+ static_assert(sizeof(aaudio_content_type_t) == sizeof(parcelable.contentType));
+ setContentType(parcelable.contentType);
+ static_assert(sizeof(aaudio_input_preset_t) == sizeof(parcelable.inputPreset));
+ setInputPreset(parcelable.inputPreset);
+ setBufferCapacity(parcelable.bufferCapacity);
+ static_assert(
+ sizeof(aaudio_allowed_capture_policy_t) == sizeof(parcelable.allowedCapturePolicy));
+ setAllowedCapturePolicy(parcelable.allowedCapturePolicy);
+ static_assert(sizeof(aaudio_session_id_t) == sizeof(parcelable.sessionId));
+ setSessionId(parcelable.sessionId);
+ setPrivacySensitive(parcelable.isPrivacySensitive);
}
-status_t AAudioStreamConfiguration::readFromParcel(const Parcel* parcel) {
- int32_t value;
- status_t status = parcel->readInt32(&value);
- if (status != NO_ERROR) goto error;
- setDeviceId(value);
- status = parcel->readInt32(&value);
- if (status != NO_ERROR) goto error;
- setSampleRate(value);
- status = parcel->readInt32(&value);
- if (status != NO_ERROR) goto error;
- setSamplesPerFrame(value);
- status = parcel->readInt32(&value);
- if (status != NO_ERROR) goto error;
- setSharingMode((aaudio_sharing_mode_t) value);
- status = parcel->readInt32(&value);
- if (status != NO_ERROR) goto error;
- setFormat((audio_format_t) value);
+AAudioStreamConfiguration&
+AAudioStreamConfiguration::operator=(const StreamParameters& parcelable) {
+ this->~AAudioStreamConfiguration();
+ new (this) AAudioStreamConfiguration(parcelable);
+ return *this;
+}
- status = parcel->readInt32(&value);
- if (status != NO_ERROR) goto error;
- setDirection((aaudio_direction_t) value);
- status = parcel->readInt32(&value);
- if (status != NO_ERROR) goto error;
- setBufferCapacity(value);
- status = parcel->readInt32(&value);
- if (status != NO_ERROR) goto error;
- setUsage((aaudio_usage_t) value);
- status = parcel->readInt32(&value);
- if (status != NO_ERROR) goto error;
- setContentType((aaudio_content_type_t) value);
- status = parcel->readInt32(&value);
- if (status != NO_ERROR) goto error;
- setInputPreset((aaudio_input_preset_t) value);
- status = parcel->readInt32(&value);
- if (status != NO_ERROR) goto error;
- setAllowedCapturePolicy((aaudio_allowed_capture_policy_t) value);
- 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);
- return status;
+StreamParameters AAudioStreamConfiguration::parcelable() const {
+ StreamParameters result;
+ result.samplesPerFrame = getSamplesPerFrame();
+ result.sampleRate = getSampleRate();
+ result.deviceId = getDeviceId();
+ static_assert(sizeof(aaudio_sharing_mode_t) == sizeof(result.sharingMode));
+ result.sharingMode = getSharingMode();
+ static_assert(sizeof(audio_format_t) == sizeof(result.audioFormat));
+ result.audioFormat = static_cast<AudioFormat>(getFormat());
+ static_assert(sizeof(aaudio_direction_t) == sizeof(result.direction));
+ result.direction = getDirection();
+ static_assert(sizeof(audio_usage_t) == sizeof(result.usage));
+ result.usage = getUsage();
+ static_assert(sizeof(aaudio_content_type_t) == sizeof(result.contentType));
+ result.contentType = getContentType();
+ static_assert(sizeof(aaudio_input_preset_t) == sizeof(result.inputPreset));
+ result.inputPreset = getInputPreset();
+ result.bufferCapacity = getBufferCapacity();
+ static_assert(sizeof(aaudio_allowed_capture_policy_t) == sizeof(result.allowedCapturePolicy));
+ result.allowedCapturePolicy = getAllowedCapturePolicy();
+ static_assert(sizeof(aaudio_session_id_t) == sizeof(result.sessionId));
+ result.sessionId = getSessionId();
+ result.isPrivacySensitive = isPrivacySensitive();
+ return result;
}
diff --git a/media/libaaudio/src/binding/AAudioStreamConfiguration.h b/media/libaaudio/src/binding/AAudioStreamConfiguration.h
index b324896..f428eb0 100644
--- a/media/libaaudio/src/binding/AAudioStreamConfiguration.h
+++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.h
@@ -20,24 +20,24 @@
#include <stdint.h>
#include <aaudio/AAudio.h>
+#include <aaudio/StreamParameters.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
#include "core/AAudioStreamParameters.h"
-using android::status_t;
-using android::Parcel;
-using android::Parcelable;
-
namespace aaudio {
-class AAudioStreamConfiguration : public AAudioStreamParameters, public Parcelable {
+// This is a holder for AAudioStreamParameters, which allows conversion to/from it parcelable
+// representation, StreamParameters.
+class AAudioStreamConfiguration : public AAudioStreamParameters {
public:
- AAudioStreamConfiguration();
- virtual ~AAudioStreamConfiguration();
+ AAudioStreamConfiguration() = default;
- virtual status_t writeToParcel(Parcel* parcel) const override;
+ explicit AAudioStreamConfiguration(const StreamParameters& parcelable);
- virtual status_t readFromParcel(const Parcel* parcel) override;
+ AAudioStreamConfiguration& operator=(const StreamParameters& parcelable);
+
+ StreamParameters parcelable() const;
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/binding/AAudioStreamRequest.cpp b/media/libaaudio/src/binding/AAudioStreamRequest.cpp
index c30c5b9..5e0a4bb 100644
--- a/media/libaaudio/src/binding/AAudioStreamRequest.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamRequest.cpp
@@ -21,67 +21,28 @@
#include <stdint.h>
#include <sys/mman.h>
-#include <binder/Parcel.h>
-#include <binder/Parcelable.h>
#include <aaudio/AAudio.h>
#include "binding/AAudioStreamConfiguration.h"
#include "binding/AAudioStreamRequest.h"
-using android::NO_ERROR;
-using android::status_t;
-using android::Parcel;
-using android::Parcelable;
-
using namespace aaudio;
-AAudioStreamRequest::AAudioStreamRequest()
- : mConfiguration()
- {}
-
-AAudioStreamRequest::~AAudioStreamRequest() {}
-
-status_t AAudioStreamRequest::writeToParcel(Parcel* parcel) const {
- status_t status = parcel->writeInt32((int32_t) mUserId);
- if (status != NO_ERROR) goto error;
-
- status = parcel->writeBool(mSharingModeMatchRequired);
- if (status != NO_ERROR) goto error;
-
- status = parcel->writeBool(mInService);
- if (status != NO_ERROR) goto error;
-
- status = mConfiguration.writeToParcel(parcel);
- if (status != NO_ERROR) goto error;
-
- return NO_ERROR;
-
-error:
- ALOGE("writeToParcel(): write failed = %d", status);
- return status;
+AAudioStreamRequest::AAudioStreamRequest(const StreamRequest& parcelable) :
+ mConfiguration(std::move(parcelable.params)),
+ mIdentity(parcelable.identity),
+ mSharingModeMatchRequired(parcelable.sharingModeMatchRequired),
+ mInService(parcelable.inService) {
}
-status_t AAudioStreamRequest::readFromParcel(const Parcel* parcel) {
- int32_t temp;
- status_t status = parcel->readInt32(&temp);
- if (status != NO_ERROR) goto error;
- mUserId = (uid_t) temp;
-
- status = parcel->readBool(&mSharingModeMatchRequired);
- if (status != NO_ERROR) goto error;
-
- status = parcel->readBool(&mInService);
- if (status != NO_ERROR) goto error;
-
- status = mConfiguration.readFromParcel(parcel);
- if (status != NO_ERROR) goto error;
-
- return NO_ERROR;
-
-error:
- ALOGE("readFromParcel(): read failed = %d", status);
- return status;
+StreamRequest AAudioStreamRequest::parcelable() const {
+ StreamRequest result;
+ result.params = std::move(mConfiguration).parcelable();
+ result.identity = mIdentity;
+ result.sharingModeMatchRequired = mSharingModeMatchRequired;
+ result.inService = mInService;
+ return result;
}
aaudio_result_t AAudioStreamRequest::validate() const {
@@ -89,8 +50,7 @@
}
void AAudioStreamRequest::dump() const {
- ALOGD("mUserId = %d", mUserId);
- ALOGD("mProcessId = %d", mProcessId);
+ ALOGD("mIdentity = %s", mIdentity.toString().c_str());
ALOGD("mSharingModeMatchRequired = %d", mSharingModeMatchRequired);
ALOGD("mInService = %d", mInService);
mConfiguration.dump();
diff --git a/media/libaaudio/src/binding/AAudioStreamRequest.h b/media/libaaudio/src/binding/AAudioStreamRequest.h
index 492f69d..02341c8 100644
--- a/media/libaaudio/src/binding/AAudioStreamRequest.h
+++ b/media/libaaudio/src/binding/AAudioStreamRequest.h
@@ -20,36 +20,26 @@
#include <stdint.h>
#include <aaudio/AAudio.h>
-#include <binder/Parcel.h>
-#include <binder/Parcelable.h>
+#include <aaudio/StreamRequest.h>
#include "binding/AAudioStreamConfiguration.h"
-
-using android::status_t;
-using android::Parcel;
-using android::Parcelable;
+#include <android/media/permission/Identity.h>
namespace aaudio {
-class AAudioStreamRequest : public Parcelable {
+class AAudioStreamRequest {
public:
- AAudioStreamRequest();
- virtual ~AAudioStreamRequest();
+ AAudioStreamRequest() = default;
- uid_t getUserId() const {
- return mUserId;
+ // Construct based on a parcelable representation.
+ explicit AAudioStreamRequest(const StreamRequest& parcelable);
+
+ const android::media::permission::Identity &getIdentity() const {
+ return mIdentity;
}
- void setUserId(uid_t userId) {
- mUserId = userId;
- }
-
- pid_t getProcessId() const {
- return mProcessId;
- }
-
- void setProcessId(pid_t processId) {
- mProcessId = processId;
+ void setIdentity(const android::media::permission::Identity &identity) {
+ mIdentity = identity;
}
bool isSharingModeMatchRequired() const {
@@ -76,18 +66,16 @@
mInService = inService;
}
- virtual status_t writeToParcel(Parcel* parcel) const override;
-
- virtual status_t readFromParcel(const Parcel* parcel) override;
-
aaudio_result_t validate() const;
void dump() const;
-protected:
+ // Extract a parcelable representation of this object.
+ StreamRequest parcelable() const;
+
+private:
AAudioStreamConfiguration mConfiguration;
- uid_t mUserId = (uid_t) -1;
- pid_t mProcessId = (pid_t) -1;
+ android::media::permission::Identity mIdentity;
bool mSharingModeMatchRequired = false;
bool mInService = false; // Stream opened by AAudioservice
};
diff --git a/media/libaaudio/src/binding/AudioEndpointParcelable.cpp b/media/libaaudio/src/binding/AudioEndpointParcelable.cpp
index 61d7d27..aa4ac27 100644
--- a/media/libaaudio/src/binding/AudioEndpointParcelable.cpp
+++ b/media/libaaudio/src/binding/AudioEndpointParcelable.cpp
@@ -29,22 +29,43 @@
#include "binding/AudioEndpointParcelable.h"
using android::base::unique_fd;
+using android::media::SharedFileRegion;
using android::NO_ERROR;
using android::status_t;
-using android::Parcel;
-using android::Parcelable;
using namespace aaudio;
-/**
- * Container for information about the message queues plus
- * general stream information needed by AAudio clients.
- * It contains no addresses, just sizes, offsets and file descriptors for
- * shared memory that can be passed through Binder.
- */
-AudioEndpointParcelable::AudioEndpointParcelable() {}
+AudioEndpointParcelable::AudioEndpointParcelable(Endpoint&& parcelable)
+ : mUpMessageQueueParcelable(std::move(parcelable.upMessageQueueParcelable)),
+ mDownMessageQueueParcelable(std::move(parcelable.downMessageQueueParcelable)),
+ mUpDataQueueParcelable(std::move(parcelable.upDataQueueParcelable)),
+ mDownDataQueueParcelable(std::move(parcelable.downDataQueueParcelable)),
+ mNumSharedMemories(parcelable.sharedMemories.size()) {
+ for (size_t i = 0; i < parcelable.sharedMemories.size() && i < MAX_SHARED_MEMORIES; ++i) {
+ // Re-construct.
+ mSharedMemories[i].~SharedMemoryParcelable();
+ new(&mSharedMemories[i]) SharedMemoryParcelable(std::move(parcelable.sharedMemories[i]));
+ }
+}
-AudioEndpointParcelable::~AudioEndpointParcelable() {}
+AudioEndpointParcelable& AudioEndpointParcelable::operator=(Endpoint&& parcelable) {
+ this->~AudioEndpointParcelable();
+ new(this) AudioEndpointParcelable(std::move(parcelable));
+ return *this;
+}
+
+Endpoint AudioEndpointParcelable::parcelable()&& {
+ Endpoint result;
+ result.upMessageQueueParcelable = std::move(mUpMessageQueueParcelable).parcelable();
+ result.downMessageQueueParcelable = std::move(mDownMessageQueueParcelable).parcelable();
+ result.upDataQueueParcelable = std::move(mUpDataQueueParcelable).parcelable();
+ result.downDataQueueParcelable = std::move(mDownDataQueueParcelable).parcelable();
+ result.sharedMemories.reserve(std::min(mNumSharedMemories, MAX_SHARED_MEMORIES));
+ for (size_t i = 0; i < mNumSharedMemories && i < MAX_SHARED_MEMORIES; ++i) {
+ result.sharedMemories.emplace_back(std::move(mSharedMemories[i]).parcelable());
+ }
+ return result;
+}
/**
* Add the file descriptor to the table.
@@ -60,60 +81,6 @@
return index;
}
-/**
- * The read and write must be symmetric.
- */
-status_t AudioEndpointParcelable::writeToParcel(Parcel* parcel) const {
- status_t status = AAudioConvert_aaudioToAndroidStatus(validate());
- if (status != NO_ERROR) goto error;
-
- status = parcel->writeInt32(mNumSharedMemories);
- if (status != NO_ERROR) goto error;
-
- for (int i = 0; i < mNumSharedMemories; i++) {
- status = mSharedMemories[i].writeToParcel(parcel);
- if (status != NO_ERROR) goto error;
- }
- status = mUpMessageQueueParcelable.writeToParcel(parcel);
- if (status != NO_ERROR) goto error;
- status = mDownMessageQueueParcelable.writeToParcel(parcel);
- if (status != NO_ERROR) goto error;
- status = mUpDataQueueParcelable.writeToParcel(parcel);
- if (status != NO_ERROR) goto error;
- status = mDownDataQueueParcelable.writeToParcel(parcel);
- if (status != NO_ERROR) goto error;
-
- return NO_ERROR;
-
-error:
- ALOGE("%s returning %d", __func__, status);
- return status;
-}
-
-status_t AudioEndpointParcelable::readFromParcel(const Parcel* parcel) {
- status_t status = parcel->readInt32(&mNumSharedMemories);
- if (status != NO_ERROR) goto error;
-
- for (int i = 0; i < mNumSharedMemories; i++) {
- mSharedMemories[i].readFromParcel(parcel);
- if (status != NO_ERROR) goto error;
- }
- status = mUpMessageQueueParcelable.readFromParcel(parcel);
- if (status != NO_ERROR) goto error;
- status = mDownMessageQueueParcelable.readFromParcel(parcel);
- if (status != NO_ERROR) goto error;
- status = mUpDataQueueParcelable.readFromParcel(parcel);
- if (status != NO_ERROR) goto error;
- status = mDownDataQueueParcelable.readFromParcel(parcel);
- if (status != NO_ERROR) goto error;
-
- return AAudioConvert_aaudioToAndroidStatus(validate());
-
-error:
- ALOGE("%s returning %d", __func__, status);
- return status;
-}
-
aaudio_result_t AudioEndpointParcelable::resolve(EndpointDescriptor *descriptor) {
aaudio_result_t result = mUpMessageQueueParcelable.resolve(mSharedMemories,
&descriptor->upMessageQueueDescriptor);
diff --git a/media/libaaudio/src/binding/AudioEndpointParcelable.h b/media/libaaudio/src/binding/AudioEndpointParcelable.h
index e4f8b9e..5237a1a 100644
--- a/media/libaaudio/src/binding/AudioEndpointParcelable.h
+++ b/media/libaaudio/src/binding/AudioEndpointParcelable.h
@@ -20,16 +20,13 @@
#include <stdint.h>
//#include <sys/mman.h>
+#include <aaudio/Endpoint.h>
#include <android-base/unique_fd.h>
-#include <binder/Parcel.h>
-#include <binder/Parcelable.h>
#include "binding/AAudioServiceDefinitions.h"
#include "binding/RingBufferParcelable.h"
using android::status_t;
-using android::Parcel;
-using android::Parcelable;
namespace aaudio {
@@ -39,10 +36,15 @@
* It contains no addresses, just sizes, offsets and file descriptors for
* shared memory that can be passed through Binder.
*/
-class AudioEndpointParcelable : public Parcelable {
+class AudioEndpointParcelable {
public:
- AudioEndpointParcelable();
- virtual ~AudioEndpointParcelable();
+ AudioEndpointParcelable() = default;
+
+ // Ctor/assignment from a parcelable representation.
+ // Since the parcelable object owns unique FDs (for shared memory blocks), move semantics are
+ // provided to avoid the need to dupe.
+ AudioEndpointParcelable(Endpoint&& parcelable);
+ AudioEndpointParcelable& operator=(Endpoint&& parcelable);
/**
* Add the file descriptor to the table.
@@ -50,16 +52,17 @@
*/
int32_t addFileDescriptor(const android::base::unique_fd& fd, int32_t sizeInBytes);
- virtual status_t writeToParcel(Parcel* parcel) const override;
-
- virtual status_t readFromParcel(const Parcel* parcel) override;
-
aaudio_result_t resolve(EndpointDescriptor *descriptor);
aaudio_result_t close();
void dump();
+ // Extract a parcelable representation of this object.
+ // Since our shared memory objects own a unique FD, move semantics are provided to avoid the
+ // need to dupe.
+ Endpoint parcelable()&&;
+
public: // TODO add getters
// Set capacityInFrames to zero if Queue is unused.
RingBufferParcelable mUpMessageQueueParcelable; // server to client
diff --git a/media/libaaudio/src/binding/IAAudioClient.cpp b/media/libaaudio/src/binding/IAAudioClient.cpp
deleted file mode 100644
index c69c4e8..0000000
--- a/media/libaaudio/src/binding/IAAudioClient.cpp
+++ /dev/null
@@ -1,85 +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.
- */
-
-#define LOG_TAG "AAudio"
-//#define LOG_NDEBUG 0
-#include <utils/Log.h>
-
-#include <aaudio/AAudio.h>
-
-#include "binding/AAudioBinderClient.h"
-#include "binding/AAudioServiceDefinitions.h"
-#include "binding/IAAudioClient.h"
-#include "utility/AAudioUtilities.h"
-
-namespace android {
-
-using aaudio::aaudio_handle_t;
-
-/**
- * This is used by the AAudio Service to talk to an AAudio Client.
- *
- * The order of parameters in the Parcels must match with code in AAudioClient.cpp.
- */
-class BpAAudioClient : public BpInterface<IAAudioClient>
-{
-public:
- explicit BpAAudioClient(const sp<IBinder>& impl)
- : BpInterface<IAAudioClient>(impl)
- {
- }
-
- void onStreamChange(aaudio_handle_t handle, int32_t opcode, int32_t value) override {
- Parcel data, reply;
- data.writeInterfaceToken(IAAudioClient::getInterfaceDescriptor());
- data.writeInt32(handle);
- data.writeInt32(opcode);
- data.writeInt32(value);
- remote()->transact(ON_STREAM_CHANGE, data, &reply, IBinder::FLAG_ONEWAY);
- }
-
-};
-
-// Implement an interface to the service.
-IMPLEMENT_META_INTERFACE(AAudioClient, "IAAudioClient");
-
-// The order of parameters in the Parcels must match with code in BpAAudioClient
-
-status_t BnAAudioClient::onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags) {
- aaudio_handle_t streamHandle;
- int32_t opcode = 0;
- int32_t value = 0;
- ALOGV("BnAAudioClient::onTransact(%u) %u", code, flags);
-
- switch(code) {
- case ON_STREAM_CHANGE: {
- CHECK_INTERFACE(IAAudioClient, data, reply);
- data.readInt32(&streamHandle);
- data.readInt32(&opcode);
- data.readInt32(&value);
- onStreamChange(streamHandle, opcode, value);
- ALOGD("BnAAudioClient onStreamChange(%x, %d, %d)", streamHandle, opcode, value);
- return NO_ERROR;
- } break;
-
- default:
- // ALOGW("BnAAudioClient::onTransact not handled %u", code);
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-} /* namespace android */
diff --git a/media/libaaudio/src/binding/IAAudioClient.h b/media/libaaudio/src/binding/IAAudioClient.h
deleted file mode 100644
index f21fd93..0000000
--- a/media/libaaudio/src/binding/IAAudioClient.h
+++ /dev/null
@@ -1,48 +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_AAUDIO_IAAUDIO_CLIENT_H
-#define ANDROID_AAUDIO_IAAUDIO_CLIENT_H
-
-#include <stdint.h>
-#include <binder/IInterface.h>
-
-#include <aaudio/AAudio.h>
-
-#include "binding/AAudioCommon.h"
-
-namespace android {
-
-
-// Interface (our AIDL) - client methods called by service
-class IAAudioClient : public IInterface {
-public:
-
- DECLARE_META_INTERFACE(AAudioClient);
-
- virtual void onStreamChange(aaudio::aaudio_handle_t handle, int32_t opcode, int32_t value) = 0;
-
-};
-
-class BnAAudioClient : public BnInterface<IAAudioClient> {
-public:
- virtual status_t onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags = 0);
-};
-
-} /* namespace android */
-
-#endif //ANDROID_AAUDIO_IAAUDIO_SERVICE_H
diff --git a/media/libaaudio/src/binding/IAAudioService.cpp b/media/libaaudio/src/binding/IAAudioService.cpp
deleted file mode 100644
index e017b3a..0000000
--- a/media/libaaudio/src/binding/IAAudioService.cpp
+++ /dev/null
@@ -1,424 +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 "AAudio"
-//#define LOG_NDEBUG 0
-#include <utils/Log.h>
-
-#include <aaudio/AAudio.h>
-#include <binder/IPCThreadState.h>
-
-#include "binding/AudioEndpointParcelable.h"
-#include "binding/AAudioStreamRequest.h"
-#include "binding/AAudioServiceDefinitions.h"
-#include "binding/AAudioStreamConfiguration.h"
-#include "binding/IAAudioService.h"
-#include "utility/AAudioUtilities.h"
-
-namespace android {
-
-using aaudio::aaudio_handle_t;
-
-/**
- * This is used by the AAudio Client to talk to the AAudio Service.
- *
- * The order of parameters in the Parcels must match with code in AAudioService.cpp.
- */
-class BpAAudioService : public BpInterface<IAAudioService>
-{
-public:
- explicit BpAAudioService(const sp<IBinder>& impl)
- : BpInterface<IAAudioService>(impl)
- {
- }
-
- void registerClient(const sp<IAAudioClient>& client) override
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
- data.writeStrongBinder(IInterface::asBinder(client));
- remote()->transact(REGISTER_CLIENT, data, &reply);
- }
-
- aaudio_handle_t openStream(const aaudio::AAudioStreamRequest &request,
- aaudio::AAudioStreamConfiguration &configurationOutput) override {
- Parcel data, reply;
- // send command
- data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
- // request.dump();
- request.writeToParcel(&data);
- status_t err = remote()->transact(OPEN_STREAM, data, &reply);
- if (err != NO_ERROR) {
- ALOGE("BpAAudioService::client openStream transact failed %d", err);
- return AAudioConvert_androidToAAudioResult(err);
- }
- // parse reply
- aaudio_handle_t stream;
- err = reply.readInt32(&stream);
- if (err != NO_ERROR) {
- ALOGE("BpAAudioService::client transact(OPEN_STREAM) readInt %d", err);
- return AAudioConvert_androidToAAudioResult(err);
- } else if (stream < 0) {
- return stream;
- }
- err = configurationOutput.readFromParcel(&reply);
- if (err != NO_ERROR) {
- ALOGE("BpAAudioService::client openStream readFromParcel failed %d", err);
- closeStream(stream);
- return AAudioConvert_androidToAAudioResult(err);
- }
- return stream;
- }
-
- virtual aaudio_result_t closeStream(aaudio_handle_t streamHandle) override {
- Parcel data, reply;
- // send command
- data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
- data.writeInt32(streamHandle);
- status_t err = remote()->transact(CLOSE_STREAM, data, &reply);
- if (err != NO_ERROR) {
- ALOGE("BpAAudioService::client closeStream transact failed %d", err);
- return AAudioConvert_androidToAAudioResult(err);
- }
- // parse reply
- aaudio_result_t res;
- reply.readInt32(&res);
- return res;
- }
-
- virtual aaudio_result_t getStreamDescription(aaudio_handle_t streamHandle,
- aaudio::AudioEndpointParcelable &parcelable) {
- Parcel data, reply;
- // send command
- data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
- data.writeInt32(streamHandle);
- status_t err = remote()->transact(GET_STREAM_DESCRIPTION, data, &reply);
- if (err != NO_ERROR) {
- ALOGE("BpAAudioService::client transact(GET_STREAM_DESCRIPTION) returns %d", err);
- return AAudioConvert_androidToAAudioResult(err);
- }
- // parse reply
- aaudio_result_t result;
- err = reply.readInt32(&result);
- if (err != NO_ERROR) {
- ALOGE("BpAAudioService::client transact(GET_STREAM_DESCRIPTION) readInt %d", err);
- return AAudioConvert_androidToAAudioResult(err);
- } else if (result != AAUDIO_OK) {
- ALOGE("BpAAudioService::client GET_STREAM_DESCRIPTION passed result %d", result);
- return result;
- }
- err = parcelable.readFromParcel(&reply);
- if (err != NO_ERROR) {
- ALOGE("BpAAudioService::client transact(GET_STREAM_DESCRIPTION) read endpoint %d", err);
- return AAudioConvert_androidToAAudioResult(err);
- }
- return result;
- }
-
- // TODO should we wait for a reply?
- virtual aaudio_result_t startStream(aaudio_handle_t streamHandle) override {
- Parcel data, reply;
- // send command
- data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
- data.writeInt32(streamHandle);
- status_t err = remote()->transact(START_STREAM, data, &reply);
- if (err != NO_ERROR) {
- return AAudioConvert_androidToAAudioResult(err);
- }
- // parse reply
- aaudio_result_t res;
- reply.readInt32(&res);
- return res;
- }
-
- virtual aaudio_result_t pauseStream(aaudio_handle_t streamHandle) override {
- Parcel data, reply;
- // send command
- data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
- data.writeInt32(streamHandle);
- status_t err = remote()->transact(PAUSE_STREAM, data, &reply);
- if (err != NO_ERROR) {
- return AAudioConvert_androidToAAudioResult(err);
- }
- // parse reply
- aaudio_result_t res;
- reply.readInt32(&res);
- return res;
- }
-
- virtual aaudio_result_t stopStream(aaudio_handle_t streamHandle) override {
- Parcel data, reply;
- // send command
- data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
- data.writeInt32(streamHandle);
- status_t err = remote()->transact(STOP_STREAM, data, &reply);
- if (err != NO_ERROR) {
- return AAudioConvert_androidToAAudioResult(err);
- }
- // parse reply
- aaudio_result_t res;
- reply.readInt32(&res);
- return res;
- }
-
- virtual aaudio_result_t flushStream(aaudio_handle_t streamHandle) override {
- Parcel data, reply;
- // send command
- data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
- data.writeInt32(streamHandle);
- status_t err = remote()->transact(FLUSH_STREAM, data, &reply);
- if (err != NO_ERROR) {
- return AAudioConvert_androidToAAudioResult(err);
- }
- // parse reply
- aaudio_result_t res;
- reply.readInt32(&res);
- return res;
- }
-
- virtual aaudio_result_t registerAudioThread(aaudio_handle_t streamHandle,
- pid_t clientThreadId,
- int64_t periodNanoseconds)
- override {
- Parcel data, reply;
- // send command
- data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
- data.writeInt32(streamHandle);
- data.writeInt32((int32_t) clientThreadId);
- data.writeInt64(periodNanoseconds);
- status_t err = remote()->transact(REGISTER_AUDIO_THREAD, data, &reply);
- if (err != NO_ERROR) {
- return AAudioConvert_androidToAAudioResult(err);
- }
- // parse reply
- aaudio_result_t res;
- reply.readInt32(&res);
- return res;
- }
-
- virtual aaudio_result_t unregisterAudioThread(aaudio_handle_t streamHandle,
- pid_t clientThreadId)
- override {
- Parcel data, reply;
- // send command
- data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
- data.writeInt32(streamHandle);
- data.writeInt32((int32_t) clientThreadId);
- status_t err = remote()->transact(UNREGISTER_AUDIO_THREAD, data, &reply);
- if (err != NO_ERROR) {
- return AAudioConvert_androidToAAudioResult(err);
- }
- // parse reply
- aaudio_result_t res;
- reply.readInt32(&res);
- return res;
- }
-
-};
-
-// Implement an interface to the service.
-// This is here so that you don't have to link with libaaudio static library.
-IMPLEMENT_META_INTERFACE(AAudioService, "IAAudioService");
-
-// The order of parameters in the Parcels must match with code in BpAAudioService
-
-status_t BnAAudioService::onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags) {
- aaudio_handle_t streamHandle = 0;
- aaudio::AAudioStreamRequest request;
- aaudio::AAudioStreamConfiguration configuration;
- pid_t tid = 0;
- int64_t nanoseconds = 0;
- aaudio_result_t result = AAUDIO_OK;
- status_t status = NO_ERROR;
- ALOGV("BnAAudioService::onTransact(%i) %i", code, flags);
-
- switch(code) {
- case REGISTER_CLIENT: {
- CHECK_INTERFACE(IAAudioService, data, reply);
- sp<IAAudioClient> client = interface_cast<IAAudioClient>(
- data.readStrongBinder());
- // readStrongBinder() can return null
- if (client.get() == nullptr) {
- ALOGE("BnAAudioService::%s(REGISTER_CLIENT) client is NULL!", __func__);
- android_errorWriteLog(0x534e4554, "116230453");
- return DEAD_OBJECT;
- } else {
- registerClient(client);
- return NO_ERROR;
- }
- } break;
-
- case OPEN_STREAM: {
- CHECK_INTERFACE(IAAudioService, data, reply);
- request.readFromParcel(&data);
- result = request.validate();
- if (result != AAUDIO_OK) {
- streamHandle = result;
- } else {
- //ALOGD("BnAAudioService::client openStream request dump --------------------");
- //request.dump();
- // Override the uid and pid from the client in case they are incorrect.
- request.setUserId(IPCThreadState::self()->getCallingUid());
- request.setProcessId(IPCThreadState::self()->getCallingPid());
- streamHandle = openStream(request, configuration);
- //ALOGD("BnAAudioService::onTransact OPEN_STREAM server handle = 0x%08X",
- // streamHandle);
- }
- reply->writeInt32(streamHandle);
- configuration.writeToParcel(reply);
- return NO_ERROR;
- } break;
-
- case CLOSE_STREAM: {
- CHECK_INTERFACE(IAAudioService, data, reply);
- status = data.readInt32(&streamHandle);
- if (status != NO_ERROR) {
- ALOGE("BnAAudioService::%s(CLOSE_STREAM) streamHandle failed!", __func__);
- return status;
- }
- result = closeStream(streamHandle);
- //ALOGD("BnAAudioService::onTransact CLOSE_STREAM 0x%08X, result = %d",
- // streamHandle, result);
- reply->writeInt32(result);
- return NO_ERROR;
- } break;
-
- case GET_STREAM_DESCRIPTION: {
- CHECK_INTERFACE(IAAudioService, data, reply);
- status = data.readInt32(&streamHandle);
- if (status != NO_ERROR) {
- ALOGE("BnAAudioService::%s(GET_STREAM_DESCRIPTION) streamHandle failed!", __func__);
- return status;
- }
- aaudio::AudioEndpointParcelable parcelable;
- result = getStreamDescription(streamHandle, parcelable);
- if (result != AAUDIO_OK) {
- return AAudioConvert_aaudioToAndroidStatus(result);
- }
- status = reply->writeInt32(result);
- if (status != NO_ERROR) {
- return status;
- }
- return parcelable.writeToParcel(reply);
- } break;
-
- case START_STREAM: {
- CHECK_INTERFACE(IAAudioService, data, reply);
- status = data.readInt32(&streamHandle);
- if (status != NO_ERROR) {
- ALOGE("BnAAudioService::%s(START_STREAM) streamHandle failed!", __func__);
- return status;
- }
- result = startStream(streamHandle);
- ALOGV("BnAAudioService::onTransact START_STREAM 0x%08X, result = %d",
- streamHandle, result);
- reply->writeInt32(result);
- return NO_ERROR;
- } break;
-
- case PAUSE_STREAM: {
- CHECK_INTERFACE(IAAudioService, data, reply);
- status = data.readInt32(&streamHandle);
- if (status != NO_ERROR) {
- ALOGE("BnAAudioService::%s(PAUSE_STREAM) streamHandle failed!", __func__);
- return status;
- }
- result = pauseStream(streamHandle);
- ALOGV("BnAAudioService::onTransact PAUSE_STREAM 0x%08X, result = %d",
- streamHandle, result);
- reply->writeInt32(result);
- return NO_ERROR;
- } break;
-
- case STOP_STREAM: {
- CHECK_INTERFACE(IAAudioService, data, reply);
- status = data.readInt32(&streamHandle);
- if (status != NO_ERROR) {
- ALOGE("BnAAudioService::%s(STOP_STREAM) streamHandle failed!", __func__);
- return status;
- }
- result = stopStream(streamHandle);
- ALOGV("BnAAudioService::onTransact STOP_STREAM 0x%08X, result = %d",
- streamHandle, result);
- reply->writeInt32(result);
- return NO_ERROR;
- } break;
-
- case FLUSH_STREAM: {
- CHECK_INTERFACE(IAAudioService, data, reply);
- status = data.readInt32(&streamHandle);
- if (status != NO_ERROR) {
- ALOGE("BnAAudioService::%s(FLUSH_STREAM) streamHandle failed!", __func__);
- return status;
- }
- result = flushStream(streamHandle);
- ALOGV("BnAAudioService::onTransact FLUSH_STREAM 0x%08X, result = %d",
- streamHandle, result);
- reply->writeInt32(result);
- return NO_ERROR;
- } break;
-
- case REGISTER_AUDIO_THREAD: {
- CHECK_INTERFACE(IAAudioService, data, reply);
- status = data.readInt32(&streamHandle);
- if (status != NO_ERROR) {
- ALOGE("BnAAudioService::%s(REGISTER_AUDIO_THREAD) streamHandle failed!", __func__);
- return status;
- }
- status = data.readInt32(&tid);
- if (status != NO_ERROR) {
- ALOGE("BnAAudioService::%s(REGISTER_AUDIO_THREAD) tid failed!", __func__);
- return status;
- }
- status = data.readInt64(&nanoseconds);
- if (status != NO_ERROR) {
- ALOGE("BnAAudioService::%s(REGISTER_AUDIO_THREAD) nanoseconds failed!", __func__);
- return status;
- }
- result = registerAudioThread(streamHandle, tid, nanoseconds);
- ALOGV("BnAAudioService::%s(REGISTER_AUDIO_THREAD) 0x%08X, result = %d",
- __func__, streamHandle, result);
- reply->writeInt32(result);
- return NO_ERROR;
- } break;
-
- case UNREGISTER_AUDIO_THREAD: {
- CHECK_INTERFACE(IAAudioService, data, reply);
- status = data.readInt32(&streamHandle);
- if (status != NO_ERROR) {
- ALOGE("BnAAudioService::%s(UNREGISTER_AUDIO_THREAD) streamHandle failed!", __func__);
- return status;
- }
- status = data.readInt32(&tid);
- if (status != NO_ERROR) {
- ALOGE("BnAAudioService::%s(UNREGISTER_AUDIO_THREAD) tid failed!", __func__);
- return status;
- }
- result = unregisterAudioThread(streamHandle, tid);
- ALOGV("BnAAudioService::onTransact UNREGISTER_AUDIO_THREAD 0x%08X, result = %d",
- streamHandle, result);
- reply->writeInt32(result);
- return NO_ERROR;
- } break;
-
- default:
- // ALOGW("BnAAudioService::onTransact not handled %u", code);
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-} /* namespace android */
diff --git a/media/libaaudio/src/binding/IAAudioService.h b/media/libaaudio/src/binding/IAAudioService.h
deleted file mode 100644
index 6bdb826..0000000
--- a/media/libaaudio/src/binding/IAAudioService.h
+++ /dev/null
@@ -1,109 +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_AAUDIO_IAAUDIO_SERVICE_H
-#define ANDROID_AAUDIO_IAAUDIO_SERVICE_H
-
-#include <stdint.h>
-#include <utils/RefBase.h>
-#include <binder/TextOutput.h>
-#include <binder/IInterface.h>
-
-#include <aaudio/AAudio.h>
-
-#include "binding/AAudioCommon.h"
-#include "binding/AAudioServiceDefinitions.h"
-#include "binding/AAudioStreamConfiguration.h"
-#include "binding/AAudioStreamRequest.h"
-#include "binding/AudioEndpointParcelable.h"
-#include "binding/IAAudioClient.h"
-
-namespace android {
-
-#define AAUDIO_SERVICE_NAME "media.aaudio"
-
-// Interface (our AIDL) - service methods called by client
-class IAAudioService : public IInterface {
-public:
-
- DECLARE_META_INTERFACE(AAudioService);
-
- // Register an object to receive audio input/output change and track notifications.
- // For a given calling pid, AAudio service disregards any registrations after the first.
- // Thus the IAAudioClient must be a singleton per process.
- virtual void registerClient(const sp<IAAudioClient>& client) = 0;
-
- /**
- * @param request info needed to create the stream
- * @param configuration contains information about the created stream
- * @return handle to the stream or a negative error
- */
- virtual aaudio::aaudio_handle_t openStream(const aaudio::AAudioStreamRequest &request,
- aaudio::AAudioStreamConfiguration &configurationOutput) = 0;
-
- virtual aaudio_result_t closeStream(aaudio::aaudio_handle_t streamHandle) = 0;
-
- /* Get an immutable description of the in-memory queues
- * used to communicate with the underlying HAL or Service.
- */
- virtual aaudio_result_t getStreamDescription(aaudio::aaudio_handle_t streamHandle,
- aaudio::AudioEndpointParcelable &parcelable) = 0;
-
- /**
- * Start the flow of data.
- * This is asynchronous. When complete, the service will send a STARTED event.
- */
- virtual aaudio_result_t startStream(aaudio::aaudio_handle_t streamHandle) = 0;
-
- /**
- * Stop the flow of data such that start() can resume without loss of data.
- * This is asynchronous. When complete, the service will send a PAUSED event.
- */
- virtual aaudio_result_t pauseStream(aaudio::aaudio_handle_t streamHandle) = 0;
-
- /**
- * Stop the flow of data such that the data currently in the buffer is played.
- * This is asynchronous. When complete, the service will send a STOPPED event.
- */
- virtual aaudio_result_t stopStream(aaudio::aaudio_handle_t streamHandle) = 0;
-
- /**
- * Discard any data held by the underlying HAL or Service.
- * This is asynchronous. When complete, the service will send a FLUSHED event.
- */
- virtual aaudio_result_t flushStream(aaudio::aaudio_handle_t streamHandle) = 0;
-
- /**
- * Manage the specified thread as a low latency audio thread.
- */
- virtual aaudio_result_t registerAudioThread(aaudio::aaudio_handle_t streamHandle,
- pid_t clientThreadId,
- int64_t periodNanoseconds) = 0;
-
- virtual aaudio_result_t unregisterAudioThread(aaudio::aaudio_handle_t streamHandle,
- pid_t clientThreadId) = 0;
-};
-
-class BnAAudioService : public BnInterface<IAAudioService> {
-public:
- virtual status_t onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags = 0);
-
-};
-
-} /* namespace android */
-
-#endif //ANDROID_AAUDIO_IAAUDIO_SERVICE_H
diff --git a/media/libaaudio/src/binding/RingBufferParcelable.cpp b/media/libaaudio/src/binding/RingBufferParcelable.cpp
index 4996b3f..a4b3cec 100644
--- a/media/libaaudio/src/binding/RingBufferParcelable.cpp
+++ b/media/libaaudio/src/binding/RingBufferParcelable.cpp
@@ -29,8 +29,29 @@
using namespace aaudio;
-RingBufferParcelable::RingBufferParcelable() {}
-RingBufferParcelable::~RingBufferParcelable() {}
+RingBufferParcelable::RingBufferParcelable(const RingBuffer& parcelable)
+ : mReadCounterParcelable(std::move(parcelable.readCounterParcelable)),
+ mWriteCounterParcelable(std::move(parcelable.writeCounterParcelable)),
+ mDataParcelable(std::move(parcelable.dataParcelable)),
+ mBytesPerFrame(parcelable.bytesPerFrame),
+ mFramesPerBurst(parcelable.framesPerBurst),
+ mCapacityInFrames(parcelable.capacityInFrames),
+ mFlags(static_cast<RingbufferFlags>(parcelable.flags)) {
+ static_assert(sizeof(mFlags) == sizeof(parcelable.flags));
+}
+
+RingBuffer RingBufferParcelable::parcelable() const {
+ RingBuffer result;
+ result.readCounterParcelable = std::move(mReadCounterParcelable).parcelable();
+ result.writeCounterParcelable = std::move(mWriteCounterParcelable).parcelable();
+ result.dataParcelable = std::move(mDataParcelable).parcelable();
+ result.bytesPerFrame = mBytesPerFrame;
+ result.framesPerBurst = mFramesPerBurst;
+ result.capacityInFrames = mCapacityInFrames;
+ static_assert(sizeof(mFlags) == sizeof(result.flags));
+ result.flags = static_cast<int32_t>(mFlags);
+ return result;
+}
// TODO This assumes that all three use the same SharedMemoryParcelable
void RingBufferParcelable::setupMemory(int32_t sharedMemoryIndex,
@@ -76,58 +97,6 @@
mCapacityInFrames = capacityInFrames;
}
-/**
- * The read and write must be symmetric.
- */
-status_t RingBufferParcelable::writeToParcel(Parcel* parcel) const {
- status_t status = AAudioConvert_aaudioToAndroidStatus(validate());
- if (status != NO_ERROR) goto error;
-
- status = parcel->writeInt32(mCapacityInFrames);
- if (status != NO_ERROR) goto error;
- if (mCapacityInFrames > 0) {
- status = parcel->writeInt32(mBytesPerFrame);
- if (status != NO_ERROR) goto error;
- status = parcel->writeInt32(mFramesPerBurst);
- if (status != NO_ERROR) goto error;
- status = parcel->writeInt32(mFlags);
- if (status != NO_ERROR) goto error;
- status = mReadCounterParcelable.writeToParcel(parcel);
- if (status != NO_ERROR) goto error;
- status = mWriteCounterParcelable.writeToParcel(parcel);
- if (status != NO_ERROR) goto error;
- status = mDataParcelable.writeToParcel(parcel);
- if (status != NO_ERROR) goto error;
- }
- return NO_ERROR;
-error:
- ALOGE("%s returning %d", __func__, status);
- return status;
-}
-
-status_t RingBufferParcelable::readFromParcel(const Parcel* parcel) {
- status_t status = parcel->readInt32(&mCapacityInFrames);
- if (status != NO_ERROR) goto error;
- if (mCapacityInFrames > 0) {
- status = parcel->readInt32(&mBytesPerFrame);
- if (status != NO_ERROR) goto error;
- status = parcel->readInt32(&mFramesPerBurst);
- if (status != NO_ERROR) goto error;
- status = parcel->readInt32((int32_t *)&mFlags);
- if (status != NO_ERROR) goto error;
- status = mReadCounterParcelable.readFromParcel(parcel);
- if (status != NO_ERROR) goto error;
- status = mWriteCounterParcelable.readFromParcel(parcel);
- if (status != NO_ERROR) goto error;
- status = mDataParcelable.readFromParcel(parcel);
- if (status != NO_ERROR) goto error;
- }
- return AAudioConvert_aaudioToAndroidStatus(validate());
-error:
- ALOGE("%s returning %d", __func__, status);
- return status;
-}
-
aaudio_result_t RingBufferParcelable::resolve(SharedMemoryParcelable *memoryParcels, RingBufferDescriptor *descriptor) {
aaudio_result_t result;
diff --git a/media/libaaudio/src/binding/RingBufferParcelable.h b/media/libaaudio/src/binding/RingBufferParcelable.h
index 1dbcf07..2508cea 100644
--- a/media/libaaudio/src/binding/RingBufferParcelable.h
+++ b/media/libaaudio/src/binding/RingBufferParcelable.h
@@ -19,6 +19,7 @@
#include <stdint.h>
+#include <aaudio/RingBuffer.h>
#include <binder/Parcelable.h>
#include "binding/AAudioServiceDefinitions.h"
@@ -26,10 +27,12 @@
namespace aaudio {
-class RingBufferParcelable : public Parcelable {
+class RingBufferParcelable {
public:
- RingBufferParcelable();
- virtual ~RingBufferParcelable();
+ RingBufferParcelable() = default;
+
+ // Construct based on a parcelable representation.
+ explicit RingBufferParcelable(const RingBuffer& parcelable);
// TODO This assumes that all three use the same SharedMemoryParcelable
void setupMemory(int32_t sharedMemoryIndex,
@@ -57,21 +60,14 @@
bool isFileDescriptorSafe(SharedMemoryParcelable *memoryParcels);
- /**
- * The read and write must be symmetric.
- */
- virtual status_t writeToParcel(Parcel* parcel) const override;
-
- virtual status_t readFromParcel(const Parcel* parcel) override;
-
aaudio_result_t resolve(SharedMemoryParcelable *memoryParcels, RingBufferDescriptor *descriptor);
void dump();
+ // Extract a parcelable representation of this object.
+ RingBuffer parcelable() const;
+
private:
-
- aaudio_result_t validate() const;
-
SharedRegionParcelable mReadCounterParcelable;
SharedRegionParcelable mWriteCounterParcelable;
SharedRegionParcelable mDataParcelable;
@@ -79,6 +75,8 @@
int32_t mFramesPerBurst = 0; // for ISOCHRONOUS queues
int32_t mCapacityInFrames = 0; // zero if unused
RingbufferFlags mFlags = RingbufferFlags::NONE;
+
+ aaudio_result_t validate() const;
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.cpp b/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
index b6e8472..eef238f 100644
--- a/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
+++ b/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
@@ -18,6 +18,7 @@
//#define LOG_NDEBUG 0
#include <utils/Log.h>
+#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
@@ -33,61 +34,37 @@
using android::base::unique_fd;
using android::NO_ERROR;
using android::status_t;
-using android::Parcel;
-using android::Parcelable;
+using android::media::SharedFileRegion;
using namespace aaudio;
-SharedMemoryParcelable::SharedMemoryParcelable() {}
-SharedMemoryParcelable::~SharedMemoryParcelable() {};
+SharedMemoryParcelable::SharedMemoryParcelable(SharedFileRegion&& parcelable) {
+ mFd = parcelable.fd.release();
+ mSizeInBytes = parcelable.size;
+ mOffsetInBytes = parcelable.offset;
+}
+
+SharedFileRegion SharedMemoryParcelable::parcelable() && {
+ SharedFileRegion result;
+ result.fd.reset(std::move(mFd));
+ result.size = mSizeInBytes;
+ result.offset = mOffsetInBytes;
+ return result;
+}
+
+SharedMemoryParcelable SharedMemoryParcelable::dup() const {
+ SharedMemoryParcelable result;
+ result.setup(mFd, static_cast<int32_t>(mSizeInBytes));
+ return result;
+}
void SharedMemoryParcelable::setup(const unique_fd& fd, int32_t sizeInBytes) {
- mFd.reset(dup(fd.get())); // store a duplicate fd
+ constexpr int minFd = 3; // skip over stdout, stdin and stderr
+ mFd.reset(fcntl(fd.get(), F_DUPFD_CLOEXEC, minFd)); // store a duplicate FD
ALOGV("setup(fd = %d -> %d, size = %d) this = %p\n", fd.get(), mFd.get(), sizeInBytes, this);
mSizeInBytes = sizeInBytes;
}
-status_t SharedMemoryParcelable::writeToParcel(Parcel* parcel) const {
- status_t status = AAudioConvert_aaudioToAndroidStatus(validate());
- if (status != NO_ERROR) return status;
-
- status = parcel->writeInt32(mSizeInBytes);
- if (status != NO_ERROR) return status;
- if (mSizeInBytes > 0) {
- ALOGV("writeToParcel() mFd = %d, this = %p\n", mFd.get(), this);
- status = parcel->writeUniqueFileDescriptor(mFd);
- ALOGE_IF(status != NO_ERROR, "SharedMemoryParcelable writeDupFileDescriptor failed : %d",
- status);
- }
- return status;
-}
-
-status_t SharedMemoryParcelable::readFromParcel(const Parcel* parcel) {
- status_t status = parcel->readInt32(&mSizeInBytes);
- if (status != NO_ERROR) goto error;
-
- if (mSizeInBytes > 0) {
- // The Parcel owns the file descriptor and will close it later.
- unique_fd mmapFd;
- status = parcel->readUniqueFileDescriptor(&mmapFd);
- if (status != NO_ERROR) {
- ALOGE("readFromParcel() readUniqueFileDescriptor() failed : %d", status);
- goto error;
- }
-
- // Resolve the memory now while we still have the FD from the Parcel.
- // Closing the FD will not affect the shared memory once mmap() has been called.
- aaudio_result_t result = resolveSharedMemory(mmapFd);
- status = AAudioConvert_aaudioToAndroidStatus(result);
- if (status != NO_ERROR) goto error;
- }
-
- return AAudioConvert_aaudioToAndroidStatus(validate());
-
-error:
- return status;
-}
-
aaudio_result_t SharedMemoryParcelable::close() {
if (mResolvedAddress != MMAP_UNRESOLVED_ADDRESS) {
int err = munmap(mResolvedAddress, mSizeInBytes);
@@ -104,7 +81,7 @@
mResolvedAddress = (uint8_t *) mmap(0, mSizeInBytes, PROT_READ | PROT_WRITE,
MAP_SHARED, fd.get(), 0);
if (mResolvedAddress == MMAP_UNRESOLVED_ADDRESS) {
- ALOGE("mmap() failed for fd = %d, nBytes = %d, errno = %s",
+ ALOGE("mmap() failed for fd = %d, nBytes = %" PRId64 ", errno = %s",
fd.get(), mSizeInBytes, strerror(errno));
return AAUDIO_ERROR_INTERNAL;
}
@@ -118,7 +95,7 @@
return AAUDIO_ERROR_OUT_OF_RANGE;
} else if ((offsetInBytes + sizeInBytes) > mSizeInBytes) {
ALOGE("out of range, offsetInBytes = %d, "
- "sizeInBytes = %d, mSizeInBytes = %d",
+ "sizeInBytes = %d, mSizeInBytes = %" PRId64,
offsetInBytes, sizeInBytes, mSizeInBytes);
return AAUDIO_ERROR_OUT_OF_RANGE;
}
@@ -148,7 +125,11 @@
aaudio_result_t SharedMemoryParcelable::validate() const {
if (mSizeInBytes < 0 || mSizeInBytes >= MAX_MMAP_SIZE_BYTES) {
- ALOGE("invalid mSizeInBytes = %d", mSizeInBytes);
+ ALOGE("invalid mSizeInBytes = %" PRId64, mSizeInBytes);
+ return AAUDIO_ERROR_OUT_OF_RANGE;
+ }
+ if (mOffsetInBytes != 0) {
+ ALOGE("invalid mOffsetInBytes = %" PRId64, mOffsetInBytes);
return AAUDIO_ERROR_OUT_OF_RANGE;
}
return AAUDIO_OK;
@@ -156,5 +137,5 @@
void SharedMemoryParcelable::dump() {
ALOGD("mFd = %d", mFd.get());
- ALOGD("mSizeInBytes = %d", mSizeInBytes);
+ ALOGD("mSizeInBytes = %" PRId64, mSizeInBytes);
}
diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.h b/media/libaaudio/src/binding/SharedMemoryParcelable.h
index 3927f58..1f2c335 100644
--- a/media/libaaudio/src/binding/SharedMemoryParcelable.h
+++ b/media/libaaudio/src/binding/SharedMemoryParcelable.h
@@ -21,8 +21,7 @@
#include <sys/mman.h>
#include <android-base/unique_fd.h>
-#include <binder/Parcel.h>
-#include <binder/Parcelable.h>
+#include <android/media/SharedFileRegion.h>
namespace aaudio {
@@ -36,10 +35,14 @@
* It may be divided into several regions.
* The memory can be shared using Binder or simply shared between threads.
*/
-class SharedMemoryParcelable : public android::Parcelable {
+class SharedMemoryParcelable {
public:
- SharedMemoryParcelable();
- virtual ~SharedMemoryParcelable();
+ SharedMemoryParcelable() = default;
+
+ // Ctor from a parcelable representation.
+ // Since the parcelable object owns a unique FD, move semantics are provided to avoid the need
+ // to dupe.
+ explicit SharedMemoryParcelable(android::media::SharedFileRegion&& parcelable);
/**
* Make a dup() of the fd and store it for later use.
@@ -49,10 +52,6 @@
*/
void setup(const android::base::unique_fd& fd, int32_t sizeInBytes);
- virtual android::status_t writeToParcel(android::Parcel* parcel) const override;
-
- virtual android::status_t readFromParcel(const android::Parcel* parcel) override;
-
// mmap() shared memory
aaudio_result_t resolve(int32_t offsetInBytes, int32_t sizeInBytes, void **regionAddressPtr);
@@ -63,20 +62,23 @@
void dump();
-protected:
+ // Extract a parcelable representation of this object.
+ // Since we own a unique FD, move semantics are provided to avoid the need to dupe.
+ android::media::SharedFileRegion parcelable() &&;
-#define MMAP_UNRESOLVED_ADDRESS reinterpret_cast<uint8_t*>(MAP_FAILED)
-
- aaudio_result_t resolveSharedMemory(const android::base::unique_fd& fd);
-
- android::base::unique_fd mFd;
- int32_t mSizeInBytes = 0;
- uint8_t *mResolvedAddress = MMAP_UNRESOLVED_ADDRESS;
+ // Copy this instance. Duplicates the underlying FD.
+ SharedMemoryParcelable dup() const;
private:
+#define MMAP_UNRESOLVED_ADDRESS reinterpret_cast<uint8_t*>(MAP_FAILED)
+ android::base::unique_fd mFd;
+ int64_t mSizeInBytes = 0;
+ int64_t mOffsetInBytes = 0;
+ uint8_t *mResolvedAddress = MMAP_UNRESOLVED_ADDRESS;
+
+ aaudio_result_t resolveSharedMemory(const android::base::unique_fd& fd);
aaudio_result_t validate() const;
-
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/binding/SharedRegionParcelable.cpp b/media/libaaudio/src/binding/SharedRegionParcelable.cpp
index c776116..56b99c0 100644
--- a/media/libaaudio/src/binding/SharedRegionParcelable.cpp
+++ b/media/libaaudio/src/binding/SharedRegionParcelable.cpp
@@ -36,8 +36,18 @@
using namespace aaudio;
-SharedRegionParcelable::SharedRegionParcelable() {}
-SharedRegionParcelable::~SharedRegionParcelable() {}
+SharedRegionParcelable::SharedRegionParcelable(const SharedRegion& parcelable)
+ : mSharedMemoryIndex(parcelable.sharedMemoryIndex),
+ mOffsetInBytes(parcelable.offsetInBytes),
+ mSizeInBytes(parcelable.sizeInBytes) {}
+
+SharedRegion SharedRegionParcelable::parcelable() const {
+ SharedRegion result;
+ result.sharedMemoryIndex = mSharedMemoryIndex;
+ result.offsetInBytes = mOffsetInBytes;
+ result.sizeInBytes = mSizeInBytes;
+ return result;
+}
void SharedRegionParcelable::setup(int32_t sharedMemoryIndex,
int32_t offsetInBytes,
@@ -47,41 +57,6 @@
mSizeInBytes = sizeInBytes;
}
-status_t SharedRegionParcelable::writeToParcel(Parcel* parcel) const {
- status_t status = AAudioConvert_aaudioToAndroidStatus(validate());
- if (status != NO_ERROR) goto error;
-
- status = parcel->writeInt32(mSizeInBytes);
- if (status != NO_ERROR) goto error;
- if (mSizeInBytes > 0) {
- status = parcel->writeInt32(mSharedMemoryIndex);
- if (status != NO_ERROR) goto error;
- status = parcel->writeInt32(mOffsetInBytes);
- if (status != NO_ERROR) goto error;
- }
- return NO_ERROR;
-
-error:
- ALOGE("%s returning %d", __func__, status);
- return status;
-}
-
-status_t SharedRegionParcelable::readFromParcel(const Parcel* parcel) {
- status_t status = parcel->readInt32(&mSizeInBytes);
- if (status != NO_ERROR) goto error;
- if (mSizeInBytes > 0) {
- status = parcel->readInt32(&mSharedMemoryIndex);
- if (status != NO_ERROR) goto error;
- status = parcel->readInt32(&mOffsetInBytes);
- if (status != NO_ERROR) goto error;
- }
- return AAudioConvert_aaudioToAndroidStatus(validate());
-
-error:
- ALOGE("%s returning %d", __func__, status);
- return status;
-}
-
aaudio_result_t SharedRegionParcelable::resolve(SharedMemoryParcelable *memoryParcels,
void **regionAddressPtr) {
if (mSizeInBytes == 0) {
diff --git a/media/libaaudio/src/binding/SharedRegionParcelable.h b/media/libaaudio/src/binding/SharedRegionParcelable.h
index 0cd8c04..c15fc30 100644
--- a/media/libaaudio/src/binding/SharedRegionParcelable.h
+++ b/media/libaaudio/src/binding/SharedRegionParcelable.h
@@ -20,41 +20,39 @@
#include <stdint.h>
#include <sys/mman.h>
-#include <binder/Parcelable.h>
#include <aaudio/AAudio.h>
+#include <aaudio/SharedRegion.h>
#include "binding/SharedMemoryParcelable.h"
using android::status_t;
-using android::Parcel;
-using android::Parcelable;
namespace aaudio {
-class SharedRegionParcelable : public Parcelable {
+class SharedRegionParcelable {
public:
- SharedRegionParcelable();
- virtual ~SharedRegionParcelable();
+ SharedRegionParcelable() = default;
+
+ // Construct based on a parcelable representation.
+ explicit SharedRegionParcelable(const SharedRegion& parcelable);
void setup(int32_t sharedMemoryIndex, int32_t offsetInBytes, int32_t sizeInBytes);
- virtual status_t writeToParcel(Parcel* parcel) const override;
-
- virtual status_t readFromParcel(const Parcel* parcel) override;
-
aaudio_result_t resolve(SharedMemoryParcelable *memoryParcels, void **regionAddressPtr);
bool isFileDescriptorSafe(SharedMemoryParcelable *memoryParcels);
void dump();
-protected:
+ // Extract a parcelable representation of this object.
+ SharedRegion parcelable() const;
+
+private:
int32_t mSharedMemoryIndex = -1;
int32_t mOffsetInBytes = 0;
int32_t mSizeInBytes = 0;
-private:
aaudio_result_t validate() const;
};
diff --git a/media/libaaudio/src/binding/aidl/aaudio/Endpoint.aidl b/media/libaaudio/src/binding/aidl/aaudio/Endpoint.aidl
new file mode 100644
index 0000000..3600b6a
--- /dev/null
+++ b/media/libaaudio/src/binding/aidl/aaudio/Endpoint.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package aaudio;
+
+import aaudio.RingBuffer;
+import android.media.SharedFileRegion;
+
+parcelable Endpoint {
+ // Set capacityInFrames to zero if Queue is unused.
+ RingBuffer upMessageQueueParcelable; // server to client
+ RingBuffer downMessageQueueParcelable; // to server
+ RingBuffer upDataQueueParcelable; // eg. record, could share same queue
+ RingBuffer downDataQueueParcelable; // eg. playback
+ SharedFileRegion[] sharedMemories;
+}
diff --git a/media/libaaudio/src/binding/aidl/aaudio/IAAudioClient.aidl b/media/libaaudio/src/binding/aidl/aaudio/IAAudioClient.aidl
new file mode 100644
index 0000000..a010dbc
--- /dev/null
+++ b/media/libaaudio/src/binding/aidl/aaudio/IAAudioClient.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package aaudio;
+
+interface IAAudioClient {
+ oneway void onStreamChange(int handle, int opcode, int value);
+}
diff --git a/media/libaaudio/src/binding/aidl/aaudio/IAAudioService.aidl b/media/libaaudio/src/binding/aidl/aaudio/IAAudioService.aidl
new file mode 100644
index 0000000..44d2211
--- /dev/null
+++ b/media/libaaudio/src/binding/aidl/aaudio/IAAudioService.aidl
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package aaudio;
+
+import aaudio.Endpoint;
+import aaudio.IAAudioClient;
+import aaudio.StreamParameters;
+import aaudio.StreamRequest;
+
+interface IAAudioService {
+ /**
+ * Register an object to receive audio input/output change and track notifications.
+ * For a given calling pid, AAudio service disregards any registrations after the first.
+ * Thus the IAAudioClient must be a singleton per process.
+ */
+ void registerClient(IAAudioClient client);
+
+ /**
+ * @param request info needed to create the stream
+ * @param paramsOut contains information about the created stream
+ * @return handle to the stream or a negative error
+ */
+ int openStream(in StreamRequest request,
+ out StreamParameters paramsOut);
+
+ int closeStream(int streamHandle);
+
+ /*
+ * Get an immutable description of the in-memory queues
+ * used to communicate with the underlying HAL or Service.
+ */
+ int getStreamDescription(int streamHandle, out Endpoint endpoint);
+
+ /**
+ * Start the flow of data.
+ * This is asynchronous. When complete, the service will send a STARTED event.
+ */
+ int startStream(int streamHandle);
+
+ /**
+ * Stop the flow of data such that start() can resume without loss of data.
+ * This is asynchronous. When complete, the service will send a PAUSED event.
+ */
+ int pauseStream(int streamHandle);
+
+ /**
+ * Stop the flow of data such that the data currently in the buffer is played.
+ * This is asynchronous. When complete, the service will send a STOPPED event.
+ */
+ int stopStream(int streamHandle);
+
+ /**
+ * Discard any data held by the underlying HAL or Service.
+ * This is asynchronous. When complete, the service will send a FLUSHED event.
+ */
+ int flushStream(int streamHandle);
+
+ /**
+ * Manage the specified thread as a low latency audio thread.
+ */
+ int registerAudioThread(int streamHandle,
+ int clientThreadId,
+ long periodNanoseconds);
+
+ int unregisterAudioThread(int streamHandle,
+ int clientThreadId);
+}
diff --git a/media/libaaudio/src/binding/aidl/aaudio/RingBuffer.aidl b/media/libaaudio/src/binding/aidl/aaudio/RingBuffer.aidl
new file mode 100644
index 0000000..a58b33a
--- /dev/null
+++ b/media/libaaudio/src/binding/aidl/aaudio/RingBuffer.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package aaudio;
+
+import aaudio.SharedRegion;
+
+parcelable RingBuffer {
+ SharedRegion readCounterParcelable;
+ SharedRegion writeCounterParcelable;
+ SharedRegion dataParcelable;
+ int bytesPerFrame; // index is in frames
+ int framesPerBurst; // for ISOCHRONOUS queues
+ int capacityInFrames; // zero if unused
+ int /* RingbufferFlags */ flags; // = RingbufferFlags::NONE;
+}
\ No newline at end of file
diff --git a/media/libaaudio/src/binding/aidl/aaudio/SharedRegion.aidl b/media/libaaudio/src/binding/aidl/aaudio/SharedRegion.aidl
new file mode 100644
index 0000000..26153e8
--- /dev/null
+++ b/media/libaaudio/src/binding/aidl/aaudio/SharedRegion.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package aaudio;
+
+parcelable SharedRegion {
+ int sharedMemoryIndex;
+ int offsetInBytes;
+ int sizeInBytes;
+}
diff --git a/media/libaaudio/src/binding/aidl/aaudio/StreamParameters.aidl b/media/libaaudio/src/binding/aidl/aaudio/StreamParameters.aidl
new file mode 100644
index 0000000..b7c4f70
--- /dev/null
+++ b/media/libaaudio/src/binding/aidl/aaudio/StreamParameters.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package aaudio;
+
+import android.media.audio.common.AudioFormat;
+
+parcelable StreamParameters {
+ int samplesPerFrame; // = AAUDIO_UNSPECIFIED;
+ int sampleRate; // = AAUDIO_UNSPECIFIED;
+ int deviceId; // = AAUDIO_UNSPECIFIED;
+ int /* aaudio_sharing_mode_t */ sharingMode; // = AAUDIO_SHARING_MODE_SHARED;
+ AudioFormat audioFormat; // = AUDIO_FORMAT_DEFAULT;
+ int /* aaudio_direction_t */ direction; // = AAUDIO_DIRECTION_OUTPUT;
+ int /* aaudio_usage_t */ usage; // = AAUDIO_UNSPECIFIED;
+ int /* aaudio_content_type_t */ contentType; // = AAUDIO_UNSPECIFIED;
+ int /* aaudio_input_preset_t */ inputPreset; // = AAUDIO_UNSPECIFIED;
+ int bufferCapacity; // = AAUDIO_UNSPECIFIED;
+ int /* aaudio_allowed_capture_policy_t */ allowedCapturePolicy; // = AAUDIO_UNSPECIFIED;
+ int /* aaudio_session_id_t */ sessionId; // = AAUDIO_SESSION_ID_NONE;
+ boolean isPrivacySensitive; // = false;
+}
diff --git a/media/libaaudio/src/binding/aidl/aaudio/StreamRequest.aidl b/media/libaaudio/src/binding/aidl/aaudio/StreamRequest.aidl
new file mode 100644
index 0000000..12802e6
--- /dev/null
+++ b/media/libaaudio/src/binding/aidl/aaudio/StreamRequest.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package aaudio;
+
+import aaudio.StreamParameters;
+import android.media.permission.Identity;
+
+parcelable StreamRequest {
+ StreamParameters params;
+ Identity identity;
+ boolean sharingModeMatchRequired; // = false;
+ boolean inService; // = false; // Stream opened by AAudioservice
+}
\ No newline at end of file
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.cpp b/media/libaaudio/src/client/AAudioFlowGraph.cpp
index 8f2c488..61b50f3 100644
--- a/media/libaaudio/src/client/AAudioFlowGraph.cpp
+++ b/media/libaaudio/src/client/AAudioFlowGraph.cpp
@@ -26,9 +26,11 @@
#include <flowgraph/SinkFloat.h>
#include <flowgraph/SinkI16.h>
#include <flowgraph/SinkI24.h>
+#include <flowgraph/SinkI32.h>
#include <flowgraph/SourceFloat.h>
#include <flowgraph/SourceI16.h>
#include <flowgraph/SourceI24.h>
+#include <flowgraph/SourceI32.h>
using namespace flowgraph;
@@ -38,7 +40,8 @@
int32_t sinkChannelCount) {
AudioFloatOutputPort *lastOutput = nullptr;
- ALOGV("%s() source format = 0x%08x, channels = %d, sink format = 0x%08x, channels = %d",
+ // TODO change back to ALOGD
+ ALOGI("%s() source format = 0x%08x, channels = %d, sink format = 0x%08x, channels = %d",
__func__, sourceFormat, sourceChannelCount, sinkFormat, sinkChannelCount);
switch (sourceFormat) {
@@ -51,7 +54,10 @@
case AUDIO_FORMAT_PCM_24_BIT_PACKED:
mSource = std::make_unique<SourceI24>(sourceChannelCount);
break;
- default: // TODO add I32
+ case AUDIO_FORMAT_PCM_32_BIT:
+ mSource = std::make_unique<SourceI32>(sourceChannelCount);
+ break;
+ default:
ALOGE("%s() Unsupported source format = %d", __func__, sourceFormat);
return AAUDIO_ERROR_UNIMPLEMENTED;
}
@@ -90,7 +96,10 @@
case AUDIO_FORMAT_PCM_24_BIT_PACKED:
mSink = std::make_unique<SinkI24>(sinkChannelCount);
break;
- default: // TODO add I32
+ case AUDIO_FORMAT_PCM_32_BIT:
+ mSink = std::make_unique<SinkI32>(sinkChannelCount);
+ break;
+ default:
ALOGE("%s() Unsupported sink format = %d", __func__, sinkFormat);
return AAUDIO_ERROR_UNIMPLEMENTED;
}
diff --git a/media/libaaudio/src/client/AudioEndpoint.cpp b/media/libaaudio/src/client/AudioEndpoint.cpp
index 06f66d3..0a19d17 100644
--- a/media/libaaudio/src/client/AudioEndpoint.cpp
+++ b/media/libaaudio/src/client/AudioEndpoint.cpp
@@ -137,7 +137,7 @@
return AAUDIO_ERROR_INTERNAL;
}
- mUpCommandQueue = std::make_unique<FifoBuffer>(
+ mUpCommandQueue = std::make_unique<FifoBufferIndirect>(
descriptor->bytesPerFrame,
descriptor->capacityInFrames,
descriptor->readCounterAddress,
@@ -166,7 +166,7 @@
? &mDataWriteCounter
: descriptor->writeCounterAddress;
- mDataQueue = std::make_unique<FifoBuffer>(
+ mDataQueue = std::make_unique<FifoBufferIndirect>(
descriptor->bytesPerFrame,
descriptor->capacityInFrames,
readCounterAddress,
diff --git a/media/libaaudio/src/client/AudioEndpoint.h b/media/libaaudio/src/client/AudioEndpoint.h
index 484d917..4c8d60f 100644
--- a/media/libaaudio/src/client/AudioEndpoint.h
+++ b/media/libaaudio/src/client/AudioEndpoint.h
@@ -93,8 +93,8 @@
void dump() const;
private:
- std::unique_ptr<android::FifoBuffer> mUpCommandQueue;
- std::unique_ptr<android::FifoBuffer> mDataQueue;
+ std::unique_ptr<android::FifoBufferIndirect> mUpCommandQueue;
+ std::unique_ptr<android::FifoBufferIndirect> mDataQueue;
bool mFreeRunning;
android::fifo_counter_t mDataReadCounter; // only used if free-running
android::fifo_counter_t mDataWriteCounter; // only used if free-running
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 4520823..d8b27c3 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -28,18 +28,17 @@
#include <cutils/properties.h>
#include <media/MediaMetricsItem.h>
-#include <utils/String16.h>
#include <utils/Trace.h>
#include "AudioEndpointParcelable.h"
#include "binding/AAudioStreamRequest.h"
#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"
+#include <media/AidlConversion.h>
#include "AudioStreamInternal.h"
@@ -50,9 +49,9 @@
// This is needed to make sense of the logs more easily.
#define LOG_TAG (mInService ? "AudioStreamInternal_Service" : "AudioStreamInternal_Client")
-using android::String16;
using android::Mutex;
using android::WrappingBuffer;
+using android::media::permission::Identity;
using namespace aaudio;
@@ -76,6 +75,7 @@
}
AudioStreamInternal::~AudioStreamInternal() {
+ ALOGD("%s() %p called", __func__, this);
}
aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) {
@@ -100,16 +100,23 @@
const int32_t burstMinMicros = AAudioProperty_getHardwareBurstMinMicros();
int32_t burstMicros = 0;
+ const audio_format_t requestedFormat = getFormat();
// We have to do volume scaling. So we prefer FLOAT format.
- if (getFormat() == AUDIO_FORMAT_DEFAULT) {
+ if (requestedFormat == AUDIO_FORMAT_DEFAULT) {
setFormat(AUDIO_FORMAT_PCM_FLOAT);
}
- // Request FLOAT for the shared mixer.
+ // Request FLOAT for the shared mixer or the device.
request.getConfiguration().setFormat(AUDIO_FORMAT_PCM_FLOAT);
+ // TODO b/182392769: use identity util
+ Identity identity;
+ identity.uid = VALUE_OR_FATAL(android::legacy2aidl_uid_t_int32_t(getuid()));
+ identity.pid = VALUE_OR_FATAL(android::legacy2aidl_pid_t_int32_t(getpid()));
+ identity.packageName = builder.getOpPackageName();
+ identity.attributionTag = builder.getAttributionTag();
+
// Build the request to send to the server.
- request.setUserId(getuid());
- request.setProcessId(getpid());
+ request.setIdentity(identity);
request.setSharingModeMatchRequired(isSharingModeMatchRequired());
request.setInService(isInService());
@@ -150,6 +157,14 @@
mMetricsId = std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_STREAM)
+ std::to_string(mServiceStreamHandle);
+ android::mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_PERFORMANCEMODE,
+ AudioGlobal_convertPerformanceModeToText(builder.getPerformanceMode()))
+ .set(AMEDIAMETRICS_PROP_SHARINGMODE,
+ AudioGlobal_convertSharingModeToText(builder.getSharingMode()))
+ .set(AMEDIAMETRICS_PROP_ENCODINGCLIENT,
+ android::toString(requestedFormat).c_str()).record();
+
result = configurationOutput.validate();
if (result != AAUDIO_OK) {
goto error;
@@ -211,10 +226,10 @@
result = AAUDIO_ERROR_OUT_OF_RANGE;
goto error;
}
- mFramesPerBurst = framesPerBurst; // only save good value
+ setFramesPerBurst(framesPerBurst); // only save good value
mBufferCapacityInFrames = mEndpointDescriptor.dataQueueDescriptor.capacityInFrames;
- if (mBufferCapacityInFrames < mFramesPerBurst
+ if (mBufferCapacityInFrames < getFramesPerBurst()
|| mBufferCapacityInFrames > MAX_BUFFER_CAPACITY_IN_FRAMES) {
ALOGE("%s - bufferCapacity out of range = %d", __func__, mBufferCapacityInFrames);
result = AAUDIO_ERROR_OUT_OF_RANGE;
@@ -239,7 +254,7 @@
}
if (mCallbackFrames == AAUDIO_UNSPECIFIED) {
- mCallbackFrames = mFramesPerBurst;
+ mCallbackFrames = getFramesPerBurst();
}
const int32_t callbackBufferSize = mCallbackFrames * getBytesPerFrame();
@@ -271,21 +286,21 @@
return result;
error:
- releaseCloseFinal();
+ safeReleaseClose();
return result;
}
// This must be called under mStreamLock.
aaudio_result_t AudioStreamInternal::release_l() {
aaudio_result_t result = AAUDIO_OK;
- ALOGV("%s(): mServiceStreamHandle = 0x%08X", __func__, mServiceStreamHandle);
+ ALOGD("%s(): mServiceStreamHandle = 0x%08X", __func__, mServiceStreamHandle);
if (mServiceStreamHandle != AAUDIO_HANDLE_INVALID) {
aaudio_stream_state_t currentState = getState();
// Don't release a stream while it is running. Stop it first.
// If DISCONNECTED then we should still try to stop in case the
// error callback is still running.
if (isActive() || currentState == AAUDIO_STREAM_STATE_DISCONNECTED) {
- requestStop();
+ requestStop_l();
}
logReleaseBufferState();
@@ -331,7 +346,7 @@
* The processing code will then save the current offset
* between client and server and apply that to any position given to the app.
*/
-aaudio_result_t AudioStreamInternal::requestStart()
+aaudio_result_t AudioStreamInternal::requestStart_l()
{
int64_t startTime;
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
@@ -353,6 +368,8 @@
// Clear any stale timestamps from the previous run.
drainTimestampsFromService();
+ prepareBuffersForStart(); // tell subclasses to get ready
+
aaudio_result_t result = mServiceInterface.startStream(mServiceStreamHandle);
if (result == AAUDIO_ERROR_INVALID_HANDLE) {
ALOGD("%s() INVALID_HANDLE, stream was probably stolen", __func__);
@@ -372,7 +389,7 @@
* AAUDIO_NANOS_PER_SECOND
/ getSampleRate();
mCallbackEnabled.store(true);
- result = createThread(periodNanos, aaudio_callback_thread_proc, this);
+ result = createThread_l(periodNanos, aaudio_callback_thread_proc, this);
}
if (result != AAUDIO_OK) {
setState(originalState);
@@ -398,33 +415,36 @@
}
// This must be called under mStreamLock.
-aaudio_result_t AudioStreamInternal::stopCallback()
+aaudio_result_t AudioStreamInternal::stopCallback_l()
{
if (isDataCallbackSet()
&& (isActive() || getState() == AAUDIO_STREAM_STATE_DISCONNECTED)) {
mCallbackEnabled.store(false);
- aaudio_result_t result = joinThread(NULL); // may temporarily unlock mStreamLock
+ aaudio_result_t result = joinThread_l(NULL); // may temporarily unlock mStreamLock
if (result == AAUDIO_ERROR_INVALID_HANDLE) {
ALOGD("%s() INVALID_HANDLE, stream was probably stolen", __func__);
result = AAUDIO_OK;
}
return result;
} else {
+ ALOGD("%s() skipped, isDataCallbackSet() = %d, isActive() = %d, getState() = %d", __func__,
+ isDataCallbackSet(), isActive(), getState());
return AAUDIO_OK;
}
}
-// This must be called under mStreamLock.
-aaudio_result_t AudioStreamInternal::requestStop() {
- aaudio_result_t result = stopCallback();
+aaudio_result_t AudioStreamInternal::requestStop_l() {
+ aaudio_result_t result = stopCallback_l();
if (result != AAUDIO_OK) {
+ ALOGW("%s() stop callback returned %d, returning early", __func__, result);
return result;
}
// The stream may have been unlocked temporarily to let a callback finish
// and the callback may have stopped the stream.
// Check to make sure the stream still needs to be stopped.
- // See also AudioStream::safeStop().
+ // See also AudioStream::safeStop_l().
if (!(isActive() || getState() == AAUDIO_STREAM_STATE_DISCONNECTED)) {
+ ALOGD("%s() returning early, not active or disconnected", __func__);
return AAUDIO_OK;
}
@@ -755,9 +775,9 @@
aaudio_result_t AudioStreamInternal::setBufferSize(int32_t requestedFrames) {
int32_t adjustedFrames = requestedFrames;
- const int32_t maximumSize = getBufferCapacity() - mFramesPerBurst;
+ const int32_t maximumSize = getBufferCapacity() - getFramesPerBurst();
// Minimum size should be a multiple number of bursts.
- const int32_t minimumSize = 1 * mFramesPerBurst;
+ const int32_t minimumSize = 1 * getFramesPerBurst();
// Clip to minimum size so that rounding up will work better.
adjustedFrames = std::max(minimumSize, adjustedFrames);
@@ -767,9 +787,9 @@
adjustedFrames = maximumSize;
} else {
// Round to the next highest burst size.
- int32_t numBursts = (adjustedFrames + mFramesPerBurst - 1) / mFramesPerBurst;
- adjustedFrames = numBursts * mFramesPerBurst;
- // Clip just in case maximumSize is not a multiple of mFramesPerBurst.
+ int32_t numBursts = (adjustedFrames + getFramesPerBurst() - 1) / getFramesPerBurst();
+ adjustedFrames = numBursts * getFramesPerBurst();
+ // Clip just in case maximumSize is not a multiple of getFramesPerBurst().
adjustedFrames = std::min(maximumSize, adjustedFrames);
}
@@ -804,15 +824,6 @@
return mBufferCapacityInFrames;
}
-int32_t AudioStreamInternal::getFramesPerBurst() const {
- return mFramesPerBurst;
-}
-
-// This must be called under mStreamLock.
-aaudio_result_t AudioStreamInternal::joinThread(void** returnArg) {
- return AudioStream::joinThread(returnArg, calculateReasonableTimeout(getFramesPerBurst()));
-}
-
bool AudioStreamInternal::isClockModelInControl() const {
return isActive() && mAudioEndpoint->isFreeRunning() && mClockModel.isRunning();
}
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 61591b3..fbe4c13 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -20,7 +20,6 @@
#include <stdint.h>
#include <aaudio/AAudio.h>
-#include "binding/IAAudioService.h"
#include "binding/AudioEndpointParcelable.h"
#include "binding/AAudioServiceInterface.h"
#include "client/IsochronousClockModel.h"
@@ -29,7 +28,6 @@
#include "utility/AudioClock.h"
using android::sp;
-using android::IAAudioService;
namespace aaudio {
@@ -46,10 +44,6 @@
AudioStreamInternal(AAudioServiceInterface &serviceInterface, bool inService);
virtual ~AudioStreamInternal();
- aaudio_result_t requestStart() override;
-
- aaudio_result_t requestStop() override;
-
aaudio_result_t getTimestamp(clockid_t clockId,
int64_t *framePosition,
int64_t *timeNanoseconds) override;
@@ -58,16 +52,12 @@
aaudio_result_t open(const AudioStreamBuilder &builder) override;
- aaudio_result_t release_l() override;
-
aaudio_result_t setBufferSize(int32_t requestedFrames) override;
int32_t getBufferSize() const override;
int32_t getBufferCapacity() const override;
- int32_t getFramesPerBurst() const override;
-
int32_t getXRunCount() const override {
return mXRunCount;
}
@@ -76,12 +66,9 @@
aaudio_result_t unregisterThread() override;
- aaudio_result_t joinThread(void** returnArg);
-
// Called internally from 'C'
virtual void *callbackLoop() = 0;
-
bool isMMap() override {
return true;
}
@@ -100,6 +87,10 @@
}
protected:
+ aaudio_result_t requestStart_l() REQUIRES(mStreamLock) override;
+ aaudio_result_t requestStop_l() REQUIRES(mStreamLock) override;
+
+ aaudio_result_t release_l() REQUIRES(mStreamLock) override;
aaudio_result_t processData(void *buffer,
int32_t numFrames,
@@ -121,9 +112,11 @@
aaudio_result_t processCommands();
- aaudio_result_t stopCallback();
+ aaudio_result_t stopCallback_l();
- virtual void advanceClientToMatchServerPosition() = 0;
+ virtual void prepareBuffersForStart() {}
+
+ virtual void advanceClientToMatchServerPosition(int32_t serverMargin = 0) = 0;
virtual void onFlushFromServer() {}
@@ -159,7 +152,6 @@
aaudio_handle_t mServiceStreamHandle; // opaque handle returned from service
- int32_t mFramesPerBurst = MIN_FRAMES_PER_BURST; // frames per HAL transfer
int32_t mXRunCount = 0; // how many underrun events?
// Offset from underlying frame position.
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index fce322b..2da5406 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -18,8 +18,9 @@
#include <utils/Log.h>
#include <algorithm>
-#include <audio_utils/primitives.h>
+#include <audio_utils/format.h>
#include <aaudio/AAudio.h>
+#include <media/MediaMetricsItem.h>
#include "client/AudioStreamInternalCapture.h"
#include "utility/AudioClock.h"
@@ -47,9 +48,9 @@
AudioStreamInternalCapture::~AudioStreamInternalCapture() {}
-void AudioStreamInternalCapture::advanceClientToMatchServerPosition() {
+void AudioStreamInternalCapture::advanceClientToMatchServerPosition(int32_t serverMargin) {
int64_t readCounter = mAudioEndpoint->getDataReadCounter();
- int64_t writeCounter = mAudioEndpoint->getDataWriteCounter();
+ int64_t writeCounter = mAudioEndpoint->getDataWriteCounter() + serverMargin;
// Bump offset so caller does not see the retrograde motion in getFramesRead().
int64_t offset = readCounter - writeCounter;
@@ -149,7 +150,7 @@
// Calculate frame position based off of the readCounter because
// the writeCounter might have just advanced in the background,
// causing us to sleep until a later burst.
- int64_t nextPosition = mAudioEndpoint->getDataReadCounter() + mFramesPerBurst;
+ int64_t nextPosition = mAudioEndpoint->getDataReadCounter() + getFramesPerBurst();
wakeTime = mClockModel.convertPositionToLatestTime(nextPosition);
}
break;
@@ -189,26 +190,10 @@
const audio_format_t sourceFormat = getDeviceFormat();
const audio_format_t destinationFormat = getFormat();
- // TODO factor this out into a utility function
- if (sourceFormat == destinationFormat) {
- memcpy(destination, wrappingBuffer.data[partIndex], numBytes);
- } else if (sourceFormat == AUDIO_FORMAT_PCM_16_BIT
- && destinationFormat == AUDIO_FORMAT_PCM_FLOAT) {
- memcpy_to_float_from_i16(
- (float *) destination,
- (const int16_t *) wrappingBuffer.data[partIndex],
- numSamples);
- } else if (sourceFormat == AUDIO_FORMAT_PCM_FLOAT
- && destinationFormat == AUDIO_FORMAT_PCM_16_BIT) {
- memcpy_to_i16_from_float(
- (int16_t *) destination,
- (const float *) wrappingBuffer.data[partIndex],
- numSamples);
- } else {
- ALOGE("%s() - Format conversion not supported! audio_format_t source = %u, dest = %u",
- __func__, sourceFormat, destinationFormat);
- return AAUDIO_ERROR_INVALID_FORMAT;
- }
+
+ memcpy_by_audio_format(destination, destinationFormat,
+ wrappingBuffer.data[partIndex], sourceFormat, numSamples);
+
destination += numBytes;
framesLeft -= framesToProcess;
}
@@ -268,7 +253,7 @@
if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {
ALOGD("%s(): callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__);
- result = systemStopFromCallback();
+ result = systemStopInternal();
break;
}
}
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.h b/media/libaaudio/src/client/AudioStreamInternalCapture.h
index 6436a53..251a7f2 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.h
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.h
@@ -23,7 +23,6 @@
#include "client/AudioStreamInternal.h"
using android::sp;
-using android::IAAudioService;
namespace aaudio {
@@ -46,7 +45,7 @@
}
protected:
- void advanceClientToMatchServerPosition() override;
+ void advanceClientToMatchServerPosition(int32_t serverOffset = 0) override;
/**
* Low level data processing that will not block. It will just read or write as much as it can.
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index d6b73b4..71bde90 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -19,6 +19,7 @@
#define ATRACE_TAG ATRACE_TAG_AUDIO
+#include <media/MediaMetricsItem.h>
#include <utils/Trace.h>
#include "client/AudioStreamInternalPlay.h"
@@ -32,6 +33,7 @@
#define LOG_TAG (mInService ? "AudioStreamInternalPlay_Service" \
: "AudioStreamInternalPlay_Client")
+using android::status_t;
using android::WrappingBuffer;
using namespace aaudio;
@@ -55,7 +57,7 @@
getDeviceChannelCount());
if (result != AAUDIO_OK) {
- releaseCloseFinal();
+ safeReleaseClose();
}
// Sample rate is constrained to common values by now and should not overflow.
int32_t numFrames = kRampMSec * getSampleRate() / AAUDIO_MILLIS_PER_SECOND;
@@ -65,9 +67,9 @@
}
// This must be called under mStreamLock.
-aaudio_result_t AudioStreamInternalPlay::requestPause()
+aaudio_result_t AudioStreamInternalPlay::requestPause_l()
{
- aaudio_result_t result = stopCallback();
+ aaudio_result_t result = stopCallback_l();
if (result != AAUDIO_OK) {
return result;
}
@@ -82,7 +84,7 @@
return mServiceInterface.pauseStream(mServiceStreamHandle);
}
-aaudio_result_t AudioStreamInternalPlay::requestFlush() {
+aaudio_result_t AudioStreamInternalPlay::requestFlush_l() {
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
ALOGW("%s() mServiceStreamHandle invalid", __func__);
return AAUDIO_ERROR_INVALID_STATE;
@@ -92,8 +94,13 @@
return mServiceInterface.flushStream(mServiceStreamHandle);
}
-void AudioStreamInternalPlay::advanceClientToMatchServerPosition() {
- int64_t readCounter = mAudioEndpoint->getDataReadCounter();
+void AudioStreamInternalPlay::prepareBuffersForStart() {
+ // Prevent stale data from being played.
+ mAudioEndpoint->eraseDataMemory();
+}
+
+void AudioStreamInternalPlay::advanceClientToMatchServerPosition(int32_t serverMargin) {
+ int64_t readCounter = mAudioEndpoint->getDataReadCounter() + serverMargin;
int64_t writeCounter = mAudioEndpoint->getDataWriteCounter();
// Bump offset so caller does not see the retrograde motion in getFramesRead().
@@ -151,7 +158,9 @@
if (mNeedCatchUp.isRequested()) {
// Catch an MMAP pointer that is already advancing.
// This will avoid initial underruns caused by a slow cold start.
- advanceClientToMatchServerPosition();
+ // We add a one burst margin in case the DSP advances before we can write the data.
+ // This can help prevent the beginning of the stream from being skipped.
+ advanceClientToMatchServerPosition(getFramesPerBurst());
mNeedCatchUp.acknowledge();
}
@@ -293,7 +302,7 @@
}
} else if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {
ALOGD("%s(): callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__);
- result = systemStopFromCallback();
+ result = systemStopInternal();
break;
}
}
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.h b/media/libaaudio/src/client/AudioStreamInternalPlay.h
index 2e93157..03c957d 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.h
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.h
@@ -25,7 +25,6 @@
#include "client/AudioStreamInternal.h"
using android::sp;
-using android::IAAudioService;
namespace aaudio {
@@ -36,9 +35,9 @@
aaudio_result_t open(const AudioStreamBuilder &builder) override;
- aaudio_result_t requestPause() override;
+ aaudio_result_t requestPause_l() override;
- aaudio_result_t requestFlush() override;
+ aaudio_result_t requestFlush_l() override;
bool isFlushSupported() const override {
// Only implement FLUSH for OUTPUT streams.
@@ -65,7 +64,9 @@
protected:
- void advanceClientToMatchServerPosition() override;
+ void prepareBuffersForStart() override;
+
+ void advanceClientToMatchServerPosition(int32_t serverMargin = 0) override;
void onFlushFromServer() override;
diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index cfa7221..d103aca 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -87,6 +87,30 @@
streamBuilder->setDeviceId(deviceId);
}
+AAUDIO_API void AAudioStreamBuilder_setPackageName(AAudioStreamBuilder* builder,
+ const char* packageName)
+{
+ AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
+ std::optional<std::string> optionalPackageName;
+ if (packageName != nullptr) {
+ optionalPackageName = std::string(packageName);
+ }
+ // Only system apps can read the op package name. For regular apps the
+ // regular package name is a sufficient replacement
+ streamBuilder->setOpPackageName(optionalPackageName);
+}
+
+AAUDIO_API void AAudioStreamBuilder_setAttributionTag(AAudioStreamBuilder* builder,
+ const char* attributionTag)
+{
+ AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
+ std::optional<std::string> optionalAttrTag;
+ if (attributionTag != nullptr) {
+ optionalAttrTag = std::string(attributionTag);
+ }
+ streamBuilder->setAttributionTag(optionalAttrTag);
+}
+
AAUDIO_API void AAudioStreamBuilder_setSampleRate(AAudioStreamBuilder* builder,
int32_t sampleRate)
{
@@ -209,7 +233,6 @@
AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(streamPtr);
aaudio_result_t result = streamBuilder->build(&audioStream);
if (result == AAUDIO_OK) {
- audioStream->registerPlayerBase();
*streamPtr = (AAudioStream*) audioStream;
id = audioStream->getId();
} else {
@@ -348,7 +371,8 @@
// Don't allow writes when playing with a callback.
if (audioStream->isDataCallbackActive()) {
- ALOGD("Cannot write to a callback stream when running.");
+ // A developer requested this warning because it would have saved lots of debugging.
+ ALOGW("%s() - Cannot write to a callback stream when running.", __func__);
return AAUDIO_ERROR_INVALID_STATE;
}
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp
index 5f45261..0d60120 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.cpp
+++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp
@@ -48,13 +48,17 @@
mInputPreset = other.mInputPreset;
mAllowedCapturePolicy = other.mAllowedCapturePolicy;
mIsPrivacySensitive = other.mIsPrivacySensitive;
+ mOpPackageName = other.mOpPackageName;
+ mAttributionTag = other.mAttributionTag;
}
static aaudio_result_t isFormatValid(audio_format_t format) {
switch (format) {
case AUDIO_FORMAT_DEFAULT:
case AUDIO_FORMAT_PCM_16_BIT:
+ case AUDIO_FORMAT_PCM_32_BIT:
case AUDIO_FORMAT_PCM_FLOAT:
+ case AUDIO_FORMAT_PCM_24_BIT_PACKED:
break; // valid
default:
ALOGD("audioFormat not valid, audio_format_t = 0x%08x", format);
@@ -201,4 +205,8 @@
ALOGD("mInputPreset = %6d", mInputPreset);
ALOGD("mAllowedCapturePolicy = %6d", mAllowedCapturePolicy);
ALOGD("mIsPrivacySensitive = %s", mIsPrivacySensitive ? "true" : "false");
+ ALOGD("mOpPackageName = %s", !mOpPackageName.has_value() ?
+ "(null)" : mOpPackageName.value().c_str());
+ ALOGD("mAttributionTag = %s", !mAttributionTag.has_value() ?
+ "(null)" : mAttributionTag.value().c_str());
}
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.h b/media/libaaudio/src/core/AAudioStreamParameters.h
index 3e65b37..5737052 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.h
+++ b/media/libaaudio/src/core/AAudioStreamParameters.h
@@ -136,6 +136,23 @@
mIsPrivacySensitive = privacySensitive;
}
+ const std::optional<std::string> getOpPackageName() const {
+ return mOpPackageName;
+ }
+
+ // TODO b/182392769: reexamine if Identity can be used
+ void setOpPackageName(const std::optional<std::string> opPackageName) {
+ mOpPackageName = opPackageName;
+ }
+
+ const std::optional<std::string> getAttributionTag() const {
+ return mAttributionTag;
+ }
+
+ void setAttributionTag(const std::optional<std::string> attributionTag) {
+ mAttributionTag = attributionTag;
+ }
+
/**
* @return bytes per frame of getFormat()
*/
@@ -167,6 +184,8 @@
aaudio_allowed_capture_policy_t mAllowedCapturePolicy = AAUDIO_UNSPECIFIED;
aaudio_session_id_t mSessionId = AAUDIO_SESSION_ID_NONE;
bool mIsPrivacySensitive = false;
+ std::optional<std::string> mOpPackageName = {};
+ std::optional<std::string> mAttributionTag = {};
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/core/AudioGlobal.cpp b/media/libaaudio/src/core/AudioGlobal.cpp
index 7f5d8d5..0e5b8be 100644
--- a/media/libaaudio/src/core/AudioGlobal.cpp
+++ b/media/libaaudio/src/core/AudioGlobal.cpp
@@ -80,6 +80,8 @@
AAUDIO_CASE_ENUM(AAUDIO_FORMAT_INVALID);
AAUDIO_CASE_ENUM(AAUDIO_FORMAT_PCM_I16);
AAUDIO_CASE_ENUM(AAUDIO_FORMAT_PCM_FLOAT);
+ AAUDIO_CASE_ENUM(AAUDIO_FORMAT_PCM_I24_PACKED);
+ AAUDIO_CASE_ENUM(AAUDIO_FORMAT_PCM_I32);
}
return "Unrecognized";
}
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index f439310..1ed240a 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -42,16 +42,20 @@
: mPlayerBase(new MyPlayerBase())
, mStreamId(AAudio_getNextStreamId())
{
- // mThread is a pthread_t of unknown size so we need memset.
- memset(&mThread, 0, sizeof(mThread));
setPeriodNanoseconds(0);
}
AudioStream::~AudioStream() {
- // Please preserve this log because there have been several bugs related to
+ // Please preserve these logs because there have been several bugs related to
// AudioStream deletion and late callbacks.
ALOGD("%s(s#%u) mPlayerBase strongCount = %d",
__func__, getId(), mPlayerBase->getStrongCount());
+
+ ALOGE_IF(pthread_equal(pthread_self(), mThread),
+ "%s() destructor running in callback", __func__);
+
+ ALOGE_IF(mHasThread, "%s() callback thread never join()ed", __func__);
+
// If the stream is deleted when OPEN or in use then audio resources will leak.
// This would indicate an internal error. So we want to find this ASAP.
LOG_ALWAYS_FATAL_IF(!(getState() == AAUDIO_STREAM_STATE_CLOSED
@@ -107,14 +111,23 @@
return AAUDIO_OK;
}
-void AudioStream::logOpen() {
+void AudioStream::logOpenActual() {
if (mMetricsId.size() > 0) {
- android::mediametrics::LogItem(mMetricsId)
- .set(AMEDIAMETRICS_PROP_PERFORMANCEMODE,
- AudioGlobal_convertPerformanceModeToText(getPerformanceMode()))
- .set(AMEDIAMETRICS_PROP_SHARINGMODE,
- AudioGlobal_convertSharingModeToText(getSharingMode()))
- .record();
+ android::mediametrics::LogItem item(mMetricsId);
+ item.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_OPEN)
+ .set(AMEDIAMETRICS_PROP_PERFORMANCEMODEACTUAL,
+ AudioGlobal_convertPerformanceModeToText(getPerformanceMode()))
+ .set(AMEDIAMETRICS_PROP_SHARINGMODEACTUAL,
+ AudioGlobal_convertSharingModeToText(getSharingMode()))
+ .set(AMEDIAMETRICS_PROP_BUFFERCAPACITYFRAMES, getBufferCapacity())
+ .set(AMEDIAMETRICS_PROP_BURSTFRAMES, getFramesPerBurst())
+ .set(AMEDIAMETRICS_PROP_DIRECTION,
+ AudioGlobal_convertDirectionToText(getDirection()));
+
+ if (getDirection() == AAUDIO_DIRECTION_OUTPUT) {
+ item.set(AMEDIAMETRICS_PROP_PLAYERIID, mPlayerBase->getPlayerIId());
+ }
+ item.record();
}
}
@@ -129,13 +142,13 @@
}
aaudio_result_t AudioStream::systemStart() {
- std::lock_guard<std::mutex> lock(mStreamLock);
-
if (collidesWithCallback()) {
ALOGE("%s cannot be called from a callback!", __func__);
return AAUDIO_ERROR_INVALID_STATE;
}
+ std::lock_guard<std::mutex> lock(mStreamLock);
+
switch (getState()) {
// Is this a good time to start?
case AAUDIO_STREAM_STATE_OPEN:
@@ -164,16 +177,15 @@
return AAUDIO_ERROR_INVALID_STATE;
}
- aaudio_result_t result = requestStart();
+ aaudio_result_t result = requestStart_l();
if (result == AAUDIO_OK) {
// We only call this for logging in "dumpsys audio". So ignore return code.
- (void) mPlayerBase->start();
+ (void) mPlayerBase->startWithStatus(getDeviceId());
}
return result;
}
aaudio_result_t AudioStream::systemPause() {
- std::lock_guard<std::mutex> lock(mStreamLock);
if (!isPauseSupported()) {
return AAUDIO_ERROR_UNIMPLEMENTED;
@@ -184,6 +196,7 @@
return AAUDIO_ERROR_INVALID_STATE;
}
+ std::lock_guard<std::mutex> lock(mStreamLock);
switch (getState()) {
// Proceed with pausing.
case AAUDIO_STREAM_STATE_STARTING:
@@ -214,10 +227,10 @@
return AAUDIO_ERROR_INVALID_STATE;
}
- aaudio_result_t result = requestPause();
+ aaudio_result_t result = requestPause_l();
if (result == AAUDIO_OK) {
// We only call this for logging in "dumpsys audio". So ignore return code.
- (void) mPlayerBase->pause();
+ (void) mPlayerBase->pauseWithStatus();
}
return result;
}
@@ -228,46 +241,40 @@
return AAUDIO_ERROR_UNIMPLEMENTED;
}
- std::lock_guard<std::mutex> lock(mStreamLock);
if (collidesWithCallback()) {
ALOGE("stream cannot be flushed from a callback!");
return AAUDIO_ERROR_INVALID_STATE;
}
+ std::lock_guard<std::mutex> lock(mStreamLock);
aaudio_result_t result = AAudio_isFlushAllowed(getState());
if (result != AAUDIO_OK) {
return result;
}
- return requestFlush();
+ return requestFlush_l();
}
-aaudio_result_t AudioStream::systemStopFromCallback() {
+aaudio_result_t AudioStream::systemStopInternal() {
std::lock_guard<std::mutex> lock(mStreamLock);
- aaudio_result_t result = safeStop();
+ aaudio_result_t result = safeStop_l();
if (result == AAUDIO_OK) {
// We only call this for logging in "dumpsys audio". So ignore return code.
- (void) mPlayerBase->stop();
+ (void) mPlayerBase->stopWithStatus();
}
return result;
}
aaudio_result_t AudioStream::systemStopFromApp() {
- std::lock_guard<std::mutex> lock(mStreamLock);
+ // This check can and should be done outside the lock.
if (collidesWithCallback()) {
ALOGE("stream cannot be stopped by calling from a callback!");
return AAUDIO_ERROR_INVALID_STATE;
}
- aaudio_result_t result = safeStop();
- if (result == AAUDIO_OK) {
- // We only call this for logging in "dumpsys audio". So ignore return code.
- (void) mPlayerBase->stop();
- }
- return result;
+ return systemStopInternal();
}
-// This must be called under mStreamLock.
-aaudio_result_t AudioStream::safeStop() {
+aaudio_result_t AudioStream::safeStop_l() {
switch (getState()) {
// Proceed with stopping.
@@ -299,16 +306,16 @@
return AAUDIO_ERROR_INVALID_STATE;
}
- return requestStop();
+ return requestStop_l();
}
aaudio_result_t AudioStream::safeRelease() {
- // This get temporarily unlocked in the MMAP release() when joining callback threads.
- std::lock_guard<std::mutex> lock(mStreamLock);
if (collidesWithCallback()) {
ALOGE("%s cannot be called from a callback!", __func__);
return AAUDIO_ERROR_INVALID_STATE;
}
+ // This may get temporarily unlocked in the MMAP release() when joining callback threads.
+ std::lock_guard<std::mutex> lock(mStreamLock);
if (getState() == AAUDIO_STREAM_STATE_CLOSING) { // already released?
return AAUDIO_OK;
}
@@ -316,20 +323,44 @@
}
aaudio_result_t AudioStream::safeReleaseClose() {
- // This get temporarily unlocked in the MMAP release() when joining callback threads.
- std::lock_guard<std::mutex> lock(mStreamLock);
if (collidesWithCallback()) {
ALOGE("%s cannot be called from a callback!", __func__);
return AAUDIO_ERROR_INVALID_STATE;
}
- releaseCloseFinal();
+ return safeReleaseCloseInternal();
+}
+
+aaudio_result_t AudioStream::safeReleaseCloseInternal() {
+ // This get temporarily unlocked in the MMAP release() when joining callback threads.
+ std::lock_guard<std::mutex> lock(mStreamLock);
+ releaseCloseFinal_l();
return AAUDIO_OK;
}
+void AudioStream::close_l() {
+ // Releasing the stream will set the state to CLOSING.
+ assert(getState() == AAUDIO_STREAM_STATE_CLOSING);
+ // setState() prevents a transition from CLOSING to any state other than CLOSED.
+ // State is checked by destructor.
+ setState(AAUDIO_STREAM_STATE_CLOSED);
+
+ if (!mMetricsId.empty()) {
+ android::mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_FRAMESTRANSFERRED,
+ getDirection() == AAUDIO_DIRECTION_INPUT ? getFramesWritten()
+ : getFramesRead())
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAAUDIOSTREAM)
+ .record();
+ }
+}
+
void AudioStream::setState(aaudio_stream_state_t state) {
ALOGD("%s(s#%d) from %d to %d", __func__, getId(), mState, state);
+ if (state == mState) {
+ return; // no change
+ }
// Track transition to DISCONNECTED state.
- if (state == AAUDIO_STREAM_STATE_DISCONNECTED && mState != state) {
+ if (state == AAUDIO_STREAM_STATE_DISCONNECTED) {
android::mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DISCONNECT)
.set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState()))
@@ -337,18 +368,18 @@
}
// CLOSED is a final state
if (mState == AAUDIO_STREAM_STATE_CLOSED) {
- ALOGE("%s(%d) tried to set to %d but already CLOSED", __func__, getId(), state);
+ ALOGW("%s(%d) tried to set to %d but already CLOSED", __func__, getId(), state);
// Once CLOSING, we can only move to CLOSED state.
} else if (mState == AAUDIO_STREAM_STATE_CLOSING
&& state != AAUDIO_STREAM_STATE_CLOSED) {
- ALOGE("%s(%d) tried to set to %d but already CLOSING", __func__, getId(), state);
+ ALOGW("%s(%d) tried to set to %d but already CLOSING", __func__, getId(), state);
// Once DISCONNECTED, we can only move to CLOSING or CLOSED state.
} else if (mState == AAUDIO_STREAM_STATE_DISCONNECTED
&& !(state == AAUDIO_STREAM_STATE_CLOSING
|| state == AAUDIO_STREAM_STATE_CLOSED)) {
- ALOGE("%s(%d) tried to set to %d but already DISCONNECTED", __func__, getId(), state);
+ ALOGW("%s(%d) tried to set to %d but already DISCONNECTED", __func__, getId(), state);
} else {
mState = state;
@@ -400,23 +431,28 @@
return procResult;
}
-// This is the entry point for the new thread created by createThread().
+
+// This is the entry point for the new thread created by createThread_l().
// It converts the 'C' function call to a C++ method call.
static void* AudioStream_internalThreadProc(void* threadArg) {
AudioStream *audioStream = (AudioStream *) threadArg;
- // Use an sp<> to prevent the stream from being deleted while running.
+ // Prevent the stream from being deleted while being used.
+ // This is just for extra safety. It is probably not needed because
+ // this callback should be joined before the stream is closed.
android::sp<AudioStream> protectedStream(audioStream);
+ // Balance the incStrong() in createThread_l().
+ protectedStream->decStrong(nullptr);
return protectedStream->wrapUserThread();
}
// This is not exposed in the API.
// But it is still used internally to implement callbacks for MMAP mode.
-aaudio_result_t AudioStream::createThread(int64_t periodNanoseconds,
- aaudio_audio_thread_proc_t threadProc,
- void* threadArg)
+aaudio_result_t AudioStream::createThread_l(int64_t periodNanoseconds,
+ aaudio_audio_thread_proc_t threadProc,
+ void* threadArg)
{
if (mHasThread) {
- ALOGE("createThread() - mHasThread already true");
+ ALOGE("%s() - mHasThread already true", __func__);
return AAUDIO_ERROR_INVALID_STATE;
}
if (threadProc == nullptr) {
@@ -426,10 +462,14 @@
mThreadProc = threadProc;
mThreadArg = threadArg;
setPeriodNanoseconds(periodNanoseconds);
+ // Prevent this object from getting deleted before the thread has a chance to create
+ // its strong pointer. Assume the thread will call decStrong().
+ this->incStrong(nullptr);
int err = pthread_create(&mThread, nullptr, AudioStream_internalThreadProc, this);
if (err != 0) {
android::status_t status = -errno;
- ALOGE("createThread() - pthread_create() failed, %d", status);
+ ALOGE("%s() - pthread_create() failed, %d", __func__, status);
+ this->decStrong(nullptr); // Because the thread won't do it.
return AAudioConvert_androidToAAudioResult(status);
} else {
// TODO Use AAudioThread or maybe AndroidThread
@@ -449,36 +489,39 @@
}
}
+aaudio_result_t AudioStream::joinThread(void** returnArg) {
+ // This may get temporarily unlocked in the MMAP release() when joining callback threads.
+ std::lock_guard<std::mutex> lock(mStreamLock);
+ return joinThread_l(returnArg);
+}
+
// This must be called under mStreamLock.
-aaudio_result_t AudioStream::joinThread(void** returnArg, int64_t timeoutNanoseconds __unused)
-{
+aaudio_result_t AudioStream::joinThread_l(void** returnArg) {
if (!mHasThread) {
- ALOGE("joinThread() - but has no thread");
+ ALOGD("joinThread() - but has no thread");
return AAUDIO_ERROR_INVALID_STATE;
}
aaudio_result_t result = AAUDIO_OK;
// If the callback is stopping the stream because the app passed back STOP
// then we don't need to join(). The thread is already about to exit.
- if (pthread_self() != mThread) {
+ if (!pthread_equal(pthread_self(), mThread)) {
// Called from an app thread. Not the callback.
// Unlock because the callback may be trying to stop the stream but is blocked.
mStreamLock.unlock();
-#if 0
- // TODO implement equivalent of pthread_timedjoin_np()
- struct timespec abstime;
- int err = pthread_timedjoin_np(mThread, returnArg, &abstime);
-#else
int err = pthread_join(mThread, returnArg);
-#endif
mStreamLock.lock();
if (err) {
ALOGE("%s() pthread_join() returns err = %d", __func__, err);
result = AAudioConvert_androidToAAudioResult(-err);
+ } else {
+ ALOGD("%s() pthread_join succeeded", __func__);
+ // This must be set false so that the callback thread can be created
+ // when the stream is restarted.
+ mHasThread = false;
}
+ } else {
+ ALOGD("%s() pthread_join() called on itself!", __func__);
}
- // This must be set false so that the callback thread can be created
- // when the stream is restarted.
- mHasThread = false;
return (result != AAUDIO_OK) ? result : mThreadRegistrationResult;
}
@@ -560,7 +603,8 @@
std::lock_guard<std::mutex> lock(mParentLock);
mParent = parent;
if (!mRegistered) {
- init(android::PLAYER_TYPE_AAUDIO, AAudioConvert_usageToInternal(parent->getUsage()));
+ init(android::PLAYER_TYPE_AAUDIO, AAudioConvert_usageToInternal(parent->getUsage()),
+ (audio_session_t)parent->getSessionId());
mRegistered = true;
}
}
@@ -581,7 +625,7 @@
}
if (audioStream) {
// No pan and only left volume is taken into account from IPLayer interface
- audioStream->setDuckAndMuteVolume(mVolumeMultiplierL /* * mPanMultiplierL */);
+ audioStream->setDuckAndMuteVolume(mVolumeMultiplierL /* mPanMultiplierL */);
}
return android::NO_ERROR;
}
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index e0bd9d8..2b45ed3 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -20,11 +20,13 @@
#include <atomic>
#include <mutex>
#include <stdint.h>
-#include <aaudio/AAudio.h>
+
+#include <android-base/thread_annotations.h>
#include <binder/IServiceManager.h>
#include <binder/Status.h>
#include <utils/StrongPointer.h>
+#include <aaudio/AAudio.h>
#include <media/AudioSystem.h>
#include <media/PlayerBase.h>
#include <media/VolumeShaper.h>
@@ -57,11 +59,6 @@
protected:
- /* Asynchronous requests.
- * Use waitForStateChange() to wait for completion.
- */
- virtual aaudio_result_t requestStart() = 0;
-
/**
* Check the state to see if Pause is currently legal.
*
@@ -80,18 +77,22 @@
return false;
}
- virtual aaudio_result_t requestPause()
- {
+ /* Asynchronous requests.
+ * Use waitForStateChange() to wait for completion.
+ */
+ virtual aaudio_result_t requestStart_l() REQUIRES(mStreamLock) = 0;
+
+ virtual aaudio_result_t requestPause_l() REQUIRES(mStreamLock) {
// Only implement this for OUTPUT streams.
return AAUDIO_ERROR_UNIMPLEMENTED;
}
- virtual aaudio_result_t requestFlush() {
+ virtual aaudio_result_t requestFlush_l() REQUIRES(mStreamLock) {
// Only implement this for OUTPUT streams.
return AAUDIO_ERROR_UNIMPLEMENTED;
}
- virtual aaudio_result_t requestStop() = 0;
+ virtual aaudio_result_t requestStop_l() REQUIRES(mStreamLock) = 0;
public:
virtual aaudio_result_t getTimestamp(clockid_t clockId,
@@ -117,7 +118,7 @@
virtual aaudio_result_t open(const AudioStreamBuilder& builder);
// log to MediaMetrics
- virtual void logOpen();
+ virtual void logOpenActual();
void logReleaseBufferState();
/* Note about naming for "release" and "close" related methods.
@@ -131,11 +132,12 @@
* The AAudioStream_close() method releases if needed and then closes.
*/
+protected:
/**
* Free any hardware or system resources from the open() call.
* It is safe to call release_l() multiple times.
*/
- virtual aaudio_result_t release_l() {
+ virtual aaudio_result_t release_l() REQUIRES(mStreamLock) {
setState(AAUDIO_STREAM_STATE_CLOSING);
return AAUDIO_OK;
}
@@ -144,25 +146,9 @@
* Free any resources not already freed by release_l().
* Assume release_l() already called.
*/
- virtual void close_l() {
- // Releasing the stream will set the state to CLOSING.
- assert(getState() == AAUDIO_STREAM_STATE_CLOSING);
- // setState() prevents a transition from CLOSING to any state other than CLOSED.
- // State is checked by destructor.
- setState(AAUDIO_STREAM_STATE_CLOSED);
- }
+ virtual void close_l() REQUIRES(mStreamLock);
- /**
- * Release then close the stream.
- */
- void releaseCloseFinal() {
- if (getState() != AAUDIO_STREAM_STATE_CLOSING) { // not already released?
- // Ignore result and keep closing.
- (void) release_l();
- }
- close_l();
- }
-
+public:
// This is only used to identify a stream in the logs without
// revealing any pointers.
aaudio_stream_id_t getId() {
@@ -171,11 +157,11 @@
virtual aaudio_result_t setBufferSize(int32_t requestedFrames) = 0;
- virtual aaudio_result_t createThread(int64_t periodNanoseconds,
- aaudio_audio_thread_proc_t threadProc,
- void *threadArg);
+ virtual aaudio_result_t createThread_l(int64_t periodNanoseconds,
+ aaudio_audio_thread_proc_t threadProc,
+ void *threadArg);
- aaudio_result_t joinThread(void **returnArg, int64_t timeoutNanoseconds);
+ aaudio_result_t joinThread(void **returnArg);
virtual aaudio_result_t registerThread() {
return AAUDIO_OK;
@@ -202,11 +188,11 @@
}
virtual int32_t getBufferCapacity() const {
- return AAUDIO_ERROR_UNIMPLEMENTED;
+ return mBufferCapacity;
}
virtual int32_t getFramesPerBurst() const {
- return AAUDIO_ERROR_UNIMPLEMENTED;
+ return mFramesPerBurst;
}
virtual int32_t getXRunCount() const {
@@ -416,7 +402,7 @@
/**
* This is called internally when an app callback returns AAUDIO_CALLBACK_RESULT_STOP.
*/
- aaudio_result_t systemStopFromCallback();
+ aaudio_result_t systemStopInternal();
/**
* Safely RELEASE a stream after taking mStreamLock and checking
@@ -432,6 +418,8 @@
*/
aaudio_result_t safeReleaseClose();
+ aaudio_result_t safeReleaseCloseInternal();
+
protected:
// PlayerBase allows the system to control the stream volume.
@@ -481,11 +469,15 @@
return mResult;
}
+ // Returns the playerIId if registered, -1 otherwise.
+ int32_t getPlayerIId() const {
+ return mPIId;
+ }
+
private:
// Use a weak pointer so the AudioStream can be deleted.
-
std::mutex mParentLock;
- android::wp<AudioStream> mParent;
+ android::wp<AudioStream> mParent GUARDED_BY(mParentLock);
aaudio_result_t mResult = AAUDIO_OK;
bool mRegistered = false;
};
@@ -498,30 +490,32 @@
mSampleRate = sampleRate;
}
- /**
- * This should not be called after the open() call.
- */
+ // This should not be called after the open() call.
void setSamplesPerFrame(int32_t samplesPerFrame) {
mSamplesPerFrame = samplesPerFrame;
}
- /**
- * This should not be called after the open() call.
- */
+ // This should not be called after the open() call.
+ void setFramesPerBurst(int32_t framesPerBurst) {
+ mFramesPerBurst = framesPerBurst;
+ }
+
+ // This should not be called after the open() call.
+ void setBufferCapacity(int32_t bufferCapacity) {
+ mBufferCapacity = bufferCapacity;
+ }
+
+ // This should not be called after the open() call.
void setSharingMode(aaudio_sharing_mode_t sharingMode) {
mSharingMode = sharingMode;
}
- /**
- * This should not be called after the open() call.
- */
+ // This should not be called after the open() call.
void setFormat(audio_format_t format) {
mFormat = format;
}
- /**
- * This should not be called after the open() call.
- */
+ // This should not be called after the open() call.
void setDeviceFormat(audio_format_t format) {
mDeviceFormat = format;
}
@@ -536,10 +530,13 @@
mDeviceId = deviceId;
}
+ // This should not be called after the open() call.
void setSessionId(int32_t sessionId) {
mSessionId = sessionId;
}
+ aaudio_result_t joinThread_l(void **returnArg) REQUIRES(mStreamLock);
+
std::atomic<bool> mCallbackEnabled{false};
float mDuckAndMuteVolume = 1.0f;
@@ -606,11 +603,22 @@
std::string mMetricsId; // set once during open()
+ std::mutex mStreamLock;
+
private:
- aaudio_result_t safeStop();
+ aaudio_result_t safeStop_l() REQUIRES(mStreamLock);
- std::mutex mStreamLock;
+ /**
+ * Release then close the stream.
+ */
+ void releaseCloseFinal_l() REQUIRES(mStreamLock) {
+ if (getState() != AAUDIO_STREAM_STATE_CLOSING) { // not already released?
+ // Ignore result and keep closing.
+ (void) release_l();
+ }
+ close_l();
+ }
const android::sp<MyPlayerBase> mPlayerBase;
@@ -623,6 +631,8 @@
audio_format_t mFormat = AUDIO_FORMAT_DEFAULT;
aaudio_stream_state_t mState = AAUDIO_STREAM_STATE_UNINITIALIZED;
aaudio_performance_mode_t mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
+ int32_t mFramesPerBurst = 0;
+ int32_t mBufferCapacity = 0;
aaudio_usage_t mUsage = AAUDIO_UNSPECIFIED;
aaudio_content_type_t mContentType = AAUDIO_UNSPECIFIED;
@@ -648,8 +658,8 @@
std::atomic<pid_t> mErrorCallbackThread{CALLBACK_THREAD_NONE};
// background thread ----------------------------------
- bool mHasThread = false;
- pthread_t mThread; // initialized in constructor
+ bool mHasThread GUARDED_BY(mStreamLock) = false;
+ pthread_t mThread GUARDED_BY(mStreamLock) = {};
// These are set by the application thread and then read by the audio pthread.
std::atomic<int64_t> mPeriodNanoseconds; // for tuning SCHED_FIFO threads
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index 630b289..2135c54 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -188,7 +188,8 @@
}
}
if (result == AAUDIO_OK) {
- audioStream->logOpen();
+ audioStream->registerPlayerBase();
+ audioStream->logOpenActual();
*streamPtr = startUsingStream(audioStream);
} // else audioStream will go out of scope and be deleted
}
@@ -281,4 +282,8 @@
ALOGI("usage = %6d, contentType = %d, inputPreset = %d, allowedCapturePolicy = %d",
getUsage(), getContentType(), getInputPreset(), getAllowedCapturePolicy());
ALOGI("privacy sensitive = %s", isPrivacySensitive() ? "true" : "false");
+ ALOGI("opPackageName = %s", !getOpPackageName().has_value() ?
+ "(null)" : getOpPackageName().value().c_str());
+ ALOGI("attributionTag = %s", !getAttributionTag().has_value() ?
+ "(null)" : getAttributionTag().value().c_str());
}
diff --git a/media/libaaudio/src/fifo/FifoBuffer.cpp b/media/libaaudio/src/fifo/FifoBuffer.cpp
index f5113f2..5c11882 100644
--- a/media/libaaudio/src/fifo/FifoBuffer.cpp
+++ b/media/libaaudio/src/fifo/FifoBuffer.cpp
@@ -31,40 +31,37 @@
#include "FifoBuffer.h"
using android::FifoBuffer;
+using android::FifoBufferAllocated;
+using android::FifoBufferIndirect;
using android::fifo_frames_t;
-FifoBuffer::FifoBuffer(int32_t bytesPerFrame, fifo_frames_t capacityInFrames)
- : mBytesPerFrame(bytesPerFrame)
+FifoBuffer::FifoBuffer(int32_t bytesPerFrame)
+ : mBytesPerFrame(bytesPerFrame) {}
+
+FifoBufferAllocated::FifoBufferAllocated(int32_t bytesPerFrame, fifo_frames_t capacityInFrames)
+ : FifoBuffer(bytesPerFrame)
{
mFifo = std::make_unique<FifoController>(capacityInFrames, capacityInFrames);
// allocate buffer
int32_t bytesPerBuffer = bytesPerFrame * capacityInFrames;
- mStorage = new uint8_t[bytesPerBuffer];
- mStorageOwned = true;
+ mInternalStorage = std::make_unique<uint8_t[]>(bytesPerBuffer);
ALOGV("%s() capacityInFrames = %d, bytesPerFrame = %d",
__func__, capacityInFrames, bytesPerFrame);
}
-FifoBuffer::FifoBuffer( int32_t bytesPerFrame,
+FifoBufferIndirect::FifoBufferIndirect( int32_t bytesPerFrame,
fifo_frames_t capacityInFrames,
- fifo_counter_t * readIndexAddress,
- fifo_counter_t * writeIndexAddress,
+ fifo_counter_t *readIndexAddress,
+ fifo_counter_t *writeIndexAddress,
void * dataStorageAddress
)
- : mBytesPerFrame(bytesPerFrame)
- , mStorage(static_cast<uint8_t *>(dataStorageAddress))
+ : FifoBuffer(bytesPerFrame)
+ , mExternalStorage(static_cast<uint8_t *>(dataStorageAddress))
{
mFifo = std::make_unique<FifoControllerIndirect>(capacityInFrames,
capacityInFrames,
readIndexAddress,
writeIndexAddress);
- mStorageOwned = false;
-}
-
-FifoBuffer::~FifoBuffer() {
- if (mStorageOwned) {
- delete[] mStorage;
- }
}
int32_t FifoBuffer::convertFramesToBytes(fifo_frames_t frames) {
@@ -76,15 +73,16 @@
int32_t startIndex) {
wrappingBuffer->data[1] = nullptr;
wrappingBuffer->numFrames[1] = 0;
+ uint8_t *storage = getStorage();
if (framesAvailable > 0) {
fifo_frames_t capacity = mFifo->getCapacity();
- uint8_t *source = &mStorage[convertFramesToBytes(startIndex)];
+ uint8_t *source = &storage[convertFramesToBytes(startIndex)];
// Does the available data cross the end of the FIFO?
if ((startIndex + framesAvailable) > capacity) {
wrappingBuffer->data[0] = source;
fifo_frames_t firstFrames = capacity - startIndex;
wrappingBuffer->numFrames[0] = firstFrames;
- wrappingBuffer->data[1] = &mStorage[0];
+ wrappingBuffer->data[1] = &storage[0];
wrappingBuffer->numFrames[1] = framesAvailable - firstFrames;
} else {
wrappingBuffer->data[0] = source;
@@ -191,6 +189,6 @@
void FifoBuffer::eraseMemory() {
int32_t numBytes = convertFramesToBytes(getBufferCapacityInFrames());
if (numBytes > 0) {
- memset(mStorage, 0, (size_t) numBytes);
+ memset(getStorage(), 0, (size_t) numBytes);
}
}
diff --git a/media/libaaudio/src/fifo/FifoBuffer.h b/media/libaaudio/src/fifo/FifoBuffer.h
index 0d188c4..37548f0 100644
--- a/media/libaaudio/src/fifo/FifoBuffer.h
+++ b/media/libaaudio/src/fifo/FifoBuffer.h
@@ -38,15 +38,9 @@
class FifoBuffer {
public:
- FifoBuffer(int32_t bytesPerFrame, fifo_frames_t capacityInFrames);
+ FifoBuffer(int32_t bytesPerFrame);
- FifoBuffer(int32_t bytesPerFrame,
- fifo_frames_t capacityInFrames,
- fifo_counter_t *readCounterAddress,
- fifo_counter_t *writeCounterAddress,
- void *dataStorageAddress);
-
- ~FifoBuffer();
+ virtual ~FifoBuffer() = default;
int32_t convertFramesToBytes(fifo_frames_t frames);
@@ -121,19 +115,53 @@
*/
void eraseMemory();
-private:
+protected:
+
+ virtual uint8_t *getStorage() const = 0;
void fillWrappingBuffer(WrappingBuffer *wrappingBuffer,
int32_t framesAvailable, int32_t startIndex);
const int32_t mBytesPerFrame;
- // We do not use a std::unique_ptr for mStorage because it is often a pointer to
- // memory shared between processes and cannot be deleted trivially.
- uint8_t *mStorage = nullptr;
- bool mStorageOwned = false; // did this object allocate the storage?
std::unique_ptr<FifoControllerBase> mFifo{};
};
+// Define two subclasses to handle the two ways that storage is allocated.
+
+// Allocate storage internally.
+class FifoBufferAllocated : public FifoBuffer {
+public:
+ FifoBufferAllocated(int32_t bytesPerFrame, fifo_frames_t capacityInFrames);
+
+private:
+
+ uint8_t *getStorage() const override {
+ return mInternalStorage.get();
+ };
+
+ std::unique_ptr<uint8_t[]> mInternalStorage;
+};
+
+// Allocate storage externally and pass it in.
+class FifoBufferIndirect : public FifoBuffer {
+public:
+ // We use raw pointers because the memory may be
+ // in the middle of an allocated block and cannot be deleted directly.
+ FifoBufferIndirect(int32_t bytesPerFrame,
+ fifo_frames_t capacityInFrames,
+ fifo_counter_t* readCounterAddress,
+ fifo_counter_t* writeCounterAddress,
+ void* dataStorageAddress);
+
+private:
+
+ uint8_t *getStorage() const override {
+ return mExternalStorage;
+ };
+
+ uint8_t *mExternalStorage = nullptr;
+};
+
} // android
#endif //FIFO_FIFO_BUFFER_H
diff --git a/media/libaaudio/src/fifo/FifoControllerIndirect.h b/media/libaaudio/src/fifo/FifoControllerIndirect.h
index 5832d9c..ec48e57 100644
--- a/media/libaaudio/src/fifo/FifoControllerIndirect.h
+++ b/media/libaaudio/src/fifo/FifoControllerIndirect.h
@@ -27,7 +27,7 @@
/**
* A FifoControllerBase with counters external to the class.
*
- * The actual copunters may be stored in separate regions of shared memory
+ * The actual counters may be stored in separate regions of shared memory
* with different access rights.
*/
class FifoControllerIndirect : public FifoControllerBase {
diff --git a/media/libaaudio/src/flowgraph/FlowgraphUtilities.h b/media/libaaudio/src/flowgraph/FlowgraphUtilities.h
new file mode 100644
index 0000000..b750410
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/FlowgraphUtilities.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FLOWGRAPH_UTILITIES_H
+#define FLOWGRAPH_UTILITIES_H
+
+#include <unistd.h>
+
+using namespace flowgraph;
+
+class FlowgraphUtilities {
+public:
+// This was copied from audio_utils/primitives.h
+/**
+ * Convert a single-precision floating point value to a Q0.31 integer value.
+ * Rounds to nearest, ties away from 0.
+ *
+ * Values outside the range [-1.0, 1.0) are properly clamped to -2147483648 and 2147483647,
+ * including -Inf and +Inf. NaN values are considered undefined, and behavior may change
+ * depending on hardware and future implementation of this function.
+ */
+static int32_t clamp32FromFloat(float f)
+{
+ static const float scale = (float)(1UL << 31);
+ static const float limpos = 1.;
+ static const float limneg = -1.;
+
+ if (f <= limneg) {
+ return -0x80000000; /* or 0x80000000 */
+ } else if (f >= limpos) {
+ return 0x7fffffff;
+ }
+ f *= scale;
+ /* integer conversion is through truncation (though int to float is not).
+ * ensure that we round to nearest, ties away from 0.
+ */
+ return f > 0 ? f + 0.5 : f - 0.5;
+}
+
+};
+
+#endif // FLOWGRAPH_UTILITIES_H
diff --git a/media/libaaudio/src/flowgraph/SinkI24.cpp b/media/libaaudio/src/flowgraph/SinkI24.cpp
index 6592828..0cb077d 100644
--- a/media/libaaudio/src/flowgraph/SinkI24.cpp
+++ b/media/libaaudio/src/flowgraph/SinkI24.cpp
@@ -15,7 +15,7 @@
*/
#include <algorithm>
-#include <unistd.h>
+#include <stdint.h>
#ifdef __ANDROID__
#include <audio_utils/primitives.h>
@@ -26,7 +26,6 @@
using namespace flowgraph;
-
SinkI24::SinkI24(int32_t channelCount)
: AudioSink(channelCount) {}
diff --git a/media/libaaudio/src/flowgraph/SinkI32.cpp b/media/libaaudio/src/flowgraph/SinkI32.cpp
new file mode 100644
index 0000000..eab863d
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkI32.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright 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.
+ */
+
+#ifdef __ANDROID__
+#include <audio_utils/primitives.h>
+#endif
+
+#include "AudioProcessorBase.h"
+#include "FlowgraphUtilities.h"
+#include "SinkI32.h"
+
+using namespace flowgraph;
+
+SinkI32::SinkI32(int32_t channelCount)
+ : AudioSink(channelCount) {}
+
+int32_t SinkI32::read(void *data, int32_t numFrames) {
+ int32_t *intData = (int32_t *) data;
+ const int32_t channelCount = input.getSamplesPerFrame();
+
+ int32_t framesLeft = numFrames;
+ while (framesLeft > 0) {
+ // Run the graph and pull data through the input port.
+ int32_t framesRead = pull(framesLeft);
+ if (framesRead <= 0) {
+ break;
+ }
+ const float *signal = input.getBlock();
+ int32_t numSamples = framesRead * channelCount;
+#ifdef __ANDROID__
+ memcpy_to_i32_from_float(intData, signal, numSamples);
+ intData += numSamples;
+ signal += numSamples;
+#else
+ for (int i = 0; i < numSamples; i++) {
+ *intData++ = FlowgraphUtilities::clamp32FromFloat(*signal++);
+ }
+#endif
+ framesLeft -= framesRead;
+ }
+ return numFrames - framesLeft;
+}
diff --git a/media/libaaudio/src/flowgraph/SinkI32.h b/media/libaaudio/src/flowgraph/SinkI32.h
new file mode 100644
index 0000000..09d23b7
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkI32.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FLOWGRAPH_SINK_I32_H
+#define FLOWGRAPH_SINK_I32_H
+
+#include <stdint.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class SinkI32 : public AudioSink {
+public:
+ explicit SinkI32(int32_t channelCount);
+ ~SinkI32() override = default;
+
+ int32_t read(void *data, int32_t numFrames) override;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_SINK_I32_H
diff --git a/media/libaaudio/src/flowgraph/SourceI24.cpp b/media/libaaudio/src/flowgraph/SourceI24.cpp
index f319880..097954e 100644
--- a/media/libaaudio/src/flowgraph/SourceI24.cpp
+++ b/media/libaaudio/src/flowgraph/SourceI24.cpp
@@ -15,7 +15,7 @@
*/
#include <algorithm>
-#include <unistd.h>
+#include <stdint.h>
#ifdef __ANDROID__
#include <audio_utils/primitives.h>
diff --git a/media/libaaudio/src/flowgraph/SourceI24.h b/media/libaaudio/src/flowgraph/SourceI24.h
index 39f14da..2ed6f18 100644
--- a/media/libaaudio/src/flowgraph/SourceI24.h
+++ b/media/libaaudio/src/flowgraph/SourceI24.h
@@ -17,8 +17,7 @@
#ifndef FLOWGRAPH_SOURCE_I24_H
#define FLOWGRAPH_SOURCE_I24_H
-#include <unistd.h>
-#include <sys/types.h>
+#include <stdint.h>
#include "AudioProcessorBase.h"
diff --git a/media/libaaudio/src/flowgraph/SourceI32.cpp b/media/libaaudio/src/flowgraph/SourceI32.cpp
new file mode 100644
index 0000000..e8177ad
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceI32.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <unistd.h>
+
+#ifdef __ANDROID__
+#include <audio_utils/primitives.h>
+#endif
+
+#include "AudioProcessorBase.h"
+#include "SourceI32.h"
+
+using namespace flowgraph;
+
+SourceI32::SourceI32(int32_t channelCount)
+ : AudioSource(channelCount) {
+}
+
+int32_t SourceI32::onProcess(int64_t framePosition, int32_t numFrames) {
+ float *floatData = output.getBlock();
+ int32_t channelCount = output.getSamplesPerFrame();
+
+ int32_t framesLeft = mSizeInFrames - mFrameIndex;
+ int32_t framesToProcess = std::min(numFrames, framesLeft);
+ int32_t numSamples = framesToProcess * channelCount;
+
+ const int32_t *intBase = static_cast<const int32_t *>(mData);
+ const int32_t *intData = &intBase[mFrameIndex * channelCount];
+
+#ifdef __ANDROID__
+ memcpy_to_float_from_i32(floatData, intData, numSamples);
+#else
+ for (int i = 0; i < numSamples; i++) {
+ *floatData++ = *intData++ * kScale;
+ }
+#endif
+
+ mFrameIndex += framesToProcess;
+ return framesToProcess;
+}
diff --git a/media/libaaudio/src/flowgraph/SourceI32.h b/media/libaaudio/src/flowgraph/SourceI32.h
new file mode 100644
index 0000000..e50f9be
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceI32.h
@@ -0,0 +1,39 @@
+/*
+ * 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 FLOWGRAPH_SOURCE_I32_H
+#define FLOWGRAPH_SOURCE_I32_H
+
+#include <stdint.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class SourceI32 : public AudioSource {
+public:
+ explicit SourceI32(int32_t channelCount);
+ ~SourceI32() override = default;
+
+ int32_t onProcess(int64_t framePosition, int32_t numFrames) override;
+
+private:
+ static constexpr float kScale = 1.0 / (1UL << 31);
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_SOURCE_I32_H
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index 33c1bf5..60eb73a 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -124,7 +124,7 @@
__func__, callbackResult);
}
audioBuffer->size = 0;
- systemStopFromCallback();
+ systemStopInternal();
// Disable the callback just in case the system keeps trying to call us.
mCallbackEnabled.store(false);
}
@@ -226,7 +226,7 @@
ALOGD("%s() request DISCONNECT in data callback, device %d => %d",
__func__, (int) getDeviceId(), (int) deviceId);
// If the stream is stopped before the data callback has a chance to handle the
- // request then the requestStop() and requestPause() methods will handle it after
+ // request then the requestStop_l() and requestPause() methods will handle it after
// the callback has stopped.
mRequestDisconnect.request();
} else {
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.h b/media/libaaudio/src/legacy/AudioStreamLegacy.h
index fefe6e0..88ef270 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.h
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.h
@@ -112,6 +112,18 @@
return mFramesRead.increment(frames);
}
+ /**
+ * Get the framesPerBurst from the underlying API.
+ * @return framesPerBurst
+ */
+ virtual int32_t getFramesPerBurstFromDevice() const = 0;
+
+ /**
+ * Get the bufferCapacity from the underlying API.
+ * @return bufferCapacity in frames
+ */
+ virtual int32_t getBufferCapacityFromDevice() const = 0;
+
// This is used for exact matching by MediaMetrics. So do not change it.
// MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_AAUDIO
static constexpr char kCallerName[] = "aaudio";
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index d62951e..eca5392 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -22,14 +22,18 @@
#include <aaudio/AAudio.h>
#include <audio_utils/primitives.h>
+#include <media/AidlConversion.h>
#include <media/AudioRecord.h>
#include <utils/String16.h>
+#include "core/AudioGlobal.h"
#include "legacy/AudioStreamLegacy.h"
#include "legacy/AudioStreamRecord.h"
#include "utility/AudioClock.h"
#include "utility/FixedBlockWriter.h"
+using android::media::permission::Identity;
+
using namespace android;
using namespace aaudio;
@@ -89,8 +93,9 @@
break;
}
+ const audio_format_t requestedFormat = getFormat();
// Preserve behavior of API 26
- if (getFormat() == AUDIO_FORMAT_DEFAULT) {
+ if (requestedFormat == AUDIO_FORMAT_DEFAULT) {
setFormat(AUDIO_FORMAT_PCM_FLOAT);
}
@@ -118,6 +123,7 @@
setDeviceFormat(getFormat());
}
+ // To avoid glitching, let AudioFlinger pick the optimal burst size.
uint32_t notificationFrames = 0;
// Setup the callback if there is one.
@@ -128,7 +134,6 @@
streamTransferType = AudioRecord::transfer_type::TRANSFER_CALLBACK;
callback = getLegacyCallback();
callbackData = this;
- notificationFrames = builder.getFramesPerDataCallback();
}
mCallbackBufferSize = builder.getFramesPerDataCallback();
@@ -152,13 +157,20 @@
.tags = ""
};
+ // TODO b/182392769: use identity util
+ Identity identity;
+ identity.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(getuid()));
+ identity.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(getpid()));
+ identity.packageName = builder.getOpPackageName();
+ identity.attributionTag = builder.getAttributionTag();
+
// ----------- open the AudioRecord ---------------------
// Might retry, but never more than once.
for (int i = 0; i < 2; i ++) {
const audio_format_t requestedInternalFormat = getDeviceFormat();
mAudioRecord = new AudioRecord(
- mOpPackageName // const String16& opPackageName TODO does not compile
+ identity
);
mAudioRecord->set(
AUDIO_SOURCE_DEFAULT, // ignored because we pass attributes below
@@ -185,7 +197,7 @@
// Did we get a valid track?
status_t status = mAudioRecord->initCheck();
if (status != OK) {
- releaseCloseFinal();
+ safeReleaseClose();
ALOGE("open(), initCheck() returned %d", status);
return AAudioConvert_androidToAAudioResult(status);
}
@@ -207,15 +219,18 @@
mMetricsId = std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_RECORD)
+ std::to_string(mAudioRecord->getPortId());
+ android::mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_PERFORMANCEMODE,
+ AudioGlobal_convertPerformanceModeToText(builder.getPerformanceMode()))
+ .set(AMEDIAMETRICS_PROP_SHARINGMODE,
+ AudioGlobal_convertSharingModeToText(builder.getSharingMode()))
+ .set(AMEDIAMETRICS_PROP_ENCODINGCLIENT, toString(requestedFormat).c_str()).record();
// Get the actual values from the AudioRecord.
setSamplesPerFrame(mAudioRecord->channelCount());
-
- int32_t actualSampleRate = mAudioRecord->getSampleRate();
- ALOGW_IF(actualSampleRate != getSampleRate(),
- "open() sampleRate changed from %d to %d",
- getSampleRate(), actualSampleRate);
- setSampleRate(actualSampleRate);
+ setSampleRate(mAudioRecord->getSampleRate());
+ setBufferCapacity(getBufferCapacityFromDevice());
+ setFramesPerBurst(getFramesPerBurstFromDevice());
// We may need to pass the data through a block size adapter to guarantee constant size.
if (mCallbackBufferSize != AAUDIO_UNSPECIFIED) {
@@ -301,11 +316,19 @@
}
void AudioStreamRecord::close_l() {
+ // The callbacks are normally joined in the AudioRecord destructor.
+ // But if another object has a reference to the AudioRecord then
+ // it will not get deleted here.
+ // So we should join callbacks explicitly before returning.
+ // Unlock around the join to avoid deadlocks if the callback tries to lock.
+ // This can happen if the callback returns AAUDIO_CALLBACK_RESULT_STOP
+ mStreamLock.unlock();
+ mAudioRecord->stopAndJoinCallbacks();
+ mStreamLock.lock();
+
mAudioRecord.clear();
- // Do not close mFixedBlockWriter because a data callback
- // thread might still be running if someone else has a reference
- // to mAudioRecord.
- // It has a unique_ptr to its buffer so it will clean up by itself.
+ // Do not close mFixedBlockReader. It has a unique_ptr to its buffer
+ // so it will clean up by itself.
AudioStream::close_l();
}
@@ -344,7 +367,7 @@
return;
}
-aaudio_result_t AudioStreamRecord::requestStart()
+aaudio_result_t AudioStreamRecord::requestStart_l()
{
if (mAudioRecord.get() == nullptr) {
return AAUDIO_ERROR_INVALID_STATE;
@@ -368,7 +391,7 @@
return AAUDIO_OK;
}
-aaudio_result_t AudioStreamRecord::requestStop() {
+aaudio_result_t AudioStreamRecord::requestStop_l() {
if (mAudioRecord.get() == nullptr) {
return AAUDIO_ERROR_INVALID_STATE;
}
@@ -491,7 +514,7 @@
return getBufferCapacity(); // TODO implement in AudioRecord?
}
-int32_t AudioStreamRecord::getBufferCapacity() const
+int32_t AudioStreamRecord::getBufferCapacityFromDevice() const
{
return static_cast<int32_t>(mAudioRecord->frameCount());
}
@@ -501,8 +524,7 @@
return 0; // TODO implement when AudioRecord supports it
}
-int32_t AudioStreamRecord::getFramesPerBurst() const
-{
+int32_t AudioStreamRecord::getFramesPerBurstFromDevice() const {
return static_cast<int32_t>(mAudioRecord->getNotificationPeriodInFrames());
}
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.h b/media/libaaudio/src/legacy/AudioStreamRecord.h
index e4ef1c0..7d0a197 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.h
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.h
@@ -25,6 +25,7 @@
#include "AAudioLegacy.h"
#include "legacy/AudioStreamLegacy.h"
#include "utility/FixedBlockWriter.h"
+#include <android/media/permission/Identity.h>
namespace aaudio {
@@ -41,9 +42,6 @@
aaudio_result_t release_l() override;
void close_l() override;
- aaudio_result_t requestStart() override;
- aaudio_result_t requestStop() override;
-
virtual aaudio_result_t getTimestamp(clockid_t clockId,
int64_t *framePosition,
int64_t *timeNanoseconds) override;
@@ -56,14 +54,10 @@
int32_t getBufferSize() const override;
- int32_t getBufferCapacity() const override;
-
int32_t getXRunCount() const override;
int64_t getFramesWritten() override;
- int32_t getFramesPerBurst() const override;
-
aaudio_result_t updateStateMachine() override;
aaudio_direction_t getDirection() const override {
@@ -79,13 +73,21 @@
const void * maybeConvertDeviceData(const void *audioData, int32_t numFrames) override;
+protected:
+
+ aaudio_result_t requestStart_l() REQUIRES(mStreamLock) override;
+ aaudio_result_t requestStop_l() REQUIRES(mStreamLock) override;
+
+ int32_t getFramesPerBurstFromDevice() const override;
+ int32_t getBufferCapacityFromDevice() const override;
+
private:
android::sp<android::AudioRecord> mAudioRecord;
// adapts between variable sized blocks and fixed size blocks
FixedBlockWriter mFixedBlockWriter;
// TODO add 64-bit position reporting to AudioRecord and use it.
- android::String16 mOpPackageName;
+ android::media::permission::Identity mIdentity;
// Only one type of conversion buffer is used.
std::unique_ptr<float[]> mFormatConversionBufferFloat;
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 3831046..04a9dec 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -23,14 +23,18 @@
#include <aaudio/AAudio.h>
#include <system/audio.h>
-#include "utility/AudioClock.h"
+
+#include "core/AudioGlobal.h"
#include "legacy/AudioStreamLegacy.h"
#include "legacy/AudioStreamTrack.h"
+#include "utility/AudioClock.h"
#include "utility/FixedBlockReader.h"
using namespace android;
using namespace aaudio;
+using media::permission::Identity;
+
// Arbitrary and somewhat generous number of bursts.
#define DEFAULT_BURSTS_PER_BUFFER_CAPACITY 8
@@ -96,6 +100,7 @@
size_t frameCount = (size_t)builder.getBufferCapacity();
+ // To avoid glitching, let AudioFlinger pick the optimal burst size.
int32_t notificationFrames = 0;
const audio_format_t format = (getFormat() == AUDIO_FORMAT_DEFAULT)
@@ -118,8 +123,6 @@
// Take advantage of a special trick that allows us to create a buffer
// that is some multiple of the burst size.
notificationFrames = 0 - DEFAULT_BURSTS_PER_BUFFER_CAPACITY;
- } else {
- notificationFrames = builder.getFramesPerDataCallback();
}
}
mCallbackBufferSize = builder.getFramesPerDataCallback();
@@ -148,6 +151,7 @@
};
mAudioTrack = new AudioTrack();
+ // TODO b/182392769: use identity util
mAudioTrack->set(
AUDIO_STREAM_DEFAULT, // ignored because we pass attributes below
getSampleRate(),
@@ -163,8 +167,7 @@
sessionId,
streamTransferType,
NULL, // DEFAULT audio_offload_info_t
- AUDIO_UID_INVALID, // DEFAULT uid
- -1, // DEFAULT pid
+ Identity(), // DEFAULT uid and pid
&attributes,
// WARNING - If doNotReconnect set true then audio stops after plugging and unplugging
// headphones a few times.
@@ -179,13 +182,19 @@
// Did we get a valid track?
status_t status = mAudioTrack->initCheck();
if (status != NO_ERROR) {
- releaseCloseFinal();
+ safeReleaseClose();
ALOGE("open(), initCheck() returned %d", status);
return AAudioConvert_androidToAAudioResult(status);
}
mMetricsId = std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK)
+ std::to_string(mAudioTrack->getPortId());
+ android::mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_PERFORMANCEMODE,
+ AudioGlobal_convertPerformanceModeToText(builder.getPerformanceMode()))
+ .set(AMEDIAMETRICS_PROP_SHARINGMODE,
+ AudioGlobal_convertSharingModeToText(builder.getSharingMode()))
+ .set(AMEDIAMETRICS_PROP_ENCODINGCLIENT, toString(getFormat()).c_str()).record();
doSetVolume();
@@ -193,12 +202,9 @@
setSamplesPerFrame(mAudioTrack->channelCount());
setFormat(mAudioTrack->format());
setDeviceFormat(mAudioTrack->format());
-
- int32_t actualSampleRate = mAudioTrack->getSampleRate();
- ALOGW_IF(actualSampleRate != getSampleRate(),
- "open() sampleRate changed from %d to %d",
- getSampleRate(), actualSampleRate);
- setSampleRate(actualSampleRate);
+ setSampleRate(mAudioTrack->getSampleRate());
+ setBufferCapacity(getBufferCapacityFromDevice());
+ setFramesPerBurst(getFramesPerBurstFromDevice());
// We may need to pass the data through a block size adapter to guarantee constant size.
if (mCallbackBufferSize != AAUDIO_UNSPECIFIED) {
@@ -221,9 +227,6 @@
: (aaudio_session_id_t) mAudioTrack->getSessionId();
setSessionId(actualSessionId);
- mInitialBufferCapacity = getBufferCapacity();
- mInitialFramesPerBurst = getFramesPerBurst();
-
mAudioTrack->addAudioDeviceCallback(this);
// Update performance mode based on the actual stream flags.
@@ -240,11 +243,11 @@
setSharingMode(AAUDIO_SHARING_MODE_SHARED); // EXCLUSIVE mode not supported in legacy
- // Log warning if we did not get what we asked for.
- ALOGW_IF(actualFlags != flags,
+ // Log if we did not get what we asked for.
+ ALOGD_IF(actualFlags != flags,
"open() flags changed from 0x%08X to 0x%08X",
flags, actualFlags);
- ALOGW_IF(actualPerformanceMode != perfMode,
+ ALOGD_IF(actualPerformanceMode != perfMode,
"open() perfMode changed from %d to %d",
perfMode, actualPerformanceMode);
@@ -264,12 +267,18 @@
}
void AudioStreamTrack::close_l() {
- // Stop callbacks before deleting mFixedBlockReader memory.
+ // The callbacks are normally joined in the AudioTrack destructor.
+ // But if another object has a reference to the AudioTrack then
+ // it will not get deleted here.
+ // So we should join callbacks explicitly before returning.
+ // Unlock around the join to avoid deadlocks if the callback tries to lock.
+ // This can happen if the callback returns AAUDIO_CALLBACK_RESULT_STOP
+ mStreamLock.unlock();
+ mAudioTrack->stopAndJoinCallbacks();
+ mStreamLock.lock();
mAudioTrack.clear();
- // Do not close mFixedBlockReader because a data callback
- // thread might still be running if someone else has a reference
- // to mAudioRecord.
- // It has a unique_ptr to its buffer so it will clean up by itself.
+ // Do not close mFixedBlockReader. It has a unique_ptr to its buffer
+ // so it will clean up by itself.
AudioStream::close_l();
}
@@ -288,8 +297,8 @@
|| mAudioTrack->format() != getFormat()
|| mAudioTrack->getSampleRate() != getSampleRate()
|| mAudioTrack->getRoutedDeviceId() != getDeviceId()
- || getBufferCapacity() != mInitialBufferCapacity
- || getFramesPerBurst() != mInitialFramesPerBurst) {
+ || getBufferCapacityFromDevice() != getBufferCapacity()
+ || getFramesPerBurstFromDevice() != getFramesPerBurst()) {
processCallbackCommon(AAUDIO_CALLBACK_OPERATION_DISCONNECTED, info);
}
break;
@@ -300,7 +309,7 @@
return;
}
-aaudio_result_t AudioStreamTrack::requestStart() {
+aaudio_result_t AudioStreamTrack::requestStart_l() {
if (mAudioTrack.get() == nullptr) {
ALOGE("requestStart() no AudioTrack");
return AAUDIO_ERROR_INVALID_STATE;
@@ -327,7 +336,7 @@
return AAUDIO_OK;
}
-aaudio_result_t AudioStreamTrack::requestPause() {
+aaudio_result_t AudioStreamTrack::requestPause_l() {
if (mAudioTrack.get() == nullptr) {
ALOGE("%s() no AudioTrack", __func__);
return AAUDIO_ERROR_INVALID_STATE;
@@ -343,7 +352,7 @@
return checkForDisconnectRequest(false);
}
-aaudio_result_t AudioStreamTrack::requestFlush() {
+aaudio_result_t AudioStreamTrack::requestFlush_l() {
if (mAudioTrack.get() == nullptr) {
ALOGE("%s() no AudioTrack", __func__);
return AAUDIO_ERROR_INVALID_STATE;
@@ -357,7 +366,7 @@
return AAUDIO_OK;
}
-aaudio_result_t AudioStreamTrack::requestStop() {
+aaudio_result_t AudioStreamTrack::requestStop_l() {
if (mAudioTrack.get() == nullptr) {
ALOGE("%s() no AudioTrack", __func__);
return AAUDIO_ERROR_INVALID_STATE;
@@ -478,7 +487,7 @@
return static_cast<int32_t>(mAudioTrack->getBufferSizeInFrames());
}
-int32_t AudioStreamTrack::getBufferCapacity() const
+int32_t AudioStreamTrack::getBufferCapacityFromDevice() const
{
return static_cast<int32_t>(mAudioTrack->frameCount());
}
@@ -488,8 +497,7 @@
return static_cast<int32_t>(mAudioTrack->getUnderrunCount());
}
-int32_t AudioStreamTrack::getFramesPerBurst() const
-{
+int32_t AudioStreamTrack::getFramesPerBurstFromDevice() const {
return static_cast<int32_t>(mAudioTrack->getNotificationPeriodInFrames());
}
@@ -566,7 +574,7 @@
if (status < 0) { // a non-negative value is the volume shaper id.
ALOGE("applyVolumeShaper() failed with status %d", status);
}
- return binder::Status::fromStatusT(status);
+ return aidl_utils::binderStatusFromStatusT(status);
} else {
ALOGD("applyVolumeShaper()"
" no AudioTrack for volume control from IPlayer");
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.h b/media/libaaudio/src/legacy/AudioStreamTrack.h
index 6334f66..f604871 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.h
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.h
@@ -44,11 +44,13 @@
aaudio_result_t release_l() override;
void close_l() override;
- aaudio_result_t requestStart() override;
- aaudio_result_t requestPause() override;
- aaudio_result_t requestFlush() override;
- aaudio_result_t requestStop() override;
+protected:
+ aaudio_result_t requestStart_l() REQUIRES(mStreamLock) override;
+ aaudio_result_t requestPause_l() REQUIRES(mStreamLock) override;
+ aaudio_result_t requestFlush_l() REQUIRES(mStreamLock) override;
+ aaudio_result_t requestStop_l() REQUIRES(mStreamLock) override;
+public:
bool isFlushSupported() const override {
// Only implement FLUSH for OUTPUT streams.
return true;
@@ -69,8 +71,6 @@
aaudio_result_t setBufferSize(int32_t requestedFrames) override;
int32_t getBufferSize() const override;
- int32_t getBufferCapacity() const override;
- int32_t getFramesPerBurst()const override;
int32_t getXRunCount() const override;
int64_t getFramesRead() override;
@@ -96,6 +96,11 @@
const android::media::VolumeShaper::Operation& operation) override;
#endif
+protected:
+
+ int32_t getFramesPerBurstFromDevice() const override;
+ int32_t getBufferCapacityFromDevice() const override;
+
private:
android::sp<android::AudioTrack> mAudioTrack;
@@ -105,10 +110,6 @@
// TODO add 64-bit position reporting to AudioTrack and use it.
aaudio_wrapping_frames_t mPositionWhenPausing = 0;
-
- // initial AudioTrack frame count and notification period
- int32_t mInitialBufferCapacity = 0;
- int32_t mInitialFramesPerBurst = 0;
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/libaaudio.map.txt b/media/libaaudio/src/libaaudio.map.txt
index 2e00aa5..1dd44d1 100644
--- a/media/libaaudio/src/libaaudio.map.txt
+++ b/media/libaaudio/src/libaaudio.map.txt
@@ -23,6 +23,8 @@
AAudioStreamBuilder_setAllowedCapturePolicy; # introduced=29
AAudioStreamBuilder_setSessionId; # introduced=28
AAudioStreamBuilder_setPrivacySensitive; # introduced=30
+ AAudioStreamBuilder_setPackageName; # introduced=31
+ AAudioStreamBuilder_setAttributionTag; # introduced=31
AAudioStreamBuilder_openStream;
AAudioStreamBuilder_delete;
AAudioStream_close;
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index 3dfb801..d795725 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -134,6 +134,12 @@
case AAUDIO_FORMAT_PCM_FLOAT:
androidFormat = AUDIO_FORMAT_PCM_FLOAT;
break;
+ case AAUDIO_FORMAT_PCM_I24_PACKED:
+ androidFormat = AUDIO_FORMAT_PCM_24_BIT_PACKED;
+ break;
+ case AAUDIO_FORMAT_PCM_I32:
+ androidFormat = AUDIO_FORMAT_PCM_32_BIT;
+ break;
default:
androidFormat = AUDIO_FORMAT_INVALID;
ALOGE("%s() 0x%08X unrecognized", __func__, aaudioFormat);
@@ -154,6 +160,12 @@
case AUDIO_FORMAT_PCM_FLOAT:
aaudioFormat = AAUDIO_FORMAT_PCM_FLOAT;
break;
+ case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+ aaudioFormat = AAUDIO_FORMAT_PCM_I24_PACKED;
+ break;
+ case AUDIO_FORMAT_PCM_32_BIT:
+ aaudioFormat = AAUDIO_FORMAT_PCM_I32;
+ break;
default:
aaudioFormat = AAUDIO_FORMAT_INVALID;
ALOGE("%s() 0x%08X unrecognized", __func__, androidFormat);
diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp
index ccfbea3..98e9727 100644
--- a/media/libaaudio/tests/Android.bp
+++ b/media/libaaudio/tests/Android.bp
@@ -20,10 +20,12 @@
defaults: ["libaaudio_tests_defaults"],
srcs: ["test_marshalling.cpp"],
shared_libs: [
+ "aaudio-aidl-cpp",
"libaaudio_internal",
"libbinder",
"libcutils",
"libutils",
+ "shared-file-region-aidl-cpp",
],
}
@@ -207,9 +209,9 @@
}
cc_test {
- name: "test_stop_hang",
+ name: "test_callback_race",
defaults: ["libaaudio_tests_defaults"],
- srcs: ["test_stop_hang.cpp"],
+ srcs: ["test_callback_race.cpp"],
shared_libs: [
"libaaudio",
"libbinder",
@@ -248,3 +250,16 @@
"libutils",
],
}
+
+
+cc_test {
+ name: "test_disconnect_race",
+ defaults: ["libaaudio_tests_defaults"],
+ srcs: ["test_disconnect_race.cpp"],
+ shared_libs: [
+ "libaaudio",
+ "libbinder",
+ "libcutils",
+ "libutils",
+ ],
+}
diff --git a/media/libaaudio/tests/test_aaudio_monkey.cpp b/media/libaaudio/tests/test_aaudio_monkey.cpp
index be54835..cc29678 100644
--- a/media/libaaudio/tests/test_aaudio_monkey.cpp
+++ b/media/libaaudio/tests/test_aaudio_monkey.cpp
@@ -46,11 +46,10 @@
int32_t numFrames);
void AAudioMonkeyErrorCallbackProc(
- AAudioStream *stream __unused,
- void *userData __unused,
- aaudio_result_t error) {
- printf("Error Callback, error: %d\n",(int)error);
-}
+ AAudioStream * /* stream */,
+ void *userData,
+ aaudio_result_t error);
+
// This function is not thread safe. Only use this from a single thread.
double nextRandomDouble() {
@@ -99,6 +98,10 @@
aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNKNOWN;
aaudio_result_t result = AAudioStream_waitForStateChange(getStream(),
AAUDIO_STREAM_STATE_UNKNOWN, &state, 0);
+ if (result == AAUDIO_ERROR_DISCONNECTED) {
+ printf("WARNING - AAudioStream_waitForStateChange returned DISCONNECTED\n");
+ return true; // OK
+ }
if (result != AAUDIO_OK) {
printf("ERROR - AAudioStream_waitForStateChange returned %d\n", result);
return false;
@@ -114,7 +117,7 @@
(unsigned long long) framesRead,
xRuns);
- if (framesWritten < framesRead) {
+ if (state != AAUDIO_STREAM_STATE_STARTING && framesWritten < framesRead) {
printf("WARNING - UNDERFLOW - diff = %d !!!!!!!!!!!!\n",
(int) (framesWritten - framesRead));
}
@@ -132,8 +135,23 @@
return -1;
}
+ // update and query stream state
+ aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNKNOWN;
+ state = AAudioStream_getState(getStream());
+ if (state < 0) {
+ printf("ERROR - AAudioStream_getState returned %d\n", state);
+ return state;
+ }
+
+ if (state == AAUDIO_STREAM_STATE_DISCONNECTED) {
+ printf("#%d, Closing disconnected stream.\n", getIndex());
+ result = close();
+ return result;
+ }
+
double dice = nextRandomDouble();
// Select an action based on a weighted probability.
+ printf(" "); // indent action
if (dice < PROB_START) {
printf("start\n");
result = AAudioStream_requestStart(getStream());
@@ -200,6 +218,10 @@
return AAUDIO_CALLBACK_RESULT_CONTINUE;
}
+ int getIndex() const {
+ return mIndex;
+ }
+
private:
const AAudioArgsParser *mArgParser;
const int mIndex;
@@ -223,6 +245,13 @@
return monkey->renderAudio(stream, audioData, numFrames);
}
+void AAudioMonkeyErrorCallbackProc(
+ AAudioStream * /* stream */,
+ void *userData,
+ aaudio_result_t error) {
+ AAudioMonkey *monkey = (AAudioMonkey *) userData;
+ printf("#%d, Error Callback, error: %d\n", monkey->getIndex(), (int)error);
+}
static void usage() {
AAudioArgsParser::usage();
diff --git a/media/libaaudio/tests/test_atomic_fifo.cpp b/media/libaaudio/tests/test_atomic_fifo.cpp
index 130ef43..4dbb219 100644
--- a/media/libaaudio/tests/test_atomic_fifo.cpp
+++ b/media/libaaudio/tests/test_atomic_fifo.cpp
@@ -26,6 +26,7 @@
using android::fifo_counter_t;
using android::FifoController;
using android::FifoBuffer;
+using android::FifoBufferIndirect;
using android::WrappingBuffer;
TEST(test_fifo_controller, fifo_indices) {
@@ -325,7 +326,7 @@
verifyStorageIntegrity();
}
- FifoBuffer mFifoBuffer;
+ FifoBufferIndirect mFifoBuffer;
fifo_frames_t mNextWriteIndex = 0;
fifo_frames_t mNextVerifyIndex = 0;
fifo_frames_t mThreshold;
diff --git a/media/libaaudio/tests/test_callback_race.cpp b/media/libaaudio/tests/test_callback_race.cpp
new file mode 100644
index 0000000..843d5d7
--- /dev/null
+++ b/media/libaaudio/tests/test_callback_race.cpp
@@ -0,0 +1,209 @@
+/*
+ * 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.
+ */
+
+/**
+ * Test whether the callback is joined before the close finishes.
+ *
+ * Start a stream with a callback.
+ * The callback just sleeps for a long time.
+ * While the callback is sleeping, close() the stream from the main thread.
+ * Then check to make sure the callback was joined before the close() returns.
+ *
+ * This can hang if there are deadlocks. So make sure you get a PASSED result.
+ */
+
+#include <atomic>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <aaudio/AAudio.h>
+
+// Sleep long enough that the foreground has a change to call close.
+static constexpr int kCallbackSleepMicros = 600 * 1000;
+
+class AudioEngine {
+public:
+
+ // Check for a crash or late callback if we close without stopping.
+ void checkCloseJoins(aaudio_direction_t direction,
+ aaudio_performance_mode_t perfMode,
+ aaudio_data_callback_result_t callbackResult) {
+
+ // Make printf print immediately so that debug info is not stuck
+ // in a buffer if we hang or crash.
+ setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
+
+ mCallbackResult = callbackResult;
+ startStreamForStall(direction, perfMode);
+ // When the callback starts it will go to sleep.
+ waitForCallbackToStart();
+
+ printf("call AAudioStream_close()\n");
+ ASSERT_FALSE(mCallbackFinished); // Still sleeping?
+ aaudio_result_t result = AAudioStream_close(mStream); // May hang here!
+ ASSERT_TRUE(mCallbackFinished);
+ ASSERT_EQ(AAUDIO_OK, result);
+ printf("AAudioStream_close() returned %d\n", result);
+
+ ASSERT_EQ(AAUDIO_OK, mError.load());
+ // Did calling stop() from callback fail? It should have.
+ ASSERT_NE(AAUDIO_OK, mStopResult.load());
+ }
+
+private:
+ void startStreamForStall(aaudio_direction_t direction,
+ aaudio_performance_mode_t perfMode) {
+ AAudioStreamBuilder* builder = nullptr;
+ aaudio_result_t result = AAUDIO_OK;
+
+ // Use an AAudioStreamBuilder to contain requested parameters.
+ result = AAudio_createStreamBuilder(&builder);
+ ASSERT_EQ(AAUDIO_OK, result);
+
+ // Request stream properties.
+ AAudioStreamBuilder_setDirection(builder, direction);
+ AAudioStreamBuilder_setPerformanceMode(builder, perfMode);
+ AAudioStreamBuilder_setDataCallback(builder, s_myDataCallbackProc, this);
+ AAudioStreamBuilder_setErrorCallback(builder, s_myErrorCallbackProc, this);
+
+ // Create an AAudioStream using the Builder.
+ result = AAudioStreamBuilder_openStream(builder, &mStream);
+ AAudioStreamBuilder_delete(builder);
+ ASSERT_EQ(AAUDIO_OK, result);
+
+ // Check to see what kind of stream we actually got.
+ int32_t deviceId = AAudioStream_getDeviceId(mStream);
+ aaudio_performance_mode_t
+ actualPerfMode = AAudioStream_getPerformanceMode(mStream);
+ printf("-------- opened: deviceId = %3d, perfMode = %d\n",
+ deviceId,
+ actualPerfMode);
+
+ // Start stream.
+ result = AAudioStream_requestStart(mStream);
+ ASSERT_EQ(AAUDIO_OK, result);
+ }
+
+ void waitForCallbackToStart() {
+ // Wait for callback to say it has been called.
+ int countDownMillis = 2000;
+ constexpr int countDownPeriodMillis = 50;
+ while (!mCallbackStarted && countDownMillis > 0) {
+ printf("Waiting for callback to start, %d\n", countDownMillis);
+ usleep(countDownPeriodMillis * 1000);
+ countDownMillis -= countDownPeriodMillis;
+ }
+ ASSERT_LT(0, countDownMillis);
+ ASSERT_TRUE(mCallbackStarted);
+ }
+
+// Callback function that fills the audio output buffer.
+ static aaudio_data_callback_result_t s_myDataCallbackProc(
+ AAudioStream *stream,
+ void *userData,
+ void * /*audioData */,
+ int32_t /* numFrames */
+ ) {
+ AudioEngine* engine = (AudioEngine*) userData;
+ engine->mCallbackStarted = true;
+ usleep(kCallbackSleepMicros);
+ // it is illegal to call stop() from the callback. It should
+ // return an error and not hang.
+ engine->mStopResult = AAudioStream_requestStop(stream);
+ engine->mCallbackFinished = true;
+ return engine->mCallbackResult;
+ }
+
+ static void s_myErrorCallbackProc(
+ AAudioStream * /* stream */,
+ void *userData,
+ aaudio_result_t error) {
+ AudioEngine *engine = (AudioEngine *)userData;
+ engine->mError = error;
+ }
+
+ AAudioStream* mStream = nullptr;
+
+ std::atomic<aaudio_result_t> mError{AAUDIO_OK}; // written by error callback
+ std::atomic<bool> mCallbackStarted{false}; // written by data callback
+ std::atomic<bool> mCallbackFinished{false}; // written by data callback
+ std::atomic<aaudio_data_callback_result_t> mCallbackResult{AAUDIO_CALLBACK_RESULT_CONTINUE};
+ std::atomic<aaudio_result_t> mStopResult{AAUDIO_OK};
+};
+
+/*********************************************************************/
+// Tell the callback to return AAUDIO_CALLBACK_RESULT_CONTINUE.
+
+TEST(test_close_timing, aaudio_close_joins_input_none) {
+ AudioEngine engine;
+ engine.checkCloseJoins(AAUDIO_DIRECTION_INPUT,
+ AAUDIO_PERFORMANCE_MODE_NONE,
+ AAUDIO_CALLBACK_RESULT_CONTINUE);
+}
+
+TEST(test_close_timing, aaudio_close_joins_output_none) {
+ AudioEngine engine;
+ engine.checkCloseJoins(AAUDIO_DIRECTION_OUTPUT,
+ AAUDIO_PERFORMANCE_MODE_NONE,
+ AAUDIO_CALLBACK_RESULT_CONTINUE);
+}
+
+TEST(test_close_timing, aaudio_close_joins_input_lowlat) {
+ AudioEngine engine;
+ engine.checkCloseJoins(AAUDIO_DIRECTION_INPUT,
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+ AAUDIO_CALLBACK_RESULT_CONTINUE);
+}
+
+TEST(test_close_timing, aaudio_close_joins_output_lowlat) {
+ AudioEngine engine;
+ engine.checkCloseJoins(AAUDIO_DIRECTION_OUTPUT,
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+ AAUDIO_CALLBACK_RESULT_CONTINUE);
+}
+
+/*********************************************************************/
+// Tell the callback to return AAUDIO_CALLBACK_RESULT_STOP.
+
+TEST(test_close_timing, aaudio_close_joins_input_lowlat_stop) {
+ AudioEngine engine;
+ engine.checkCloseJoins(AAUDIO_DIRECTION_INPUT,
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+ AAUDIO_CALLBACK_RESULT_STOP);
+}
+
+TEST(test_close_timing, aaudio_close_joins_output_lowlat_stop) {
+ AudioEngine engine;
+ engine.checkCloseJoins(AAUDIO_DIRECTION_OUTPUT,
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+ AAUDIO_CALLBACK_RESULT_STOP);
+}
+
+TEST(test_close_timing, aaudio_close_joins_output_none_stop) {
+ AudioEngine engine;
+ engine.checkCloseJoins(AAUDIO_DIRECTION_OUTPUT,
+ AAUDIO_PERFORMANCE_MODE_NONE,
+ AAUDIO_CALLBACK_RESULT_STOP);
+}
+
+TEST(test_close_timing, aaudio_close_joins_input_none_stop) {
+ AudioEngine engine;
+ engine.checkCloseJoins(AAUDIO_DIRECTION_INPUT,
+ AAUDIO_PERFORMANCE_MODE_NONE,
+ AAUDIO_CALLBACK_RESULT_STOP);
+}
diff --git a/media/libaaudio/tests/test_disconnect_race.cpp b/media/libaaudio/tests/test_disconnect_race.cpp
new file mode 100644
index 0000000..6dbe165
--- /dev/null
+++ b/media/libaaudio/tests/test_disconnect_race.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Test whether an error callback is joined before the close finishes.
+ *
+ * Start a stream with a callback.
+ * The callback just sleeps for a long time.
+ * While the callback is sleeping, close() the stream from the main thread.
+ * Then check to make sure the callback was joined before the close() returns.
+ *
+ * This can hang if there are deadlocks. So make sure you get a PASSED result.
+ */
+
+#include <atomic>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <aaudio/AAudio.h>
+
+// Sleep long enough that the foreground has a chance to call close.
+static constexpr int kCallbackSleepMillis = 1000;
+static constexpr int kPollSleepMillis = 100;
+
+static int sErrorCount = 0;
+
+#define MY_ASSERT_TRUE(statement) \
+ if (!(statement)) { \
+ printf("ERROR line:%d - " #statement "\n", __LINE__); \
+ sErrorCount++; \
+ return false; \
+ }
+
+#define MY_ASSERT_EQ(aa,bb) MY_ASSERT_TRUE(((aa) == (bb)))
+#define MY_ASSERT_NE(aa,bb) MY_ASSERT_TRUE(((aa) != (bb)))
+
+class AudioEngine {
+public:
+
+ // Check for a crash or late callback if we close without stopping.
+ bool checkCloseJoins(aaudio_direction_t direction,
+ aaudio_performance_mode_t perfMode,
+ bool callStopFromCallback) {
+ mCallStopFromCallback = callStopFromCallback;
+
+ if (!startStreamForStall(direction, perfMode)) return false;
+
+ printf("--------------------------------------------------------\n");
+ printf("%s() - direction = %d, perfMode = %d, callStop = %d\n",
+ __func__, direction, perfMode, callStopFromCallback);
+
+ // When the callback starts it will go to sleep.
+ if (!waitForCallbackToStart()) return false;
+
+ printf("call AAudioStream_close()\n");
+ MY_ASSERT_TRUE(!mCallbackFinished); // Still sleeping?
+ aaudio_result_t result = AAudioStream_close(mStream); // May hang here!
+ if (mCallbackStarted) {
+ MY_ASSERT_TRUE(mCallbackFinished);
+ }
+ MY_ASSERT_EQ(AAUDIO_OK, result);
+ printf("AAudioStream_close() returned %d\n", result);
+
+ MY_ASSERT_EQ(AAUDIO_ERROR_DISCONNECTED, mError.load());
+ if (mCallStopFromCallback) {
+ // Did calling stop() from callback fail? It should have.
+ MY_ASSERT_NE(AAUDIO_OK, mStopResult.load());
+ }
+
+ return true;
+ }
+
+private:
+ bool startStreamForStall(aaudio_direction_t direction,
+ aaudio_performance_mode_t perfMode) {
+ AAudioStreamBuilder* builder = nullptr;
+ aaudio_result_t result = AAUDIO_OK;
+
+ // Use an AAudioStreamBuilder to contain requested parameters.
+ result = AAudio_createStreamBuilder(&builder);
+ MY_ASSERT_EQ(AAUDIO_OK, result);
+
+ // Request stream properties.
+ AAudioStreamBuilder_setDirection(builder, direction);
+ AAudioStreamBuilder_setPerformanceMode(builder, perfMode);
+ AAudioStreamBuilder_setDataCallback(builder, s_myDataCallbackProc, this);
+ AAudioStreamBuilder_setErrorCallback(builder, s_myErrorCallbackProc, this);
+
+ // Create an AAudioStream using the Builder.
+ result = AAudioStreamBuilder_openStream(builder, &mStream);
+ AAudioStreamBuilder_delete(builder);
+ MY_ASSERT_EQ(AAUDIO_OK, result);
+
+ // Check to see what kind of stream we actually got.
+ int32_t deviceId = AAudioStream_getDeviceId(mStream);
+ aaudio_performance_mode_t
+ actualPerfMode = AAudioStream_getPerformanceMode(mStream);
+ printf("-------- opened: deviceId = %3d, perfMode = %d\n",
+ deviceId,
+ actualPerfMode);
+
+ // Start stream.
+ result = AAudioStream_requestStart(mStream);
+ MY_ASSERT_EQ(AAUDIO_OK, result);
+
+ return true;
+ }
+
+ bool waitForCallbackToStart() {
+ // Wait for callback to say it has been called.
+ int countDown = 10 * 1000 / kPollSleepMillis;
+ while (!mCallbackStarted && countDown > 0) {
+ if ((countDown % 5) == 0) {
+ printf("===== Please PLUG or UNPLUG headphones! ======= %d\n", countDown);
+ }
+ usleep(kPollSleepMillis * 1000);
+ countDown--;
+ }
+ MY_ASSERT_TRUE(countDown > 0);
+ MY_ASSERT_TRUE(mCallbackStarted);
+ return true;
+ }
+
+// Callback function that fills the audio output buffer.
+ static aaudio_data_callback_result_t s_myDataCallbackProc(
+ AAudioStream * /* stream */,
+ void * /* userData */,
+ void * /* audioData */,
+ int32_t /* numFrames */
+ ) {
+ return AAUDIO_CALLBACK_RESULT_CONTINUE;
+ }
+
+ static void s_myErrorCallbackProc(
+ AAudioStream * stream,
+ void *userData,
+ aaudio_result_t error) {
+ AudioEngine *engine = (AudioEngine *)userData;
+ engine->mError = error;
+ engine->mCallbackStarted = true;
+ usleep(kCallbackSleepMillis * 1000);
+ // it is illegal to call stop() from the callback. It should
+ // return an error and not hang.
+ if (engine->mCallStopFromCallback) {
+ engine->mStopResult = AAudioStream_requestStop(stream);
+ }
+ engine->mCallbackFinished = true;
+ }
+
+ AAudioStream* mStream = nullptr;
+
+ std::atomic<aaudio_result_t> mError{AAUDIO_OK}; // written by error callback
+ std::atomic<bool> mCallStopFromCallback{false};
+ std::atomic<bool> mCallbackStarted{false}; // written by error callback
+ std::atomic<bool> mCallbackFinished{false}; // written by error callback
+ std::atomic<aaudio_result_t> mStopResult{AAUDIO_OK};
+};
+
+int main(int, char **) {
+ // Parameters to test.
+ static aaudio_direction_t directions[] = {AAUDIO_DIRECTION_OUTPUT,
+ AAUDIO_DIRECTION_INPUT};
+ static aaudio_performance_mode_t perfModes[] =
+ {AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_PERFORMANCE_MODE_NONE};
+ static bool callStops[] = { false, true };
+
+ // Make printf print immediately so that debug info is not stuck
+ // in a buffer if we hang or crash.
+ setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
+
+ printf("Test Disconnect Race V1.0\n");
+ printf("\n");
+
+ for (auto callStop : callStops) {
+ for (auto direction : directions) {
+ for (auto perfMode : perfModes) {
+ AudioEngine engine;
+ engine.checkCloseJoins(direction, perfMode, callStop);
+ }
+ }
+ }
+
+ printf("Error Count = %d, %s\n", sErrorCount,
+ ((sErrorCount == 0) ? "PASS" : "FAIL"));
+}
diff --git a/media/libaaudio/tests/test_marshalling.cpp b/media/libaaudio/tests/test_marshalling.cpp
index c51fbce..49213dc 100644
--- a/media/libaaudio/tests/test_marshalling.cpp
+++ b/media/libaaudio/tests/test_marshalling.cpp
@@ -33,6 +33,29 @@
using namespace android;
using namespace aaudio;
+template<typename T>
+T copy(const T& object) {
+ return T(object);
+}
+
+template<>
+SharedMemoryParcelable copy<SharedMemoryParcelable>(const SharedMemoryParcelable& object) {
+ return object.dup();
+}
+
+template<typename T>
+void writeToParcel(const T& object, Parcel* parcel) {
+ copy(object).parcelable().writeToParcel(parcel);
+}
+
+template<typename T>
+T readFromParcel(const Parcel& parcel) {
+ using ParcelType = std::decay_t<decltype(std::declval<T>().parcelable())>;
+ ParcelType parcelable;
+ parcelable.readFromParcel(&parcel);
+ return T(std::move(parcelable));
+}
+
// Test adding one value.
TEST(test_marshalling, aaudio_one_read_write) {
Parcel parcel;
@@ -48,7 +71,6 @@
// Test SharedMemoryParcel.
TEST(test_marshalling, aaudio_shared_memory) {
SharedMemoryParcelable sharedMemoryA;
- SharedMemoryParcelable sharedMemoryB;
const size_t memSizeBytes = 840;
unique_fd fd(ashmem_create_region("TestMarshalling", memSizeBytes));
ASSERT_LE(0, fd);
@@ -63,10 +85,10 @@
Parcel parcel;
size_t pos = parcel.dataPosition();
- sharedMemoryA.writeToParcel(&parcel);
+ writeToParcel(sharedMemoryA, &parcel);
parcel.setDataPosition(pos);
- sharedMemoryB.readFromParcel(&parcel);
+ SharedMemoryParcelable sharedMemoryB = readFromParcel<SharedMemoryParcelable>(parcel);
EXPECT_EQ(sharedMemoryA.getSizeInBytes(), sharedMemoryB.getSizeInBytes());
// should see same value at two different addresses
@@ -81,7 +103,6 @@
TEST(test_marshalling, aaudio_shared_region) {
SharedMemoryParcelable sharedMemories[2];
SharedRegionParcelable sharedRegionA;
- SharedRegionParcelable sharedRegionB;
const size_t memSizeBytes = 840;
unique_fd fd(ashmem_create_region("TestMarshalling", memSizeBytes));
ASSERT_LE(0, fd);
@@ -97,10 +118,10 @@
Parcel parcel;
size_t pos = parcel.dataPosition();
- sharedRegionA.writeToParcel(&parcel);
+ writeToParcel(sharedRegionA, &parcel);
parcel.setDataPosition(pos);
- sharedRegionB.readFromParcel(&parcel);
+ SharedRegionParcelable sharedRegionB = readFromParcel<SharedRegionParcelable>(parcel);
// should see same value
void *region2;
@@ -113,7 +134,6 @@
TEST(test_marshalling, aaudio_ring_buffer_parcelable) {
SharedMemoryParcelable sharedMemories[2];
RingBufferParcelable ringBufferA;
- RingBufferParcelable ringBufferB;
const size_t bytesPerFrame = 8;
const size_t framesPerBurst = 32;
@@ -147,11 +167,11 @@
// write A to parcel
Parcel parcel;
size_t pos = parcel.dataPosition();
- ringBufferA.writeToParcel(&parcel);
+ writeToParcel(ringBufferA, &parcel);
// read B from parcel
parcel.setDataPosition(pos);
- ringBufferB.readFromParcel(&parcel);
+ RingBufferParcelable ringBufferB = readFromParcel<RingBufferParcelable>(parcel);
RingBufferDescriptor descriptorB;
EXPECT_EQ(AAUDIO_OK, ringBufferB.resolve(sharedMemories, &descriptorB));
diff --git a/media/libaaudio/tests/test_stop_hang.cpp b/media/libaaudio/tests/test_stop_hang.cpp
deleted file mode 100644
index 2397b6c..0000000
--- a/media/libaaudio/tests/test_stop_hang.cpp
+++ /dev/null
@@ -1,159 +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.
- */
-
-/**
- * Return stop from the callback
- * and then close the stream immediately.
- */
-
-#include <atomic>
-#include <mutex>
-#include <stdio.h>
-#include <thread>
-#include <unistd.h>
-
-#include <aaudio/AAudio.h>
-
-#define DURATION_SECONDS 5
-
-struct AudioEngine {
- AAudioStreamBuilder *builder = nullptr;
- AAudioStream *stream = nullptr;
- std::thread *thread = nullptr;
-
- std::atomic<bool> started{false};
- std::mutex doneLock; // Use a mutex so we can sleep on it while join()ing.
- std::atomic<bool> done{false};
-
- aaudio_result_t join() {
- aaudio_result_t result = AAUDIO_ERROR_INVALID_STATE;
- if (stream != nullptr) {
- while (true) {
- {
- // Will block if the thread is running.
- // This mutex is used to close() immediately after the callback returns
- // and before the requestStop() is called.
- std::lock_guard<std::mutex> lock(doneLock);
- if (done) break;
- }
- printf("join() got mutex but stream not done!");
- usleep(10 * 1000); // sleep then check again
- }
- result = AAudioStream_close(stream);
- stream = nullptr;
- }
- return result;
- }
-};
-
-// Callback function that fills the audio output buffer.
-static aaudio_data_callback_result_t s_myDataCallbackProc(
- AAudioStream *stream,
- void *userData,
- void *audioData,
- int32_t numFrames
-) {
- (void) stream;
- (void) audioData;
- (void) numFrames;
- AudioEngine *engine = (struct AudioEngine *)userData;
- std::lock_guard<std::mutex> lock(engine->doneLock);
- engine->started = true;
- usleep(DURATION_SECONDS * 1000 * 1000); // Mimic SynthMark procedure.
- engine->done = true;
- return AAUDIO_CALLBACK_RESULT_STOP;
-}
-
-static void s_myErrorCallbackProc(
- AAudioStream *stream __unused,
- void *userData __unused,
- aaudio_result_t error) {
- printf("%s() - error = %d\n", __func__, error);
-}
-
-static aaudio_result_t s_OpenAudioStream(struct AudioEngine *engine) {
- // Use an AAudioStreamBuilder to contain requested parameters.
- aaudio_result_t result = AAudio_createStreamBuilder(&engine->builder);
- if (result != AAUDIO_OK) {
- printf("AAudio_createStreamBuilder returned %s",
- AAudio_convertResultToText(result));
- return result;
- }
-
- // Request stream properties.
- AAudioStreamBuilder_setPerformanceMode(engine->builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
- AAudioStreamBuilder_setDataCallback(engine->builder, s_myDataCallbackProc, engine);
- AAudioStreamBuilder_setErrorCallback(engine->builder, s_myErrorCallbackProc, engine);
-
- // Create an AAudioStream using the Builder.
- result = AAudioStreamBuilder_openStream(engine->builder, &engine->stream);
- if (result != AAUDIO_OK) {
- printf("AAudioStreamBuilder_openStream returned %s",
- AAudio_convertResultToText(result));
- return result;
- }
-
- return result;
-}
-
-int main(int argc, char **argv) {
- (void) argc;
- (void) argv;
- struct AudioEngine engine;
- aaudio_result_t result = AAUDIO_OK;
- int errorCount = 0;
-
- // Make printf print immediately so that debug info is not stuck
- // in a buffer if we hang or crash.
- setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
-
- printf("Test Return Stop Hang V1.0\n");
-
- result = s_OpenAudioStream(&engine);
- if (result != AAUDIO_OK) {
- printf("s_OpenAudioStream returned %s\n",
- AAudio_convertResultToText(result));
- errorCount++;
- }
-
- // Check to see what kind of stream we actually got.
- int32_t deviceId = AAudioStream_getDeviceId(engine.stream);
- aaudio_performance_mode_t actualPerfMode = AAudioStream_getPerformanceMode(engine.stream);
- printf("-------- opened: deviceId = %3d, perfMode = %d\n", deviceId, actualPerfMode);
-
- // Start stream.
- result = AAudioStream_requestStart(engine.stream);
- printf("AAudioStream_requestStart() returned %d >>>>>>>>>>>>>>>>>>>>>>\n", result);
- if (result != AAUDIO_OK) {
- errorCount++;
- } else {
- int counter = 0;
- while (!engine.started) {
- printf("Waiting for stream to start, %d\n", counter++);
- usleep(5 * 1000);
- }
- printf("You should see more messages %d seconds after this. If not then the test failed!\n",
- DURATION_SECONDS);
- result = engine.join(); // This might hang!
- AAudioStreamBuilder_delete(engine.builder);
- engine.builder = nullptr;
- }
-
- printf("aaudio result = %d = %s\n", result, AAudio_convertResultToText(result));
- printf("test %s\n", errorCount ? "FAILED" : "PASSED");
-
- return errorCount ? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/media/libaudioclient/AidlConversion.cpp b/media/libaudioclient/AidlConversion.cpp
new file mode 100644
index 0000000..c77aeeb
--- /dev/null
+++ b/media/libaudioclient/AidlConversion.cpp
@@ -0,0 +1,2326 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AidlConversion"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "media/AidlConversion.h"
+
+#include <media/ShmemCompat.h>
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Utilities
+
+namespace android {
+
+using base::unexpected;
+
+namespace {
+
+enum class Direction {
+ INPUT, OUTPUT
+};
+
+ConversionResult<Direction> direction(media::AudioPortRole role, media::AudioPortType type) {
+ switch (type) {
+ case media::AudioPortType::NONE:
+ case media::AudioPortType::SESSION:
+ break; // must be listed -Werror,-Wswitch
+ case media::AudioPortType::DEVICE:
+ switch (role) {
+ case media::AudioPortRole::NONE:
+ break; // must be listed -Werror,-Wswitch
+ case media::AudioPortRole::SOURCE:
+ return Direction::INPUT;
+ case media::AudioPortRole::SINK:
+ return Direction::OUTPUT;
+ }
+ break;
+ case media::AudioPortType::MIX:
+ switch (role) {
+ case media::AudioPortRole::NONE:
+ break; // must be listed -Werror,-Wswitch
+ case media::AudioPortRole::SOURCE:
+ return Direction::OUTPUT;
+ case media::AudioPortRole::SINK:
+ return Direction::INPUT;
+ }
+ break;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<Direction> direction(audio_port_role_t role, audio_port_type_t type) {
+ switch (type) {
+ case AUDIO_PORT_TYPE_NONE:
+ case AUDIO_PORT_TYPE_SESSION:
+ break; // must be listed -Werror,-Wswitch
+ case AUDIO_PORT_TYPE_DEVICE:
+ switch (role) {
+ case AUDIO_PORT_ROLE_NONE:
+ break; // must be listed -Werror,-Wswitch
+ case AUDIO_PORT_ROLE_SOURCE:
+ return Direction::INPUT;
+ case AUDIO_PORT_ROLE_SINK:
+ return Direction::OUTPUT;
+ }
+ break;
+ case AUDIO_PORT_TYPE_MIX:
+ switch (role) {
+ case AUDIO_PORT_ROLE_NONE:
+ break; // must be listed -Werror,-Wswitch
+ case AUDIO_PORT_ROLE_SOURCE:
+ return Direction::OUTPUT;
+ case AUDIO_PORT_ROLE_SINK:
+ return Direction::INPUT;
+ }
+ break;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Converters
+
+status_t aidl2legacy_string(std::string_view aidl, char* dest, size_t maxSize) {
+ if (aidl.size() > maxSize - 1) {
+ return BAD_VALUE;
+ }
+ aidl.copy(dest, aidl.size());
+ dest[aidl.size()] = '\0';
+ return OK;
+}
+
+ConversionResult<std::string> legacy2aidl_string(const char* legacy, size_t maxSize) {
+ if (legacy == nullptr) {
+ return unexpected(BAD_VALUE);
+ }
+ if (strnlen(legacy, maxSize) == maxSize) {
+ // No null-terminator.
+ return unexpected(BAD_VALUE);
+ }
+ return std::string(legacy);
+}
+
+ConversionResult<audio_module_handle_t> aidl2legacy_int32_t_audio_module_handle_t(int32_t aidl) {
+ return convertReinterpret<audio_module_handle_t>(aidl);
+}
+
+ConversionResult<int32_t> legacy2aidl_audio_module_handle_t_int32_t(audio_module_handle_t legacy) {
+ return convertReinterpret<int32_t>(legacy);
+}
+
+ConversionResult<audio_io_handle_t> aidl2legacy_int32_t_audio_io_handle_t(int32_t aidl) {
+ return convertReinterpret<audio_io_handle_t>(aidl);
+}
+
+ConversionResult<int32_t> legacy2aidl_audio_io_handle_t_int32_t(audio_io_handle_t legacy) {
+ return convertReinterpret<int32_t>(legacy);
+}
+
+ConversionResult<audio_port_handle_t> aidl2legacy_int32_t_audio_port_handle_t(int32_t aidl) {
+ return convertReinterpret<audio_port_handle_t>(aidl);
+}
+
+ConversionResult<int32_t> legacy2aidl_audio_port_handle_t_int32_t(audio_port_handle_t legacy) {
+ return convertReinterpret<int32_t>(legacy);
+}
+
+ConversionResult<audio_patch_handle_t> aidl2legacy_int32_t_audio_patch_handle_t(int32_t aidl) {
+ return convertReinterpret<audio_patch_handle_t>(aidl);
+}
+
+ConversionResult<int32_t> legacy2aidl_audio_patch_handle_t_int32_t(audio_patch_handle_t legacy) {
+ return convertReinterpret<int32_t>(legacy);
+}
+
+ConversionResult<audio_unique_id_t> aidl2legacy_int32_t_audio_unique_id_t(int32_t aidl) {
+ return convertReinterpret<audio_unique_id_t>(aidl);
+}
+
+ConversionResult<int32_t> legacy2aidl_audio_unique_id_t_int32_t(audio_unique_id_t legacy) {
+ return convertReinterpret<int32_t>(legacy);
+}
+
+ConversionResult<audio_hw_sync_t> aidl2legacy_int32_t_audio_hw_sync_t(int32_t aidl) {
+ return convertReinterpret<audio_hw_sync_t>(aidl);
+}
+
+ConversionResult<int32_t> legacy2aidl_audio_hw_sync_t_int32_t(audio_hw_sync_t legacy) {
+ return convertReinterpret<int32_t>(legacy);
+}
+
+ConversionResult<pid_t> aidl2legacy_int32_t_pid_t(int32_t aidl) {
+ return convertReinterpret<pid_t>(aidl);
+}
+
+ConversionResult<int32_t> legacy2aidl_pid_t_int32_t(pid_t legacy) {
+ return convertReinterpret<int32_t>(legacy);
+}
+
+ConversionResult<uid_t> aidl2legacy_int32_t_uid_t(int32_t aidl) {
+ return convertReinterpret<uid_t>(aidl);
+}
+
+ConversionResult<int32_t> legacy2aidl_uid_t_int32_t(uid_t legacy) {
+ return convertReinterpret<int32_t>(legacy);
+}
+
+ConversionResult<String16> aidl2legacy_string_view_String16(std::string_view aidl) {
+ return String16(aidl.data(), aidl.size());
+}
+
+ConversionResult<std::string> legacy2aidl_String16_string(const String16& legacy) {
+ return std::string(String8(legacy).c_str());
+}
+
+// TODO b/182392769: create an optional -> optional util
+ConversionResult<std::optional<String16>>
+aidl2legacy_optional_string_view_optional_String16(std::optional<std::string_view> aidl) {
+ if (!aidl.has_value()) {
+ return std::nullopt;
+ }
+ ConversionResult<String16> conversion =
+ VALUE_OR_RETURN(aidl2legacy_string_view_String16(aidl.value()));
+ return conversion.value();
+}
+
+ConversionResult<std::optional<std::string_view>>
+legacy2aidl_optional_String16_optional_string(std::optional<String16> legacy) {
+ if (!legacy.has_value()) {
+ return std::nullopt;
+ }
+ ConversionResult<std::string> conversion =
+ VALUE_OR_RETURN(legacy2aidl_String16_string(legacy.value()));
+ return conversion.value();
+}
+
+ConversionResult<String8> aidl2legacy_string_view_String8(std::string_view aidl) {
+ return String8(aidl.data(), aidl.size());
+}
+
+ConversionResult<std::string> legacy2aidl_String8_string(const String8& legacy) {
+ return std::string(legacy.c_str());
+}
+
+// The legacy enum is unnamed. Thus, we use int32_t.
+ConversionResult<int32_t> aidl2legacy_AudioPortConfigType_int32_t(
+ media::AudioPortConfigType aidl) {
+ switch (aidl) {
+ case media::AudioPortConfigType::SAMPLE_RATE:
+ return AUDIO_PORT_CONFIG_SAMPLE_RATE;
+ case media::AudioPortConfigType::CHANNEL_MASK:
+ return AUDIO_PORT_CONFIG_CHANNEL_MASK;
+ case media::AudioPortConfigType::FORMAT:
+ return AUDIO_PORT_CONFIG_FORMAT;
+ case media::AudioPortConfigType::GAIN:
+ return AUDIO_PORT_CONFIG_GAIN;
+ case media::AudioPortConfigType::FLAGS:
+ return AUDIO_PORT_CONFIG_FLAGS;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+// The legacy enum is unnamed. Thus, we use int32_t.
+ConversionResult<media::AudioPortConfigType> legacy2aidl_int32_t_AudioPortConfigType(
+ int32_t legacy) {
+ switch (legacy) {
+ case AUDIO_PORT_CONFIG_SAMPLE_RATE:
+ return media::AudioPortConfigType::SAMPLE_RATE;
+ case AUDIO_PORT_CONFIG_CHANNEL_MASK:
+ return media::AudioPortConfigType::CHANNEL_MASK;
+ case AUDIO_PORT_CONFIG_FORMAT:
+ return media::AudioPortConfigType::FORMAT;
+ case AUDIO_PORT_CONFIG_GAIN:
+ return media::AudioPortConfigType::GAIN;
+ case AUDIO_PORT_CONFIG_FLAGS:
+ return media::AudioPortConfigType::FLAGS;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<unsigned int> aidl2legacy_int32_t_config_mask(int32_t aidl) {
+ return convertBitmask<unsigned int, int32_t, int, media::AudioPortConfigType>(
+ aidl, aidl2legacy_AudioPortConfigType_int32_t,
+ // AudioPortConfigType enum is index-based.
+ indexToEnum_index<media::AudioPortConfigType>,
+ // AUDIO_PORT_CONFIG_* flags are mask-based.
+ enumToMask_bitmask<unsigned int, int>);
+}
+
+ConversionResult<int32_t> legacy2aidl_config_mask_int32_t(unsigned int legacy) {
+ return convertBitmask<int32_t, unsigned int, media::AudioPortConfigType, int>(
+ legacy, legacy2aidl_int32_t_AudioPortConfigType,
+ // AUDIO_PORT_CONFIG_* flags are mask-based.
+ indexToEnum_bitmask<unsigned>,
+ // AudioPortConfigType enum is index-based.
+ enumToMask_index<int32_t, media::AudioPortConfigType>);
+}
+
+ConversionResult<audio_channel_mask_t> aidl2legacy_int32_t_audio_channel_mask_t(int32_t aidl) {
+ // TODO(ytai): should we convert bit-by-bit?
+ // One problem here is that the representation is both opaque and is different based on the
+ // context (input vs. output). Can determine based on type and role, as per useInChannelMask().
+ return convertReinterpret<audio_channel_mask_t>(aidl);
+}
+
+ConversionResult<int32_t> legacy2aidl_audio_channel_mask_t_int32_t(audio_channel_mask_t legacy) {
+ // TODO(ytai): should we convert bit-by-bit?
+ // One problem here is that the representation is both opaque and is different based on the
+ // context (input vs. output). Can determine based on type and role, as per useInChannelMask().
+ return convertReinterpret<int32_t>(legacy);
+}
+
+ConversionResult<audio_io_config_event> aidl2legacy_AudioIoConfigEvent_audio_io_config_event(
+ media::AudioIoConfigEvent aidl) {
+ switch (aidl) {
+ case media::AudioIoConfigEvent::OUTPUT_REGISTERED:
+ return AUDIO_OUTPUT_REGISTERED;
+ case media::AudioIoConfigEvent::OUTPUT_OPENED:
+ return AUDIO_OUTPUT_OPENED;
+ case media::AudioIoConfigEvent::OUTPUT_CLOSED:
+ return AUDIO_OUTPUT_CLOSED;
+ case media::AudioIoConfigEvent::OUTPUT_CONFIG_CHANGED:
+ return AUDIO_OUTPUT_CONFIG_CHANGED;
+ case media::AudioIoConfigEvent::INPUT_REGISTERED:
+ return AUDIO_INPUT_REGISTERED;
+ case media::AudioIoConfigEvent::INPUT_OPENED:
+ return AUDIO_INPUT_OPENED;
+ case media::AudioIoConfigEvent::INPUT_CLOSED:
+ return AUDIO_INPUT_CLOSED;
+ case media::AudioIoConfigEvent::INPUT_CONFIG_CHANGED:
+ return AUDIO_INPUT_CONFIG_CHANGED;
+ case media::AudioIoConfigEvent::CLIENT_STARTED:
+ return AUDIO_CLIENT_STARTED;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioIoConfigEvent> legacy2aidl_audio_io_config_event_AudioIoConfigEvent(
+ audio_io_config_event legacy) {
+ switch (legacy) {
+ case AUDIO_OUTPUT_REGISTERED:
+ return media::AudioIoConfigEvent::OUTPUT_REGISTERED;
+ case AUDIO_OUTPUT_OPENED:
+ return media::AudioIoConfigEvent::OUTPUT_OPENED;
+ case AUDIO_OUTPUT_CLOSED:
+ return media::AudioIoConfigEvent::OUTPUT_CLOSED;
+ case AUDIO_OUTPUT_CONFIG_CHANGED:
+ return media::AudioIoConfigEvent::OUTPUT_CONFIG_CHANGED;
+ case AUDIO_INPUT_REGISTERED:
+ return media::AudioIoConfigEvent::INPUT_REGISTERED;
+ case AUDIO_INPUT_OPENED:
+ return media::AudioIoConfigEvent::INPUT_OPENED;
+ case AUDIO_INPUT_CLOSED:
+ return media::AudioIoConfigEvent::INPUT_CLOSED;
+ case AUDIO_INPUT_CONFIG_CHANGED:
+ return media::AudioIoConfigEvent::INPUT_CONFIG_CHANGED;
+ case AUDIO_CLIENT_STARTED:
+ return media::AudioIoConfigEvent::CLIENT_STARTED;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_port_role_t> aidl2legacy_AudioPortRole_audio_port_role_t(
+ media::AudioPortRole aidl) {
+ switch (aidl) {
+ case media::AudioPortRole::NONE:
+ return AUDIO_PORT_ROLE_NONE;
+ case media::AudioPortRole::SOURCE:
+ return AUDIO_PORT_ROLE_SOURCE;
+ case media::AudioPortRole::SINK:
+ return AUDIO_PORT_ROLE_SINK;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioPortRole> legacy2aidl_audio_port_role_t_AudioPortRole(
+ audio_port_role_t legacy) {
+ switch (legacy) {
+ case AUDIO_PORT_ROLE_NONE:
+ return media::AudioPortRole::NONE;
+ case AUDIO_PORT_ROLE_SOURCE:
+ return media::AudioPortRole::SOURCE;
+ case AUDIO_PORT_ROLE_SINK:
+ return media::AudioPortRole::SINK;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_port_type_t> aidl2legacy_AudioPortType_audio_port_type_t(
+ media::AudioPortType aidl) {
+ switch (aidl) {
+ case media::AudioPortType::NONE:
+ return AUDIO_PORT_TYPE_NONE;
+ case media::AudioPortType::DEVICE:
+ return AUDIO_PORT_TYPE_DEVICE;
+ case media::AudioPortType::MIX:
+ return AUDIO_PORT_TYPE_MIX;
+ case media::AudioPortType::SESSION:
+ return AUDIO_PORT_TYPE_SESSION;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioPortType> legacy2aidl_audio_port_type_t_AudioPortType(
+ audio_port_type_t legacy) {
+ switch (legacy) {
+ case AUDIO_PORT_TYPE_NONE:
+ return media::AudioPortType::NONE;
+ case AUDIO_PORT_TYPE_DEVICE:
+ return media::AudioPortType::DEVICE;
+ case AUDIO_PORT_TYPE_MIX:
+ return media::AudioPortType::MIX;
+ case AUDIO_PORT_TYPE_SESSION:
+ return media::AudioPortType::SESSION;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_format_t> aidl2legacy_AudioFormat_audio_format_t(
+ media::audio::common::AudioFormat aidl) {
+ // This relies on AudioFormat being kept in sync with audio_format_t.
+ static_assert(sizeof(media::audio::common::AudioFormat) == sizeof(audio_format_t));
+ return static_cast<audio_format_t>(aidl);
+}
+
+ConversionResult<media::audio::common::AudioFormat> legacy2aidl_audio_format_t_AudioFormat(
+ audio_format_t legacy) {
+ // This relies on AudioFormat being kept in sync with audio_format_t.
+ static_assert(sizeof(media::audio::common::AudioFormat) == sizeof(audio_format_t));
+ return static_cast<media::audio::common::AudioFormat>(legacy);
+}
+
+ConversionResult<audio_gain_mode_t> aidl2legacy_AudioGainMode_audio_gain_mode_t(media::AudioGainMode aidl) {
+ switch (aidl) {
+ case media::AudioGainMode::JOINT:
+ return AUDIO_GAIN_MODE_JOINT;
+ case media::AudioGainMode::CHANNELS:
+ return AUDIO_GAIN_MODE_CHANNELS;
+ case media::AudioGainMode::RAMP:
+ return AUDIO_GAIN_MODE_RAMP;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioGainMode> legacy2aidl_audio_gain_mode_t_AudioGainMode(audio_gain_mode_t legacy) {
+ switch (legacy) {
+ case AUDIO_GAIN_MODE_JOINT:
+ return media::AudioGainMode::JOINT;
+ case AUDIO_GAIN_MODE_CHANNELS:
+ return media::AudioGainMode::CHANNELS;
+ case AUDIO_GAIN_MODE_RAMP:
+ return media::AudioGainMode::RAMP;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_gain_mode_t> aidl2legacy_int32_t_audio_gain_mode_t_mask(int32_t aidl) {
+ return convertBitmask<audio_gain_mode_t, int32_t, audio_gain_mode_t, media::AudioGainMode>(
+ aidl, aidl2legacy_AudioGainMode_audio_gain_mode_t,
+ // AudioGainMode is index-based.
+ indexToEnum_index<media::AudioGainMode>,
+ // AUDIO_GAIN_MODE_* constants are mask-based.
+ enumToMask_bitmask<audio_gain_mode_t, audio_gain_mode_t>);
+}
+
+ConversionResult<int32_t> legacy2aidl_audio_gain_mode_t_int32_t_mask(audio_gain_mode_t legacy) {
+ return convertBitmask<int32_t, audio_gain_mode_t, media::AudioGainMode, audio_gain_mode_t>(
+ legacy, legacy2aidl_audio_gain_mode_t_AudioGainMode,
+ // AUDIO_GAIN_MODE_* constants are mask-based.
+ indexToEnum_bitmask<audio_gain_mode_t>,
+ // AudioGainMode is index-based.
+ enumToMask_index<int32_t, media::AudioGainMode>);
+}
+
+ConversionResult<audio_devices_t> aidl2legacy_int32_t_audio_devices_t(int32_t aidl) {
+ // TODO(ytai): bitfield?
+ return convertReinterpret<audio_devices_t>(aidl);
+}
+
+ConversionResult<int32_t> legacy2aidl_audio_devices_t_int32_t(audio_devices_t legacy) {
+ // TODO(ytai): bitfield?
+ return convertReinterpret<int32_t>(legacy);
+}
+
+ConversionResult<audio_gain_config> aidl2legacy_AudioGainConfig_audio_gain_config(
+ const media::AudioGainConfig& aidl, media::AudioPortRole role, media::AudioPortType type) {
+ audio_gain_config legacy;
+ legacy.index = VALUE_OR_RETURN(convertIntegral<int>(aidl.index));
+ legacy.mode = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_gain_mode_t_mask(aidl.mode));
+ legacy.channel_mask =
+ VALUE_OR_RETURN(aidl2legacy_int32_t_audio_channel_mask_t(aidl.channelMask));
+ const bool isInput = VALUE_OR_RETURN(direction(role, type)) == Direction::INPUT;
+ const bool isJoint = bitmaskIsSet(aidl.mode, media::AudioGainMode::JOINT);
+ size_t numValues = isJoint ? 1
+ : isInput ? audio_channel_count_from_in_mask(legacy.channel_mask)
+ : audio_channel_count_from_out_mask(legacy.channel_mask);
+ if (aidl.values.size() != numValues || aidl.values.size() > std::size(legacy.values)) {
+ return unexpected(BAD_VALUE);
+ }
+ for (size_t i = 0; i < numValues; ++i) {
+ legacy.values[i] = VALUE_OR_RETURN(convertIntegral<int>(aidl.values[i]));
+ }
+ legacy.ramp_duration_ms = VALUE_OR_RETURN(convertIntegral<unsigned int>(aidl.rampDurationMs));
+ return legacy;
+}
+
+ConversionResult<media::AudioGainConfig> legacy2aidl_audio_gain_config_AudioGainConfig(
+ const audio_gain_config& legacy, audio_port_role_t role, audio_port_type_t type) {
+ media::AudioGainConfig aidl;
+ aidl.index = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.index));
+ aidl.mode = VALUE_OR_RETURN(legacy2aidl_audio_gain_mode_t_int32_t_mask(legacy.mode));
+ aidl.channelMask =
+ VALUE_OR_RETURN(legacy2aidl_audio_channel_mask_t_int32_t(legacy.channel_mask));
+ const bool isInput = VALUE_OR_RETURN(direction(role, type)) == Direction::INPUT;
+ const bool isJoint = (legacy.mode & AUDIO_GAIN_MODE_JOINT) != 0;
+ size_t numValues = isJoint ? 1
+ : isInput ? audio_channel_count_from_in_mask(legacy.channel_mask)
+ : audio_channel_count_from_out_mask(legacy.channel_mask);
+ aidl.values.resize(numValues);
+ for (size_t i = 0; i < numValues; ++i) {
+ aidl.values[i] = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.values[i]));
+ }
+ aidl.rampDurationMs = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.ramp_duration_ms));
+ return aidl;
+}
+
+ConversionResult<audio_input_flags_t> aidl2legacy_AudioInputFlags_audio_input_flags_t(
+ media::AudioInputFlags aidl) {
+ switch (aidl) {
+ case media::AudioInputFlags::FAST:
+ return AUDIO_INPUT_FLAG_FAST;
+ case media::AudioInputFlags::HW_HOTWORD:
+ return AUDIO_INPUT_FLAG_HW_HOTWORD;
+ case media::AudioInputFlags::RAW:
+ return AUDIO_INPUT_FLAG_RAW;
+ case media::AudioInputFlags::SYNC:
+ return AUDIO_INPUT_FLAG_SYNC;
+ case media::AudioInputFlags::MMAP_NOIRQ:
+ return AUDIO_INPUT_FLAG_MMAP_NOIRQ;
+ case media::AudioInputFlags::VOIP_TX:
+ return AUDIO_INPUT_FLAG_VOIP_TX;
+ case media::AudioInputFlags::HW_AV_SYNC:
+ return AUDIO_INPUT_FLAG_HW_AV_SYNC;
+ case media::AudioInputFlags::DIRECT:
+ return AUDIO_INPUT_FLAG_DIRECT;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioInputFlags> legacy2aidl_audio_input_flags_t_AudioInputFlags(
+ audio_input_flags_t legacy) {
+ switch (legacy) {
+ case AUDIO_INPUT_FLAG_NONE:
+ break; // shouldn't get here. must be listed -Werror,-Wswitch
+ case AUDIO_INPUT_FLAG_FAST:
+ return media::AudioInputFlags::FAST;
+ case AUDIO_INPUT_FLAG_HW_HOTWORD:
+ return media::AudioInputFlags::HW_HOTWORD;
+ case AUDIO_INPUT_FLAG_RAW:
+ return media::AudioInputFlags::RAW;
+ case AUDIO_INPUT_FLAG_SYNC:
+ return media::AudioInputFlags::SYNC;
+ case AUDIO_INPUT_FLAG_MMAP_NOIRQ:
+ return media::AudioInputFlags::MMAP_NOIRQ;
+ case AUDIO_INPUT_FLAG_VOIP_TX:
+ return media::AudioInputFlags::VOIP_TX;
+ case AUDIO_INPUT_FLAG_HW_AV_SYNC:
+ return media::AudioInputFlags::HW_AV_SYNC;
+ case AUDIO_INPUT_FLAG_DIRECT:
+ return media::AudioInputFlags::DIRECT;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_output_flags_t> aidl2legacy_AudioOutputFlags_audio_output_flags_t(
+ media::AudioOutputFlags aidl) {
+ switch (aidl) {
+ case media::AudioOutputFlags::DIRECT:
+ return AUDIO_OUTPUT_FLAG_DIRECT;
+ case media::AudioOutputFlags::PRIMARY:
+ return AUDIO_OUTPUT_FLAG_PRIMARY;
+ case media::AudioOutputFlags::FAST:
+ return AUDIO_OUTPUT_FLAG_FAST;
+ case media::AudioOutputFlags::DEEP_BUFFER:
+ return AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
+ case media::AudioOutputFlags::COMPRESS_OFFLOAD:
+ return AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
+ case media::AudioOutputFlags::NON_BLOCKING:
+ return AUDIO_OUTPUT_FLAG_NON_BLOCKING;
+ case media::AudioOutputFlags::HW_AV_SYNC:
+ return AUDIO_OUTPUT_FLAG_HW_AV_SYNC;
+ case media::AudioOutputFlags::TTS:
+ return AUDIO_OUTPUT_FLAG_TTS;
+ case media::AudioOutputFlags::RAW:
+ return AUDIO_OUTPUT_FLAG_RAW;
+ case media::AudioOutputFlags::SYNC:
+ return AUDIO_OUTPUT_FLAG_SYNC;
+ case media::AudioOutputFlags::IEC958_NONAUDIO:
+ return AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO;
+ case media::AudioOutputFlags::DIRECT_PCM:
+ return AUDIO_OUTPUT_FLAG_DIRECT_PCM;
+ case media::AudioOutputFlags::MMAP_NOIRQ:
+ return AUDIO_OUTPUT_FLAG_MMAP_NOIRQ;
+ case media::AudioOutputFlags::VOIP_RX:
+ return AUDIO_OUTPUT_FLAG_VOIP_RX;
+ case media::AudioOutputFlags::INCALL_MUSIC:
+ return AUDIO_OUTPUT_FLAG_INCALL_MUSIC;
+ case media::AudioOutputFlags::GAPLESS_OFFLOAD:
+ return AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioOutputFlags> legacy2aidl_audio_output_flags_t_AudioOutputFlags(
+ audio_output_flags_t legacy) {
+ switch (legacy) {
+ case AUDIO_OUTPUT_FLAG_NONE:
+ break; // shouldn't get here. must be listed -Werror,-Wswitch
+ case AUDIO_OUTPUT_FLAG_DIRECT:
+ return media::AudioOutputFlags::DIRECT;
+ case AUDIO_OUTPUT_FLAG_PRIMARY:
+ return media::AudioOutputFlags::PRIMARY;
+ case AUDIO_OUTPUT_FLAG_FAST:
+ return media::AudioOutputFlags::FAST;
+ case AUDIO_OUTPUT_FLAG_DEEP_BUFFER:
+ return media::AudioOutputFlags::DEEP_BUFFER;
+ case AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD:
+ return media::AudioOutputFlags::COMPRESS_OFFLOAD;
+ case AUDIO_OUTPUT_FLAG_NON_BLOCKING:
+ return media::AudioOutputFlags::NON_BLOCKING;
+ case AUDIO_OUTPUT_FLAG_HW_AV_SYNC:
+ return media::AudioOutputFlags::HW_AV_SYNC;
+ case AUDIO_OUTPUT_FLAG_TTS:
+ return media::AudioOutputFlags::TTS;
+ case AUDIO_OUTPUT_FLAG_RAW:
+ return media::AudioOutputFlags::RAW;
+ case AUDIO_OUTPUT_FLAG_SYNC:
+ return media::AudioOutputFlags::SYNC;
+ case AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO:
+ return media::AudioOutputFlags::IEC958_NONAUDIO;
+ case AUDIO_OUTPUT_FLAG_DIRECT_PCM:
+ return media::AudioOutputFlags::DIRECT_PCM;
+ case AUDIO_OUTPUT_FLAG_MMAP_NOIRQ:
+ return media::AudioOutputFlags::MMAP_NOIRQ;
+ case AUDIO_OUTPUT_FLAG_VOIP_RX:
+ return media::AudioOutputFlags::VOIP_RX;
+ case AUDIO_OUTPUT_FLAG_INCALL_MUSIC:
+ return media::AudioOutputFlags::INCALL_MUSIC;
+ case AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD:
+ return media::AudioOutputFlags::GAPLESS_OFFLOAD;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_input_flags_t> aidl2legacy_int32_t_audio_input_flags_t_mask(
+ int32_t aidl) {
+ using LegacyMask = std::underlying_type_t<audio_input_flags_t>;
+
+ LegacyMask converted = VALUE_OR_RETURN(
+ (convertBitmask<LegacyMask, int32_t, audio_input_flags_t, media::AudioInputFlags>(
+ aidl, aidl2legacy_AudioInputFlags_audio_input_flags_t,
+ indexToEnum_index<media::AudioInputFlags>,
+ enumToMask_bitmask<LegacyMask, audio_input_flags_t>)));
+ return static_cast<audio_input_flags_t>(converted);
+}
+
+ConversionResult<int32_t> legacy2aidl_audio_input_flags_t_int32_t_mask(
+ audio_input_flags_t legacy) {
+ using LegacyMask = std::underlying_type_t<audio_input_flags_t>;
+
+ LegacyMask legacyMask = static_cast<LegacyMask>(legacy);
+ return convertBitmask<int32_t, LegacyMask, media::AudioInputFlags, audio_input_flags_t>(
+ legacyMask, legacy2aidl_audio_input_flags_t_AudioInputFlags,
+ indexToEnum_bitmask<audio_input_flags_t>,
+ enumToMask_index<int32_t, media::AudioInputFlags>);
+}
+
+ConversionResult<audio_output_flags_t> aidl2legacy_int32_t_audio_output_flags_t_mask(
+ int32_t aidl) {
+ return convertBitmask<audio_output_flags_t,
+ int32_t,
+ audio_output_flags_t,
+ media::AudioOutputFlags>(
+ aidl, aidl2legacy_AudioOutputFlags_audio_output_flags_t,
+ indexToEnum_index<media::AudioOutputFlags>,
+ enumToMask_bitmask<audio_output_flags_t, audio_output_flags_t>);
+}
+
+ConversionResult<int32_t> legacy2aidl_audio_output_flags_t_int32_t_mask(
+ audio_output_flags_t legacy) {
+ using LegacyMask = std::underlying_type_t<audio_output_flags_t>;
+
+ LegacyMask legacyMask = static_cast<LegacyMask>(legacy);
+ return convertBitmask<int32_t, LegacyMask, media::AudioOutputFlags, audio_output_flags_t>(
+ legacyMask, legacy2aidl_audio_output_flags_t_AudioOutputFlags,
+ indexToEnum_bitmask<audio_output_flags_t>,
+ enumToMask_index<int32_t, media::AudioOutputFlags>);
+}
+
+ConversionResult<audio_io_flags> aidl2legacy_AudioIoFlags_audio_io_flags(
+ const media::AudioIoFlags& aidl, media::AudioPortRole role, media::AudioPortType type) {
+ audio_io_flags legacy;
+ Direction dir = VALUE_OR_RETURN(direction(role, type));
+ switch (dir) {
+ case Direction::INPUT: {
+ legacy.input = VALUE_OR_RETURN(
+ aidl2legacy_int32_t_audio_input_flags_t_mask(
+ VALUE_OR_RETURN(UNION_GET(aidl, input))));
+ }
+ break;
+
+ case Direction::OUTPUT: {
+ legacy.output = VALUE_OR_RETURN(
+ aidl2legacy_int32_t_audio_output_flags_t_mask(
+ VALUE_OR_RETURN(UNION_GET(aidl, output))));
+ }
+ break;
+ }
+
+ return legacy;
+}
+
+ConversionResult<media::AudioIoFlags> legacy2aidl_audio_io_flags_AudioIoFlags(
+ const audio_io_flags& legacy, audio_port_role_t role, audio_port_type_t type) {
+ media::AudioIoFlags aidl;
+
+ Direction dir = VALUE_OR_RETURN(direction(role, type));
+ switch (dir) {
+ case Direction::INPUT:
+ UNION_SET(aidl, input,
+ VALUE_OR_RETURN(legacy2aidl_audio_input_flags_t_int32_t_mask(
+ legacy.input)));
+ break;
+ case Direction::OUTPUT:
+ UNION_SET(aidl, output,
+ VALUE_OR_RETURN(legacy2aidl_audio_output_flags_t_int32_t_mask(
+ legacy.output)));
+ break;
+ }
+ return aidl;
+}
+
+ConversionResult<audio_port_config_device_ext>
+aidl2legacy_AudioPortConfigDeviceExt_audio_port_config_device_ext(
+ const media::AudioPortConfigDeviceExt& aidl) {
+ audio_port_config_device_ext legacy;
+ legacy.hw_module = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_module_handle_t(aidl.hwModule));
+ legacy.type = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_devices_t(aidl.type));
+ RETURN_IF_ERROR(aidl2legacy_string(aidl.address, legacy.address, AUDIO_DEVICE_MAX_ADDRESS_LEN));
+ return legacy;
+}
+
+ConversionResult<media::AudioPortConfigDeviceExt>
+legacy2aidl_audio_port_config_device_ext_AudioPortConfigDeviceExt(
+ const audio_port_config_device_ext& legacy) {
+ media::AudioPortConfigDeviceExt aidl;
+ aidl.hwModule = VALUE_OR_RETURN(legacy2aidl_audio_module_handle_t_int32_t(legacy.hw_module));
+ aidl.type = VALUE_OR_RETURN(legacy2aidl_audio_devices_t_int32_t(legacy.type));
+ aidl.address = VALUE_OR_RETURN(
+ legacy2aidl_string(legacy.address, AUDIO_DEVICE_MAX_ADDRESS_LEN));
+ return aidl;
+}
+
+ConversionResult<audio_stream_type_t> aidl2legacy_AudioStreamType_audio_stream_type_t(
+ media::AudioStreamType aidl) {
+ switch (aidl) {
+ case media::AudioStreamType::DEFAULT:
+ return AUDIO_STREAM_DEFAULT;
+ case media::AudioStreamType::VOICE_CALL:
+ return AUDIO_STREAM_VOICE_CALL;
+ case media::AudioStreamType::SYSTEM:
+ return AUDIO_STREAM_SYSTEM;
+ case media::AudioStreamType::RING:
+ return AUDIO_STREAM_RING;
+ case media::AudioStreamType::MUSIC:
+ return AUDIO_STREAM_MUSIC;
+ case media::AudioStreamType::ALARM:
+ return AUDIO_STREAM_ALARM;
+ case media::AudioStreamType::NOTIFICATION:
+ return AUDIO_STREAM_NOTIFICATION;
+ case media::AudioStreamType::BLUETOOTH_SCO:
+ return AUDIO_STREAM_BLUETOOTH_SCO;
+ case media::AudioStreamType::ENFORCED_AUDIBLE:
+ return AUDIO_STREAM_ENFORCED_AUDIBLE;
+ case media::AudioStreamType::DTMF:
+ return AUDIO_STREAM_DTMF;
+ case media::AudioStreamType::TTS:
+ return AUDIO_STREAM_TTS;
+ case media::AudioStreamType::ACCESSIBILITY:
+ return AUDIO_STREAM_ACCESSIBILITY;
+ case media::AudioStreamType::ASSISTANT:
+ return AUDIO_STREAM_ASSISTANT;
+ case media::AudioStreamType::REROUTING:
+ return AUDIO_STREAM_REROUTING;
+ case media::AudioStreamType::PATCH:
+ return AUDIO_STREAM_PATCH;
+ case media::AudioStreamType::CALL_ASSISTANT:
+ return AUDIO_STREAM_CALL_ASSISTANT;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioStreamType> legacy2aidl_audio_stream_type_t_AudioStreamType(
+ audio_stream_type_t legacy) {
+ switch (legacy) {
+ case AUDIO_STREAM_DEFAULT:
+ return media::AudioStreamType::DEFAULT;
+ case AUDIO_STREAM_VOICE_CALL:
+ return media::AudioStreamType::VOICE_CALL;
+ case AUDIO_STREAM_SYSTEM:
+ return media::AudioStreamType::SYSTEM;
+ case AUDIO_STREAM_RING:
+ return media::AudioStreamType::RING;
+ case AUDIO_STREAM_MUSIC:
+ return media::AudioStreamType::MUSIC;
+ case AUDIO_STREAM_ALARM:
+ return media::AudioStreamType::ALARM;
+ case AUDIO_STREAM_NOTIFICATION:
+ return media::AudioStreamType::NOTIFICATION;
+ case AUDIO_STREAM_BLUETOOTH_SCO:
+ return media::AudioStreamType::BLUETOOTH_SCO;
+ case AUDIO_STREAM_ENFORCED_AUDIBLE:
+ return media::AudioStreamType::ENFORCED_AUDIBLE;
+ case AUDIO_STREAM_DTMF:
+ return media::AudioStreamType::DTMF;
+ case AUDIO_STREAM_TTS:
+ return media::AudioStreamType::TTS;
+ case AUDIO_STREAM_ACCESSIBILITY:
+ return media::AudioStreamType::ACCESSIBILITY;
+ case AUDIO_STREAM_ASSISTANT:
+ return media::AudioStreamType::ASSISTANT;
+ case AUDIO_STREAM_REROUTING:
+ return media::AudioStreamType::REROUTING;
+ case AUDIO_STREAM_PATCH:
+ return media::AudioStreamType::PATCH;
+ case AUDIO_STREAM_CALL_ASSISTANT:
+ return media::AudioStreamType::CALL_ASSISTANT;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_source_t> aidl2legacy_AudioSourceType_audio_source_t(
+ media::AudioSourceType aidl) {
+ switch (aidl) {
+ case media::AudioSourceType::INVALID:
+ // This value does not have an enum
+ return AUDIO_SOURCE_INVALID;
+ case media::AudioSourceType::DEFAULT:
+ return AUDIO_SOURCE_DEFAULT;
+ case media::AudioSourceType::MIC:
+ return AUDIO_SOURCE_MIC;
+ case media::AudioSourceType::VOICE_UPLINK:
+ return AUDIO_SOURCE_VOICE_UPLINK;
+ case media::AudioSourceType::VOICE_DOWNLINK:
+ return AUDIO_SOURCE_VOICE_DOWNLINK;
+ case media::AudioSourceType::VOICE_CALL:
+ return AUDIO_SOURCE_VOICE_CALL;
+ case media::AudioSourceType::CAMCORDER:
+ return AUDIO_SOURCE_CAMCORDER;
+ case media::AudioSourceType::VOICE_RECOGNITION:
+ return AUDIO_SOURCE_VOICE_RECOGNITION;
+ case media::AudioSourceType::VOICE_COMMUNICATION:
+ return AUDIO_SOURCE_VOICE_COMMUNICATION;
+ case media::AudioSourceType::REMOTE_SUBMIX:
+ return AUDIO_SOURCE_REMOTE_SUBMIX;
+ case media::AudioSourceType::UNPROCESSED:
+ return AUDIO_SOURCE_UNPROCESSED;
+ case media::AudioSourceType::VOICE_PERFORMANCE:
+ return AUDIO_SOURCE_VOICE_PERFORMANCE;
+ case media::AudioSourceType::ECHO_REFERENCE:
+ return AUDIO_SOURCE_ECHO_REFERENCE;
+ case media::AudioSourceType::FM_TUNER:
+ return AUDIO_SOURCE_FM_TUNER;
+ case media::AudioSourceType::HOTWORD:
+ return AUDIO_SOURCE_HOTWORD;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioSourceType> legacy2aidl_audio_source_t_AudioSourceType(
+ audio_source_t legacy) {
+ switch (legacy) {
+ case AUDIO_SOURCE_INVALID:
+ return media::AudioSourceType::INVALID;
+ case AUDIO_SOURCE_DEFAULT:
+ return media::AudioSourceType::DEFAULT;
+ case AUDIO_SOURCE_MIC:
+ return media::AudioSourceType::MIC;
+ case AUDIO_SOURCE_VOICE_UPLINK:
+ return media::AudioSourceType::VOICE_UPLINK;
+ case AUDIO_SOURCE_VOICE_DOWNLINK:
+ return media::AudioSourceType::VOICE_DOWNLINK;
+ case AUDIO_SOURCE_VOICE_CALL:
+ return media::AudioSourceType::VOICE_CALL;
+ case AUDIO_SOURCE_CAMCORDER:
+ return media::AudioSourceType::CAMCORDER;
+ case AUDIO_SOURCE_VOICE_RECOGNITION:
+ return media::AudioSourceType::VOICE_RECOGNITION;
+ case AUDIO_SOURCE_VOICE_COMMUNICATION:
+ return media::AudioSourceType::VOICE_COMMUNICATION;
+ case AUDIO_SOURCE_REMOTE_SUBMIX:
+ return media::AudioSourceType::REMOTE_SUBMIX;
+ case AUDIO_SOURCE_UNPROCESSED:
+ return media::AudioSourceType::UNPROCESSED;
+ case AUDIO_SOURCE_VOICE_PERFORMANCE:
+ return media::AudioSourceType::VOICE_PERFORMANCE;
+ case AUDIO_SOURCE_ECHO_REFERENCE:
+ return media::AudioSourceType::ECHO_REFERENCE;
+ case AUDIO_SOURCE_FM_TUNER:
+ return media::AudioSourceType::FM_TUNER;
+ case AUDIO_SOURCE_HOTWORD:
+ return media::AudioSourceType::HOTWORD;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_session_t> aidl2legacy_int32_t_audio_session_t(int32_t aidl) {
+ return convertReinterpret<audio_session_t>(aidl);
+}
+
+ConversionResult<int32_t> legacy2aidl_audio_session_t_int32_t(audio_session_t legacy) {
+ return convertReinterpret<int32_t>(legacy);
+}
+
+// This type is unnamed in the original definition, thus we name it here.
+using audio_port_config_mix_ext_usecase = decltype(audio_port_config_mix_ext::usecase);
+
+ConversionResult<audio_port_config_mix_ext_usecase> aidl2legacy_AudioPortConfigMixExtUseCase(
+ const media::AudioPortConfigMixExtUseCase& aidl, media::AudioPortRole role) {
+ audio_port_config_mix_ext_usecase legacy;
+
+ switch (role) {
+ case media::AudioPortRole::NONE:
+ // Just verify that the union is empty.
+ VALUE_OR_RETURN(UNION_GET(aidl, unspecified));
+ return legacy;
+
+ case media::AudioPortRole::SOURCE:
+ // This is not a bug. A SOURCE role corresponds to the stream field.
+ legacy.stream = VALUE_OR_RETURN(aidl2legacy_AudioStreamType_audio_stream_type_t(
+ VALUE_OR_RETURN(UNION_GET(aidl, stream))));
+ return legacy;
+
+ case media::AudioPortRole::SINK:
+ // This is not a bug. A SINK role corresponds to the source field.
+ legacy.source = VALUE_OR_RETURN(aidl2legacy_AudioSourceType_audio_source_t(
+ VALUE_OR_RETURN(UNION_GET(aidl, source))));
+ return legacy;
+ }
+ LOG_ALWAYS_FATAL("Shouldn't get here"); // with -Werror,-Wswitch may compile-time fail
+}
+
+ConversionResult<media::AudioPortConfigMixExtUseCase> legacy2aidl_AudioPortConfigMixExtUseCase(
+ const audio_port_config_mix_ext_usecase& legacy, audio_port_role_t role) {
+ media::AudioPortConfigMixExtUseCase aidl;
+
+ switch (role) {
+ case AUDIO_PORT_ROLE_NONE:
+ UNION_SET(aidl, unspecified, false);
+ return aidl;
+ case AUDIO_PORT_ROLE_SOURCE:
+ // This is not a bug. A SOURCE role corresponds to the stream field.
+ UNION_SET(aidl, stream, VALUE_OR_RETURN(
+ legacy2aidl_audio_stream_type_t_AudioStreamType(legacy.stream)));
+ return aidl;
+ case AUDIO_PORT_ROLE_SINK:
+ // This is not a bug. A SINK role corresponds to the source field.
+ UNION_SET(aidl, source,
+ VALUE_OR_RETURN(legacy2aidl_audio_source_t_AudioSourceType(legacy.source)));
+ return aidl;
+ }
+ LOG_ALWAYS_FATAL("Shouldn't get here"); // with -Werror,-Wswitch may compile-time fail
+}
+
+ConversionResult<audio_port_config_mix_ext> aidl2legacy_AudioPortConfigMixExt(
+ const media::AudioPortConfigMixExt& aidl, media::AudioPortRole role) {
+ audio_port_config_mix_ext legacy;
+ legacy.hw_module = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_module_handle_t(aidl.hwModule));
+ legacy.handle = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_io_handle_t(aidl.handle));
+ legacy.usecase = VALUE_OR_RETURN(aidl2legacy_AudioPortConfigMixExtUseCase(aidl.usecase, role));
+ return legacy;
+}
+
+ConversionResult<media::AudioPortConfigMixExt> legacy2aidl_AudioPortConfigMixExt(
+ const audio_port_config_mix_ext& legacy, audio_port_role_t role) {
+ media::AudioPortConfigMixExt aidl;
+ aidl.hwModule = VALUE_OR_RETURN(legacy2aidl_audio_module_handle_t_int32_t(legacy.hw_module));
+ aidl.handle = VALUE_OR_RETURN(legacy2aidl_audio_io_handle_t_int32_t(legacy.handle));
+ aidl.usecase = VALUE_OR_RETURN(legacy2aidl_AudioPortConfigMixExtUseCase(legacy.usecase, role));
+ return aidl;
+}
+
+ConversionResult<audio_port_config_session_ext>
+aidl2legacy_AudioPortConfigSessionExt_audio_port_config_session_ext(
+ const media::AudioPortConfigSessionExt& aidl) {
+ audio_port_config_session_ext legacy;
+ legacy.session = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_session_t(aidl.session));
+ return legacy;
+}
+
+ConversionResult<media::AudioPortConfigSessionExt>
+legacy2aidl_audio_port_config_session_ext_AudioPortConfigSessionExt(
+ const audio_port_config_session_ext& legacy) {
+ media::AudioPortConfigSessionExt aidl;
+ aidl.session = VALUE_OR_RETURN(legacy2aidl_audio_session_t_int32_t(legacy.session));
+ return aidl;
+}
+
+// This type is unnamed in the original definition, thus we name it here.
+using audio_port_config_ext = decltype(audio_port_config::ext);
+
+ConversionResult<audio_port_config_ext> aidl2legacy_AudioPortConfigExt(
+ const media::AudioPortConfigExt& aidl, media::AudioPortType type,
+ media::AudioPortRole role) {
+ audio_port_config_ext legacy;
+ switch (type) {
+ case media::AudioPortType::NONE:
+ // Just verify that the union is empty.
+ VALUE_OR_RETURN(UNION_GET(aidl, unspecified));
+ return legacy;
+ case media::AudioPortType::DEVICE:
+ legacy.device = VALUE_OR_RETURN(
+ aidl2legacy_AudioPortConfigDeviceExt_audio_port_config_device_ext(
+ VALUE_OR_RETURN(UNION_GET(aidl, device))));
+ return legacy;
+ case media::AudioPortType::MIX:
+ legacy.mix = VALUE_OR_RETURN(
+ aidl2legacy_AudioPortConfigMixExt(VALUE_OR_RETURN(UNION_GET(aidl, mix)), role));
+ return legacy;
+ case media::AudioPortType::SESSION:
+ legacy.session = VALUE_OR_RETURN(
+ aidl2legacy_AudioPortConfigSessionExt_audio_port_config_session_ext(
+ VALUE_OR_RETURN(UNION_GET(aidl, session))));
+ return legacy;
+
+ }
+ LOG_ALWAYS_FATAL("Shouldn't get here"); // with -Werror,-Wswitch may compile-time fail
+}
+
+ConversionResult<media::AudioPortConfigExt> legacy2aidl_AudioPortConfigExt(
+ const audio_port_config_ext& legacy, audio_port_type_t type, audio_port_role_t role) {
+ media::AudioPortConfigExt aidl;
+
+ switch (type) {
+ case AUDIO_PORT_TYPE_NONE:
+ UNION_SET(aidl, unspecified, false);
+ return aidl;
+ case AUDIO_PORT_TYPE_DEVICE:
+ UNION_SET(aidl, device,
+ VALUE_OR_RETURN(
+ legacy2aidl_audio_port_config_device_ext_AudioPortConfigDeviceExt(
+ legacy.device)));
+ return aidl;
+ case AUDIO_PORT_TYPE_MIX:
+ UNION_SET(aidl, mix,
+ VALUE_OR_RETURN(legacy2aidl_AudioPortConfigMixExt(legacy.mix, role)));
+ return aidl;
+ case AUDIO_PORT_TYPE_SESSION:
+ UNION_SET(aidl, session,
+ VALUE_OR_RETURN(
+ legacy2aidl_audio_port_config_session_ext_AudioPortConfigSessionExt(
+ legacy.session)));
+ return aidl;
+ }
+ LOG_ALWAYS_FATAL("Shouldn't get here"); // with -Werror,-Wswitch may compile-time fail
+}
+
+ConversionResult<audio_port_config> aidl2legacy_AudioPortConfig_audio_port_config(
+ const media::AudioPortConfig& aidl) {
+ audio_port_config legacy;
+ legacy.id = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_port_handle_t(aidl.id));
+ legacy.role = VALUE_OR_RETURN(aidl2legacy_AudioPortRole_audio_port_role_t(aidl.role));
+ legacy.type = VALUE_OR_RETURN(aidl2legacy_AudioPortType_audio_port_type_t(aidl.type));
+ legacy.config_mask = VALUE_OR_RETURN(aidl2legacy_int32_t_config_mask(aidl.configMask));
+ if (bitmaskIsSet(aidl.configMask, media::AudioPortConfigType::SAMPLE_RATE)) {
+ legacy.sample_rate = VALUE_OR_RETURN(convertIntegral<unsigned int>(aidl.sampleRate));
+ }
+ if (bitmaskIsSet(aidl.configMask, media::AudioPortConfigType::CHANNEL_MASK)) {
+ legacy.channel_mask =
+ VALUE_OR_RETURN(aidl2legacy_int32_t_audio_channel_mask_t(aidl.channelMask));
+ }
+ if (bitmaskIsSet(aidl.configMask, media::AudioPortConfigType::FORMAT)) {
+ legacy.format = VALUE_OR_RETURN(aidl2legacy_AudioFormat_audio_format_t(aidl.format));
+ }
+ if (bitmaskIsSet(aidl.configMask, media::AudioPortConfigType::GAIN)) {
+ legacy.gain = VALUE_OR_RETURN(
+ aidl2legacy_AudioGainConfig_audio_gain_config(aidl.gain, aidl.role, aidl.type));
+ }
+ if (bitmaskIsSet(aidl.configMask, media::AudioPortConfigType::FLAGS)) {
+ legacy.flags = VALUE_OR_RETURN(
+ aidl2legacy_AudioIoFlags_audio_io_flags(aidl.flags, aidl.role, aidl.type));
+ }
+ legacy.ext = VALUE_OR_RETURN(aidl2legacy_AudioPortConfigExt(aidl.ext, aidl.type, aidl.role));
+ return legacy;
+}
+
+ConversionResult<media::AudioPortConfig> legacy2aidl_audio_port_config_AudioPortConfig(
+ const audio_port_config& legacy) {
+ media::AudioPortConfig aidl;
+ aidl.id = VALUE_OR_RETURN(legacy2aidl_audio_port_handle_t_int32_t(legacy.id));
+ aidl.role = VALUE_OR_RETURN(legacy2aidl_audio_port_role_t_AudioPortRole(legacy.role));
+ aidl.type = VALUE_OR_RETURN(legacy2aidl_audio_port_type_t_AudioPortType(legacy.type));
+ aidl.configMask = VALUE_OR_RETURN(legacy2aidl_config_mask_int32_t(legacy.config_mask));
+ if (legacy.config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) {
+ aidl.sampleRate = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.sample_rate));
+ }
+ if (legacy.config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
+ aidl.channelMask =
+ VALUE_OR_RETURN(legacy2aidl_audio_channel_mask_t_int32_t(legacy.channel_mask));
+ }
+ if (legacy.config_mask & AUDIO_PORT_CONFIG_FORMAT) {
+ aidl.format = VALUE_OR_RETURN(legacy2aidl_audio_format_t_AudioFormat(legacy.format));
+ }
+ if (legacy.config_mask & AUDIO_PORT_CONFIG_GAIN) {
+ aidl.gain = VALUE_OR_RETURN(legacy2aidl_audio_gain_config_AudioGainConfig(
+ legacy.gain, legacy.role, legacy.type));
+ }
+ if (legacy.config_mask & AUDIO_PORT_CONFIG_FLAGS) {
+ aidl.flags = VALUE_OR_RETURN(
+ legacy2aidl_audio_io_flags_AudioIoFlags(legacy.flags, legacy.role, legacy.type));
+ }
+ aidl.ext =
+ VALUE_OR_RETURN(legacy2aidl_AudioPortConfigExt(legacy.ext, legacy.type, legacy.role));
+ return aidl;
+}
+
+ConversionResult<struct audio_patch> aidl2legacy_AudioPatch_audio_patch(
+ const media::AudioPatch& aidl) {
+ struct audio_patch legacy;
+ legacy.id = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_patch_handle_t(aidl.id));
+ legacy.num_sinks = VALUE_OR_RETURN(convertIntegral<unsigned int>(aidl.sinks.size()));
+ if (legacy.num_sinks > AUDIO_PATCH_PORTS_MAX) {
+ return unexpected(BAD_VALUE);
+ }
+ for (size_t i = 0; i < legacy.num_sinks; ++i) {
+ legacy.sinks[i] =
+ VALUE_OR_RETURN(aidl2legacy_AudioPortConfig_audio_port_config(aidl.sinks[i]));
+ }
+ legacy.num_sources = VALUE_OR_RETURN(convertIntegral<unsigned int>(aidl.sources.size()));
+ if (legacy.num_sources > AUDIO_PATCH_PORTS_MAX) {
+ return unexpected(BAD_VALUE);
+ }
+ for (size_t i = 0; i < legacy.num_sources; ++i) {
+ legacy.sources[i] =
+ VALUE_OR_RETURN(aidl2legacy_AudioPortConfig_audio_port_config(aidl.sources[i]));
+ }
+ return legacy;
+}
+
+ConversionResult<media::AudioPatch> legacy2aidl_audio_patch_AudioPatch(
+ const struct audio_patch& legacy) {
+ media::AudioPatch aidl;
+ aidl.id = VALUE_OR_RETURN(legacy2aidl_audio_patch_handle_t_int32_t(legacy.id));
+
+ if (legacy.num_sinks > AUDIO_PATCH_PORTS_MAX) {
+ return unexpected(BAD_VALUE);
+ }
+ for (unsigned int i = 0; i < legacy.num_sinks; ++i) {
+ aidl.sinks.push_back(
+ VALUE_OR_RETURN(legacy2aidl_audio_port_config_AudioPortConfig(legacy.sinks[i])));
+ }
+ if (legacy.num_sources > AUDIO_PATCH_PORTS_MAX) {
+ return unexpected(BAD_VALUE);
+ }
+ for (unsigned int i = 0; i < legacy.num_sources; ++i) {
+ aidl.sources.push_back(
+ VALUE_OR_RETURN(legacy2aidl_audio_port_config_AudioPortConfig(legacy.sources[i])));
+ }
+ return aidl;
+}
+
+ConversionResult<sp<AudioIoDescriptor>> aidl2legacy_AudioIoDescriptor_AudioIoDescriptor(
+ const media::AudioIoDescriptor& aidl) {
+ sp<AudioIoDescriptor> legacy(new AudioIoDescriptor());
+ legacy->mIoHandle = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_io_handle_t(aidl.ioHandle));
+ legacy->mPatch = VALUE_OR_RETURN(aidl2legacy_AudioPatch_audio_patch(aidl.patch));
+ legacy->mSamplingRate = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.samplingRate));
+ legacy->mFormat = VALUE_OR_RETURN(aidl2legacy_AudioFormat_audio_format_t(aidl.format));
+ legacy->mChannelMask =
+ VALUE_OR_RETURN(aidl2legacy_int32_t_audio_channel_mask_t(aidl.channelMask));
+ legacy->mFrameCount = VALUE_OR_RETURN(convertIntegral<size_t>(aidl.frameCount));
+ legacy->mFrameCountHAL = VALUE_OR_RETURN(convertIntegral<size_t>(aidl.frameCountHAL));
+ legacy->mLatency = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.latency));
+ legacy->mPortId = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_port_handle_t(aidl.portId));
+ return legacy;
+}
+
+ConversionResult<media::AudioIoDescriptor> legacy2aidl_AudioIoDescriptor_AudioIoDescriptor(
+ const sp<AudioIoDescriptor>& legacy) {
+ media::AudioIoDescriptor aidl;
+ aidl.ioHandle = VALUE_OR_RETURN(legacy2aidl_audio_io_handle_t_int32_t(legacy->mIoHandle));
+ aidl.patch = VALUE_OR_RETURN(legacy2aidl_audio_patch_AudioPatch(legacy->mPatch));
+ aidl.samplingRate = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy->mSamplingRate));
+ aidl.format = VALUE_OR_RETURN(legacy2aidl_audio_format_t_AudioFormat(legacy->mFormat));
+ aidl.channelMask = VALUE_OR_RETURN(
+ legacy2aidl_audio_channel_mask_t_int32_t(legacy->mChannelMask));
+ aidl.frameCount = VALUE_OR_RETURN(convertIntegral<int64_t>(legacy->mFrameCount));
+ aidl.frameCountHAL = VALUE_OR_RETURN(convertIntegral<int64_t>(legacy->mFrameCountHAL));
+ aidl.latency = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy->mLatency));
+ aidl.portId = VALUE_OR_RETURN(legacy2aidl_audio_port_handle_t_int32_t(legacy->mPortId));
+ return aidl;
+}
+
+ConversionResult<AudioClient> aidl2legacy_AudioClient_AudioClient(
+ const media::AudioClient& aidl) {
+ AudioClient legacy;
+ legacy.clientTid = VALUE_OR_RETURN(aidl2legacy_int32_t_pid_t(aidl.clientTid));
+ legacy.identity = aidl.identity;
+ return legacy;
+}
+
+ConversionResult<media::AudioClient> legacy2aidl_AudioClient_AudioClient(
+ const AudioClient& legacy) {
+ media::AudioClient aidl;
+ aidl.clientTid = VALUE_OR_RETURN(legacy2aidl_pid_t_int32_t(legacy.clientTid));
+ aidl.identity = legacy.identity;
+ return aidl;
+}
+
+ConversionResult<audio_content_type_t>
+aidl2legacy_AudioContentType_audio_content_type_t(media::AudioContentType aidl) {
+ switch (aidl) {
+ case media::AudioContentType::UNKNOWN:
+ return AUDIO_CONTENT_TYPE_UNKNOWN;
+ case media::AudioContentType::SPEECH:
+ return AUDIO_CONTENT_TYPE_SPEECH;
+ case media::AudioContentType::MUSIC:
+ return AUDIO_CONTENT_TYPE_MUSIC;
+ case media::AudioContentType::MOVIE:
+ return AUDIO_CONTENT_TYPE_MOVIE;
+ case media::AudioContentType::SONIFICATION:
+ return AUDIO_CONTENT_TYPE_SONIFICATION;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioContentType>
+legacy2aidl_audio_content_type_t_AudioContentType(audio_content_type_t legacy) {
+ switch (legacy) {
+ case AUDIO_CONTENT_TYPE_UNKNOWN:
+ return media::AudioContentType::UNKNOWN;
+ case AUDIO_CONTENT_TYPE_SPEECH:
+ return media::AudioContentType::SPEECH;
+ case AUDIO_CONTENT_TYPE_MUSIC:
+ return media::AudioContentType::MUSIC;
+ case AUDIO_CONTENT_TYPE_MOVIE:
+ return media::AudioContentType::MOVIE;
+ case AUDIO_CONTENT_TYPE_SONIFICATION:
+ return media::AudioContentType::SONIFICATION;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_usage_t>
+aidl2legacy_AudioUsage_audio_usage_t(media::AudioUsage aidl) {
+ switch (aidl) {
+ case media::AudioUsage::UNKNOWN:
+ return AUDIO_USAGE_UNKNOWN;
+ case media::AudioUsage::MEDIA:
+ return AUDIO_USAGE_MEDIA;
+ case media::AudioUsage::VOICE_COMMUNICATION:
+ return AUDIO_USAGE_VOICE_COMMUNICATION;
+ case media::AudioUsage::VOICE_COMMUNICATION_SIGNALLING:
+ return AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING;
+ case media::AudioUsage::ALARM:
+ return AUDIO_USAGE_ALARM;
+ case media::AudioUsage::NOTIFICATION:
+ return AUDIO_USAGE_NOTIFICATION;
+ case media::AudioUsage::NOTIFICATION_TELEPHONY_RINGTONE:
+ return AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE;
+ case media::AudioUsage::NOTIFICATION_COMMUNICATION_REQUEST:
+ return AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST;
+ case media::AudioUsage::NOTIFICATION_COMMUNICATION_INSTANT:
+ return AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT;
+ case media::AudioUsage::NOTIFICATION_COMMUNICATION_DELAYED:
+ return AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED;
+ case media::AudioUsage::NOTIFICATION_EVENT:
+ return AUDIO_USAGE_NOTIFICATION_EVENT;
+ case media::AudioUsage::ASSISTANCE_ACCESSIBILITY:
+ return AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY;
+ case media::AudioUsage::ASSISTANCE_NAVIGATION_GUIDANCE:
+ return AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
+ case media::AudioUsage::ASSISTANCE_SONIFICATION:
+ return AUDIO_USAGE_ASSISTANCE_SONIFICATION;
+ case media::AudioUsage::GAME:
+ return AUDIO_USAGE_GAME;
+ case media::AudioUsage::VIRTUAL_SOURCE:
+ return AUDIO_USAGE_VIRTUAL_SOURCE;
+ case media::AudioUsage::ASSISTANT:
+ return AUDIO_USAGE_ASSISTANT;
+ case media::AudioUsage::CALL_ASSISTANT:
+ return AUDIO_USAGE_CALL_ASSISTANT;
+ case media::AudioUsage::EMERGENCY:
+ return AUDIO_USAGE_EMERGENCY;
+ case media::AudioUsage::SAFETY:
+ return AUDIO_USAGE_SAFETY;
+ case media::AudioUsage::VEHICLE_STATUS:
+ return AUDIO_USAGE_VEHICLE_STATUS;
+ case media::AudioUsage::ANNOUNCEMENT:
+ return AUDIO_USAGE_ANNOUNCEMENT;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioUsage>
+legacy2aidl_audio_usage_t_AudioUsage(audio_usage_t legacy) {
+ switch (legacy) {
+ case AUDIO_USAGE_UNKNOWN:
+ return media::AudioUsage::UNKNOWN;
+ case AUDIO_USAGE_MEDIA:
+ return media::AudioUsage::MEDIA;
+ case AUDIO_USAGE_VOICE_COMMUNICATION:
+ return media::AudioUsage::VOICE_COMMUNICATION;
+ case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING:
+ return media::AudioUsage::VOICE_COMMUNICATION_SIGNALLING;
+ case AUDIO_USAGE_ALARM:
+ return media::AudioUsage::ALARM;
+ case AUDIO_USAGE_NOTIFICATION:
+ return media::AudioUsage::NOTIFICATION;
+ case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE:
+ return media::AudioUsage::NOTIFICATION_TELEPHONY_RINGTONE;
+ case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
+ return media::AudioUsage::NOTIFICATION_COMMUNICATION_REQUEST;
+ case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
+ return media::AudioUsage::NOTIFICATION_COMMUNICATION_INSTANT;
+ case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
+ return media::AudioUsage::NOTIFICATION_COMMUNICATION_DELAYED;
+ case AUDIO_USAGE_NOTIFICATION_EVENT:
+ return media::AudioUsage::NOTIFICATION_EVENT;
+ case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY:
+ return media::AudioUsage::ASSISTANCE_ACCESSIBILITY;
+ case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
+ return media::AudioUsage::ASSISTANCE_NAVIGATION_GUIDANCE;
+ case AUDIO_USAGE_ASSISTANCE_SONIFICATION:
+ return media::AudioUsage::ASSISTANCE_SONIFICATION;
+ case AUDIO_USAGE_GAME:
+ return media::AudioUsage::GAME;
+ case AUDIO_USAGE_VIRTUAL_SOURCE:
+ return media::AudioUsage::VIRTUAL_SOURCE;
+ case AUDIO_USAGE_ASSISTANT:
+ return media::AudioUsage::ASSISTANT;
+ case AUDIO_USAGE_CALL_ASSISTANT:
+ return media::AudioUsage::CALL_ASSISTANT;
+ case AUDIO_USAGE_EMERGENCY:
+ return media::AudioUsage::EMERGENCY;
+ case AUDIO_USAGE_SAFETY:
+ return media::AudioUsage::SAFETY;
+ case AUDIO_USAGE_VEHICLE_STATUS:
+ return media::AudioUsage::VEHICLE_STATUS;
+ case AUDIO_USAGE_ANNOUNCEMENT:
+ return media::AudioUsage::ANNOUNCEMENT;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_flags_mask_t>
+aidl2legacy_AudioFlag_audio_flags_mask_t(media::AudioFlag aidl) {
+ switch (aidl) {
+ case media::AudioFlag::AUDIBILITY_ENFORCED:
+ return AUDIO_FLAG_AUDIBILITY_ENFORCED;
+ case media::AudioFlag::SECURE:
+ return AUDIO_FLAG_SECURE;
+ case media::AudioFlag::SCO:
+ return AUDIO_FLAG_SCO;
+ case media::AudioFlag::BEACON:
+ return AUDIO_FLAG_BEACON;
+ case media::AudioFlag::HW_AV_SYNC:
+ return AUDIO_FLAG_HW_AV_SYNC;
+ case media::AudioFlag::HW_HOTWORD:
+ return AUDIO_FLAG_HW_HOTWORD;
+ case media::AudioFlag::BYPASS_INTERRUPTION_POLICY:
+ return AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY;
+ case media::AudioFlag::BYPASS_MUTE:
+ return AUDIO_FLAG_BYPASS_MUTE;
+ case media::AudioFlag::LOW_LATENCY:
+ return AUDIO_FLAG_LOW_LATENCY;
+ case media::AudioFlag::DEEP_BUFFER:
+ return AUDIO_FLAG_DEEP_BUFFER;
+ case media::AudioFlag::NO_MEDIA_PROJECTION:
+ return AUDIO_FLAG_NO_MEDIA_PROJECTION;
+ case media::AudioFlag::MUTE_HAPTIC:
+ return AUDIO_FLAG_MUTE_HAPTIC;
+ case media::AudioFlag::NO_SYSTEM_CAPTURE:
+ return AUDIO_FLAG_NO_SYSTEM_CAPTURE;
+ case media::AudioFlag::CAPTURE_PRIVATE:
+ return AUDIO_FLAG_CAPTURE_PRIVATE;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioFlag>
+legacy2aidl_audio_flags_mask_t_AudioFlag(audio_flags_mask_t legacy) {
+ switch (legacy) {
+ case AUDIO_FLAG_NONE:
+ return unexpected(BAD_VALUE);
+ case AUDIO_FLAG_AUDIBILITY_ENFORCED:
+ return media::AudioFlag::AUDIBILITY_ENFORCED;
+ case AUDIO_FLAG_SECURE:
+ return media::AudioFlag::SECURE;
+ case AUDIO_FLAG_SCO:
+ return media::AudioFlag::SCO;
+ case AUDIO_FLAG_BEACON:
+ return media::AudioFlag::BEACON;
+ case AUDIO_FLAG_HW_AV_SYNC:
+ return media::AudioFlag::HW_AV_SYNC;
+ case AUDIO_FLAG_HW_HOTWORD:
+ return media::AudioFlag::HW_HOTWORD;
+ case AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY:
+ return media::AudioFlag::BYPASS_INTERRUPTION_POLICY;
+ case AUDIO_FLAG_BYPASS_MUTE:
+ return media::AudioFlag::BYPASS_MUTE;
+ case AUDIO_FLAG_LOW_LATENCY:
+ return media::AudioFlag::LOW_LATENCY;
+ case AUDIO_FLAG_DEEP_BUFFER:
+ return media::AudioFlag::DEEP_BUFFER;
+ case AUDIO_FLAG_NO_MEDIA_PROJECTION:
+ return media::AudioFlag::NO_MEDIA_PROJECTION;
+ case AUDIO_FLAG_MUTE_HAPTIC:
+ return media::AudioFlag::MUTE_HAPTIC;
+ case AUDIO_FLAG_NO_SYSTEM_CAPTURE:
+ return media::AudioFlag::NO_SYSTEM_CAPTURE;
+ case AUDIO_FLAG_CAPTURE_PRIVATE:
+ return media::AudioFlag::CAPTURE_PRIVATE;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_flags_mask_t>
+aidl2legacy_int32_t_audio_flags_mask_t_mask(int32_t aidl) {
+ return convertBitmask<audio_flags_mask_t, int32_t, audio_flags_mask_t, media::AudioFlag>(
+ aidl, aidl2legacy_AudioFlag_audio_flags_mask_t, indexToEnum_index<media::AudioFlag>,
+ enumToMask_bitmask<audio_flags_mask_t, audio_flags_mask_t>);
+}
+
+ConversionResult<int32_t>
+legacy2aidl_audio_flags_mask_t_int32_t_mask(audio_flags_mask_t legacy) {
+ return convertBitmask<int32_t, audio_flags_mask_t, media::AudioFlag, audio_flags_mask_t>(
+ legacy, legacy2aidl_audio_flags_mask_t_AudioFlag,
+ indexToEnum_bitmask<audio_flags_mask_t>,
+ enumToMask_index<int32_t, media::AudioFlag>);
+}
+
+ConversionResult<audio_attributes_t>
+aidl2legacy_AudioAttributesInternal_audio_attributes_t(const media::AudioAttributesInternal& aidl) {
+ audio_attributes_t legacy;
+ legacy.content_type = VALUE_OR_RETURN(
+ aidl2legacy_AudioContentType_audio_content_type_t(aidl.contentType));
+ legacy.usage = VALUE_OR_RETURN(aidl2legacy_AudioUsage_audio_usage_t(aidl.usage));
+ legacy.source = VALUE_OR_RETURN(aidl2legacy_AudioSourceType_audio_source_t(aidl.source));
+ legacy.flags = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_flags_mask_t_mask(aidl.flags));
+ RETURN_IF_ERROR(aidl2legacy_string(aidl.tags, legacy.tags, sizeof(legacy.tags)));
+ return legacy;
+}
+
+ConversionResult<media::AudioAttributesInternal>
+legacy2aidl_audio_attributes_t_AudioAttributesInternal(const audio_attributes_t& legacy) {
+ media::AudioAttributesInternal aidl;
+ aidl.contentType = VALUE_OR_RETURN(
+ legacy2aidl_audio_content_type_t_AudioContentType(legacy.content_type));
+ aidl.usage = VALUE_OR_RETURN(legacy2aidl_audio_usage_t_AudioUsage(legacy.usage));
+ aidl.source = VALUE_OR_RETURN(legacy2aidl_audio_source_t_AudioSourceType(legacy.source));
+ aidl.flags = VALUE_OR_RETURN(legacy2aidl_audio_flags_mask_t_int32_t_mask(legacy.flags));
+ aidl.tags = VALUE_OR_RETURN(legacy2aidl_string(legacy.tags, sizeof(legacy.tags)));
+ return aidl;
+}
+
+ConversionResult<audio_encapsulation_mode_t>
+aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t(media::AudioEncapsulationMode aidl) {
+ switch (aidl) {
+ case media::AudioEncapsulationMode::NONE:
+ return AUDIO_ENCAPSULATION_MODE_NONE;
+ case media::AudioEncapsulationMode::ELEMENTARY_STREAM:
+ return AUDIO_ENCAPSULATION_MODE_ELEMENTARY_STREAM;
+ case media::AudioEncapsulationMode::HANDLE:
+ return AUDIO_ENCAPSULATION_MODE_HANDLE;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioEncapsulationMode>
+legacy2aidl_audio_encapsulation_mode_t_AudioEncapsulationMode(audio_encapsulation_mode_t legacy) {
+ switch (legacy) {
+ case AUDIO_ENCAPSULATION_MODE_NONE:
+ return media::AudioEncapsulationMode::NONE;
+ case AUDIO_ENCAPSULATION_MODE_ELEMENTARY_STREAM:
+ return media::AudioEncapsulationMode::ELEMENTARY_STREAM;
+ case AUDIO_ENCAPSULATION_MODE_HANDLE:
+ return media::AudioEncapsulationMode::HANDLE;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_offload_info_t>
+aidl2legacy_AudioOffloadInfo_audio_offload_info_t(const media::AudioOffloadInfo& aidl) {
+ audio_offload_info_t legacy;
+ legacy.version = VALUE_OR_RETURN(convertIntegral<uint16_t>(aidl.version));
+ legacy.size = sizeof(audio_offload_info_t);
+ audio_config_base_t config = VALUE_OR_RETURN(
+ aidl2legacy_AudioConfigBase_audio_config_base_t(aidl.config));
+ legacy.sample_rate = config.sample_rate;
+ legacy.channel_mask = config.channel_mask;
+ legacy.format = config.format;
+ legacy.stream_type = VALUE_OR_RETURN(
+ aidl2legacy_AudioStreamType_audio_stream_type_t(aidl.streamType));
+ legacy.bit_rate = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.bitRate));
+ legacy.duration_us = VALUE_OR_RETURN(convertIntegral<int64_t>(aidl.durationUs));
+ legacy.has_video = aidl.hasVideo;
+ legacy.is_streaming = aidl.isStreaming;
+ legacy.bit_width = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.bitWidth));
+ legacy.offload_buffer_size = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.offloadBufferSize));
+ legacy.usage = VALUE_OR_RETURN(aidl2legacy_AudioUsage_audio_usage_t(aidl.usage));
+ legacy.encapsulation_mode = VALUE_OR_RETURN(
+ aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t(aidl.encapsulationMode));
+ legacy.content_id = VALUE_OR_RETURN(convertReinterpret<int32_t>(aidl.contentId));
+ legacy.sync_id = VALUE_OR_RETURN(convertReinterpret<int32_t>(aidl.syncId));
+ return legacy;
+}
+
+ConversionResult<media::AudioOffloadInfo>
+legacy2aidl_audio_offload_info_t_AudioOffloadInfo(const audio_offload_info_t& legacy) {
+ media::AudioOffloadInfo aidl;
+ // Version 0.1 fields.
+ if (legacy.size < offsetof(audio_offload_info_t, usage) + sizeof(audio_offload_info_t::usage)) {
+ return unexpected(BAD_VALUE);
+ }
+ aidl.version = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.version));
+ aidl.config.sampleRate = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.sample_rate));
+ aidl.config.channelMask = VALUE_OR_RETURN(
+ legacy2aidl_audio_channel_mask_t_int32_t(legacy.channel_mask));
+ aidl.config.format = VALUE_OR_RETURN(legacy2aidl_audio_format_t_AudioFormat(legacy.format));
+ aidl.streamType = VALUE_OR_RETURN(
+ legacy2aidl_audio_stream_type_t_AudioStreamType(legacy.stream_type));
+ aidl.bitRate = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.bit_rate));
+ aidl.durationUs = VALUE_OR_RETURN(convertIntegral<int64_t>(legacy.duration_us));
+ aidl.hasVideo = legacy.has_video;
+ aidl.isStreaming = legacy.is_streaming;
+ aidl.bitWidth = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.bit_width));
+ aidl.offloadBufferSize = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.offload_buffer_size));
+ aidl.usage = VALUE_OR_RETURN(legacy2aidl_audio_usage_t_AudioUsage(legacy.usage));
+
+ // Version 0.2 fields.
+ if (legacy.version >= AUDIO_OFFLOAD_INFO_VERSION_0_2) {
+ if (legacy.size <
+ offsetof(audio_offload_info_t, sync_id) + sizeof(audio_offload_info_t::sync_id)) {
+ return unexpected(BAD_VALUE);
+ }
+ aidl.encapsulationMode = VALUE_OR_RETURN(
+ legacy2aidl_audio_encapsulation_mode_t_AudioEncapsulationMode(
+ legacy.encapsulation_mode));
+ aidl.contentId = VALUE_OR_RETURN(convertReinterpret<int32_t>(legacy.content_id));
+ aidl.syncId = VALUE_OR_RETURN(convertReinterpret<int32_t>(legacy.sync_id));
+ }
+ return aidl;
+}
+
+ConversionResult<audio_config_t>
+aidl2legacy_AudioConfig_audio_config_t(const media::AudioConfig& aidl) {
+ audio_config_t legacy;
+ legacy.sample_rate = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.sampleRate));
+ legacy.channel_mask = VALUE_OR_RETURN(
+ aidl2legacy_int32_t_audio_channel_mask_t(aidl.channelMask));
+ legacy.format = VALUE_OR_RETURN(aidl2legacy_AudioFormat_audio_format_t(aidl.format));
+ legacy.offload_info = VALUE_OR_RETURN(
+ aidl2legacy_AudioOffloadInfo_audio_offload_info_t(aidl.offloadInfo));
+ legacy.frame_count = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.frameCount));
+ return legacy;
+}
+
+ConversionResult<media::AudioConfig>
+legacy2aidl_audio_config_t_AudioConfig(const audio_config_t& legacy) {
+ media::AudioConfig aidl;
+ aidl.sampleRate = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.sample_rate));
+ aidl.channelMask = VALUE_OR_RETURN(
+ legacy2aidl_audio_channel_mask_t_int32_t(legacy.channel_mask));
+ aidl.format = VALUE_OR_RETURN(legacy2aidl_audio_format_t_AudioFormat(legacy.format));
+ aidl.offloadInfo = VALUE_OR_RETURN(
+ legacy2aidl_audio_offload_info_t_AudioOffloadInfo(legacy.offload_info));
+ aidl.frameCount = VALUE_OR_RETURN(convertIntegral<int64_t>(legacy.frame_count));
+ return aidl;
+}
+
+ConversionResult<audio_config_base_t>
+aidl2legacy_AudioConfigBase_audio_config_base_t(const media::AudioConfigBase& aidl) {
+ audio_config_base_t legacy;
+ legacy.sample_rate = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.sampleRate));
+ legacy.channel_mask = VALUE_OR_RETURN(
+ aidl2legacy_int32_t_audio_channel_mask_t(aidl.channelMask));
+ legacy.format = VALUE_OR_RETURN(aidl2legacy_AudioFormat_audio_format_t(aidl.format));
+ return legacy;
+}
+
+ConversionResult<media::AudioConfigBase>
+legacy2aidl_audio_config_base_t_AudioConfigBase(const audio_config_base_t& legacy) {
+ media::AudioConfigBase aidl;
+ aidl.sampleRate = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.sample_rate));
+ aidl.channelMask = VALUE_OR_RETURN(
+ legacy2aidl_audio_channel_mask_t_int32_t(legacy.channel_mask));
+ aidl.format = VALUE_OR_RETURN(legacy2aidl_audio_format_t_AudioFormat(legacy.format));
+ return aidl;
+}
+
+ConversionResult<sp<IMemory>>
+aidl2legacy_SharedFileRegion_IMemory(const media::SharedFileRegion& aidl) {
+ sp<IMemory> legacy;
+ if (!convertSharedFileRegionToIMemory(aidl, &legacy)) {
+ return unexpected(BAD_VALUE);
+ }
+ return legacy;
+}
+
+ConversionResult<media::SharedFileRegion>
+legacy2aidl_IMemory_SharedFileRegion(const sp<IMemory>& legacy) {
+ media::SharedFileRegion aidl;
+ if (!convertIMemoryToSharedFileRegion(legacy, &aidl)) {
+ return unexpected(BAD_VALUE);
+ }
+ return aidl;
+}
+
+ConversionResult<sp<IMemory>>
+aidl2legacy_NullableSharedFileRegion_IMemory(const std::optional<media::SharedFileRegion>& aidl) {
+ sp<IMemory> legacy;
+ if (!convertNullableSharedFileRegionToIMemory(aidl, &legacy)) {
+ return unexpected(BAD_VALUE);
+ }
+ return legacy;
+}
+
+ConversionResult<std::optional<media::SharedFileRegion>>
+legacy2aidl_NullableIMemory_SharedFileRegion(const sp<IMemory>& legacy) {
+ std::optional<media::SharedFileRegion> aidl;
+ if (!convertNullableIMemoryToSharedFileRegion(legacy, &aidl)) {
+ return unexpected(BAD_VALUE);
+ }
+ return aidl;
+}
+
+ConversionResult<AudioTimestamp>
+aidl2legacy_AudioTimestampInternal_AudioTimestamp(const media::AudioTimestampInternal& aidl) {
+ AudioTimestamp legacy;
+ legacy.mPosition = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.position));
+ legacy.mTime.tv_sec = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.sec));
+ legacy.mTime.tv_nsec = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.nsec));
+ return legacy;
+}
+
+ConversionResult<media::AudioTimestampInternal>
+legacy2aidl_AudioTimestamp_AudioTimestampInternal(const AudioTimestamp& legacy) {
+ media::AudioTimestampInternal aidl;
+ aidl.position = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.mPosition));
+ aidl.sec = VALUE_OR_RETURN(convertIntegral<int64_t>(legacy.mTime.tv_sec));
+ aidl.nsec = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.mTime.tv_nsec));
+ return aidl;
+}
+
+ConversionResult<audio_uuid_t>
+aidl2legacy_AudioUuid_audio_uuid_t(const media::AudioUuid& aidl) {
+ audio_uuid_t legacy;
+ legacy.timeLow = VALUE_OR_RETURN(convertReinterpret<uint32_t>(aidl.timeLow));
+ legacy.timeMid = VALUE_OR_RETURN(convertIntegral<uint16_t>(aidl.timeMid));
+ legacy.timeHiAndVersion = VALUE_OR_RETURN(convertIntegral<uint16_t>(aidl.timeHiAndVersion));
+ legacy.clockSeq = VALUE_OR_RETURN(convertIntegral<uint16_t>(aidl.clockSeq));
+ if (aidl.node.size() != std::size(legacy.node)) {
+ return unexpected(BAD_VALUE);
+ }
+ std::copy(aidl.node.begin(), aidl.node.end(), legacy.node);
+ return legacy;
+}
+
+ConversionResult<media::AudioUuid>
+legacy2aidl_audio_uuid_t_AudioUuid(const audio_uuid_t& legacy) {
+ media::AudioUuid aidl;
+ aidl.timeLow = VALUE_OR_RETURN(convertReinterpret<int32_t>(legacy.timeLow));
+ aidl.timeMid = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.timeMid));
+ aidl.timeHiAndVersion = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.timeHiAndVersion));
+ aidl.clockSeq = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.clockSeq));
+ std::copy(legacy.node, legacy.node + std::size(legacy.node), std::back_inserter(aidl.node));
+ return aidl;
+}
+
+ConversionResult<effect_descriptor_t>
+aidl2legacy_EffectDescriptor_effect_descriptor_t(const media::EffectDescriptor& aidl) {
+ effect_descriptor_t legacy;
+ legacy.type = VALUE_OR_RETURN(aidl2legacy_AudioUuid_audio_uuid_t(aidl.type));
+ legacy.uuid = VALUE_OR_RETURN(aidl2legacy_AudioUuid_audio_uuid_t(aidl.uuid));
+ legacy.apiVersion = VALUE_OR_RETURN(convertReinterpret<uint32_t>(aidl.apiVersion));
+ legacy.flags = VALUE_OR_RETURN(convertReinterpret<uint32_t>(aidl.flags));
+ legacy.cpuLoad = VALUE_OR_RETURN(convertIntegral<uint16_t>(aidl.cpuLoad));
+ legacy.memoryUsage = VALUE_OR_RETURN(convertIntegral<uint16_t>(aidl.memoryUsage));
+ RETURN_IF_ERROR(aidl2legacy_string(aidl.name, legacy.name, sizeof(legacy.name)));
+ RETURN_IF_ERROR(
+ aidl2legacy_string(aidl.implementor, legacy.implementor, sizeof(legacy.implementor)));
+ return legacy;
+}
+
+ConversionResult<media::EffectDescriptor>
+legacy2aidl_effect_descriptor_t_EffectDescriptor(const effect_descriptor_t& legacy) {
+ media::EffectDescriptor aidl;
+ aidl.type = VALUE_OR_RETURN(legacy2aidl_audio_uuid_t_AudioUuid(legacy.type));
+ aidl.uuid = VALUE_OR_RETURN(legacy2aidl_audio_uuid_t_AudioUuid(legacy.uuid));
+ aidl.apiVersion = VALUE_OR_RETURN(convertReinterpret<int32_t>(legacy.apiVersion));
+ aidl.flags = VALUE_OR_RETURN(convertReinterpret<int32_t>(legacy.flags));
+ aidl.cpuLoad = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.cpuLoad));
+ aidl.memoryUsage = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.memoryUsage));
+ aidl.name = VALUE_OR_RETURN(legacy2aidl_string(legacy.name, sizeof(legacy.name)));
+ aidl.implementor = VALUE_OR_RETURN(
+ legacy2aidl_string(legacy.implementor, sizeof(legacy.implementor)));
+ return aidl;
+}
+
+ConversionResult<audio_encapsulation_metadata_type_t>
+aidl2legacy_AudioEncapsulationMetadataType_audio_encapsulation_metadata_type_t(
+ media::AudioEncapsulationMetadataType aidl) {
+ switch (aidl) {
+ case media::AudioEncapsulationMetadataType::NONE:
+ return AUDIO_ENCAPSULATION_METADATA_TYPE_NONE;
+ case media::AudioEncapsulationMetadataType::FRAMEWORK_TUNER:
+ return AUDIO_ENCAPSULATION_METADATA_TYPE_FRAMEWORK_TUNER;
+ case media::AudioEncapsulationMetadataType::DVB_AD_DESCRIPTOR:
+ return AUDIO_ENCAPSULATION_METADATA_TYPE_DVB_AD_DESCRIPTOR;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioEncapsulationMetadataType>
+legacy2aidl_audio_encapsulation_metadata_type_t_AudioEncapsulationMetadataType(
+ audio_encapsulation_metadata_type_t legacy) {
+ switch (legacy) {
+ case AUDIO_ENCAPSULATION_METADATA_TYPE_NONE:
+ return media::AudioEncapsulationMetadataType::NONE;
+ case AUDIO_ENCAPSULATION_METADATA_TYPE_FRAMEWORK_TUNER:
+ return media::AudioEncapsulationMetadataType::FRAMEWORK_TUNER;
+ case AUDIO_ENCAPSULATION_METADATA_TYPE_DVB_AD_DESCRIPTOR:
+ return media::AudioEncapsulationMetadataType::DVB_AD_DESCRIPTOR;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<uint32_t>
+aidl2legacy_AudioEncapsulationMode_mask(int32_t aidl) {
+ return convertBitmask<uint32_t,
+ int32_t,
+ audio_encapsulation_mode_t,
+ media::AudioEncapsulationMode>(
+ aidl, aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t,
+ indexToEnum_index<media::AudioEncapsulationMode>,
+ enumToMask_index<uint32_t, audio_encapsulation_mode_t>);
+}
+
+ConversionResult<int32_t>
+legacy2aidl_AudioEncapsulationMode_mask(uint32_t legacy) {
+ return convertBitmask<int32_t,
+ uint32_t,
+ media::AudioEncapsulationMode,
+ audio_encapsulation_mode_t>(
+ legacy, legacy2aidl_audio_encapsulation_mode_t_AudioEncapsulationMode,
+ indexToEnum_index<audio_encapsulation_mode_t>,
+ enumToMask_index<int32_t, media::AudioEncapsulationMode>);
+}
+
+ConversionResult<uint32_t>
+aidl2legacy_AudioEncapsulationMetadataType_mask(int32_t aidl) {
+ return convertBitmask<uint32_t,
+ int32_t,
+ audio_encapsulation_metadata_type_t,
+ media::AudioEncapsulationMetadataType>(
+ aidl, aidl2legacy_AudioEncapsulationMetadataType_audio_encapsulation_metadata_type_t,
+ indexToEnum_index<media::AudioEncapsulationMetadataType>,
+ enumToMask_index<uint32_t, audio_encapsulation_metadata_type_t>);
+}
+
+ConversionResult<int32_t>
+legacy2aidl_AudioEncapsulationMetadataType_mask(uint32_t legacy) {
+ return convertBitmask<int32_t,
+ uint32_t,
+ media::AudioEncapsulationMetadataType,
+ audio_encapsulation_metadata_type_t>(
+ legacy, legacy2aidl_audio_encapsulation_metadata_type_t_AudioEncapsulationMetadataType,
+ indexToEnum_index<audio_encapsulation_metadata_type_t>,
+ enumToMask_index<int32_t, media::AudioEncapsulationMetadataType>);
+}
+
+ConversionResult<audio_mix_latency_class_t>
+aidl2legacy_AudioMixLatencyClass_audio_mix_latency_class_t(
+ media::AudioMixLatencyClass aidl) {
+ switch (aidl) {
+ case media::AudioMixLatencyClass::LOW:
+ return AUDIO_LATENCY_LOW;
+ case media::AudioMixLatencyClass::NORMAL:
+ return AUDIO_LATENCY_NORMAL;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioMixLatencyClass>
+legacy2aidl_audio_mix_latency_class_t_AudioMixLatencyClass(
+ audio_mix_latency_class_t legacy) {
+ switch (legacy) {
+ case AUDIO_LATENCY_LOW:
+ return media::AudioMixLatencyClass::LOW;
+ case AUDIO_LATENCY_NORMAL:
+ return media::AudioMixLatencyClass::NORMAL;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_port_device_ext>
+aidl2legacy_AudioPortDeviceExt_audio_port_device_ext(const media::AudioPortDeviceExt& aidl) {
+ audio_port_device_ext legacy;
+ legacy.hw_module = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_module_handle_t(aidl.hwModule));
+ legacy.type = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_devices_t(aidl.device.type));
+ RETURN_IF_ERROR(
+ aidl2legacy_string(aidl.device.address, legacy.address, sizeof(legacy.address)));
+ legacy.encapsulation_modes = VALUE_OR_RETURN(
+ aidl2legacy_AudioEncapsulationMode_mask(aidl.encapsulationModes));
+ legacy.encapsulation_metadata_types = VALUE_OR_RETURN(
+ aidl2legacy_AudioEncapsulationMetadataType_mask(aidl.encapsulationMetadataTypes));
+ return legacy;
+}
+
+ConversionResult<media::AudioPortDeviceExt>
+legacy2aidl_audio_port_device_ext_AudioPortDeviceExt(const audio_port_device_ext& legacy) {
+ media::AudioPortDeviceExt aidl;
+ aidl.hwModule = VALUE_OR_RETURN(legacy2aidl_audio_module_handle_t_int32_t(legacy.hw_module));
+ aidl.device.type = VALUE_OR_RETURN(legacy2aidl_audio_devices_t_int32_t(legacy.type));
+ aidl.device.address = VALUE_OR_RETURN(
+ legacy2aidl_string(legacy.address, sizeof(legacy.address)));
+ aidl.encapsulationModes = VALUE_OR_RETURN(
+ legacy2aidl_AudioEncapsulationMode_mask(legacy.encapsulation_modes));
+ aidl.encapsulationMetadataTypes = VALUE_OR_RETURN(
+ legacy2aidl_AudioEncapsulationMetadataType_mask(legacy.encapsulation_metadata_types));
+ return aidl;
+}
+
+ConversionResult<audio_port_mix_ext>
+aidl2legacy_AudioPortMixExt_audio_port_mix_ext(const media::AudioPortMixExt& aidl) {
+ audio_port_mix_ext legacy;
+ legacy.hw_module = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_module_handle_t(aidl.hwModule));
+ legacy.handle = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_io_handle_t(aidl.handle));
+ legacy.latency_class = VALUE_OR_RETURN(
+ aidl2legacy_AudioMixLatencyClass_audio_mix_latency_class_t(aidl.latencyClass));
+ return legacy;
+}
+
+ConversionResult<media::AudioPortMixExt>
+legacy2aidl_audio_port_mix_ext_AudioPortMixExt(const audio_port_mix_ext& legacy) {
+ media::AudioPortMixExt aidl;
+ aidl.hwModule = VALUE_OR_RETURN(legacy2aidl_audio_module_handle_t_int32_t(legacy.hw_module));
+ aidl.handle = VALUE_OR_RETURN(legacy2aidl_audio_io_handle_t_int32_t(legacy.handle));
+ aidl.latencyClass = VALUE_OR_RETURN(
+ legacy2aidl_audio_mix_latency_class_t_AudioMixLatencyClass(legacy.latency_class));
+ return aidl;
+}
+
+ConversionResult<audio_port_session_ext>
+aidl2legacy_AudioPortSessionExt_audio_port_session_ext(const media::AudioPortSessionExt& aidl) {
+ audio_port_session_ext legacy;
+ legacy.session = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_session_t(aidl.session));
+ return legacy;
+}
+
+ConversionResult<media::AudioPortSessionExt>
+legacy2aidl_audio_port_session_ext_AudioPortSessionExt(const audio_port_session_ext& legacy) {
+ media::AudioPortSessionExt aidl;
+ aidl.session = VALUE_OR_RETURN(legacy2aidl_audio_session_t_int32_t(legacy.session));
+ return aidl;
+}
+
+// This type is unnamed in the original definition, thus we name it here.
+using audio_port_v7_ext = decltype(audio_port_v7::ext);
+
+ConversionResult<audio_port_v7_ext> aidl2legacy_AudioPortExt(
+ const media::AudioPortExt& aidl, media::AudioPortType type) {
+ audio_port_v7_ext legacy;
+ switch (type) {
+ case media::AudioPortType::NONE:
+ // Just verify that the union is empty.
+ VALUE_OR_RETURN(UNION_GET(aidl, unspecified));
+ return legacy;
+ case media::AudioPortType::DEVICE:
+ legacy.device = VALUE_OR_RETURN(
+ aidl2legacy_AudioPortDeviceExt_audio_port_device_ext(
+ VALUE_OR_RETURN(UNION_GET(aidl, device))));
+ return legacy;
+ case media::AudioPortType::MIX:
+ legacy.mix = VALUE_OR_RETURN(
+ aidl2legacy_AudioPortMixExt_audio_port_mix_ext(
+ VALUE_OR_RETURN(UNION_GET(aidl, mix))));
+ return legacy;
+ case media::AudioPortType::SESSION:
+ legacy.session = VALUE_OR_RETURN(aidl2legacy_AudioPortSessionExt_audio_port_session_ext(
+ VALUE_OR_RETURN(UNION_GET(aidl, session))));
+ return legacy;
+
+ }
+ LOG_ALWAYS_FATAL("Shouldn't get here"); // with -Werror,-Wswitch may compile-time fail
+}
+
+ConversionResult<media::AudioPortExt> legacy2aidl_AudioPortExt(
+ const audio_port_v7_ext& legacy, audio_port_type_t type) {
+ media::AudioPortExt aidl;
+ switch (type) {
+ case AUDIO_PORT_TYPE_NONE:
+ UNION_SET(aidl, unspecified, false);
+ return aidl;
+ case AUDIO_PORT_TYPE_DEVICE:
+ UNION_SET(aidl, device,
+ VALUE_OR_RETURN(
+ legacy2aidl_audio_port_device_ext_AudioPortDeviceExt(legacy.device)));
+ return aidl;
+ case AUDIO_PORT_TYPE_MIX:
+ UNION_SET(aidl, mix,
+ VALUE_OR_RETURN(legacy2aidl_audio_port_mix_ext_AudioPortMixExt(legacy.mix)));
+ return aidl;
+ case AUDIO_PORT_TYPE_SESSION:
+ UNION_SET(aidl, session,
+ VALUE_OR_RETURN(legacy2aidl_audio_port_session_ext_AudioPortSessionExt(
+ legacy.session)));
+ return aidl;
+ }
+ LOG_ALWAYS_FATAL("Shouldn't get here"); // with -Werror,-Wswitch may compile-time fail
+}
+
+ConversionResult<audio_profile>
+aidl2legacy_AudioProfile_audio_profile(const media::AudioProfile& aidl) {
+ audio_profile legacy;
+ legacy.format = VALUE_OR_RETURN(aidl2legacy_AudioFormat_audio_format_t(aidl.format));
+
+ if (aidl.samplingRates.size() > std::size(legacy.sample_rates)) {
+ return unexpected(BAD_VALUE);
+ }
+ RETURN_IF_ERROR(
+ convertRange(aidl.samplingRates.begin(), aidl.samplingRates.end(), legacy.sample_rates,
+ convertIntegral<int32_t, unsigned int>));
+ legacy.num_sample_rates = aidl.samplingRates.size();
+
+ if (aidl.channelMasks.size() > std::size(legacy.channel_masks)) {
+ return unexpected(BAD_VALUE);
+ }
+ RETURN_IF_ERROR(
+ convertRange(aidl.channelMasks.begin(), aidl.channelMasks.end(), legacy.channel_masks,
+ aidl2legacy_int32_t_audio_channel_mask_t));
+ legacy.num_channel_masks = aidl.channelMasks.size();
+
+ legacy.encapsulation_type = VALUE_OR_RETURN(
+ aidl2legacy_AudioEncapsulationType_audio_encapsulation_type_t(aidl.encapsulationType));
+ return legacy;
+}
+
+ConversionResult<media::AudioProfile>
+legacy2aidl_audio_profile_AudioProfile(const audio_profile& legacy) {
+ media::AudioProfile aidl;
+ aidl.format = VALUE_OR_RETURN(legacy2aidl_audio_format_t_AudioFormat(legacy.format));
+
+ if (legacy.num_sample_rates > std::size(legacy.sample_rates)) {
+ return unexpected(BAD_VALUE);
+ }
+ RETURN_IF_ERROR(
+ convertRange(legacy.sample_rates, legacy.sample_rates + legacy.num_sample_rates,
+ std::back_inserter(aidl.samplingRates),
+ convertIntegral<unsigned int, int32_t>));
+
+ if (legacy.num_channel_masks > std::size(legacy.channel_masks)) {
+ return unexpected(BAD_VALUE);
+ }
+ RETURN_IF_ERROR(
+ convertRange(legacy.channel_masks, legacy.channel_masks + legacy.num_channel_masks,
+ std::back_inserter(aidl.channelMasks),
+ legacy2aidl_audio_channel_mask_t_int32_t));
+
+ aidl.encapsulationType = VALUE_OR_RETURN(
+ legacy2aidl_audio_encapsulation_type_t_AudioEncapsulationType(
+ legacy.encapsulation_type));
+ return aidl;
+}
+
+ConversionResult<audio_gain>
+aidl2legacy_AudioGain_audio_gain(const media::AudioGain& aidl) {
+ audio_gain legacy;
+ legacy.mode = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_gain_mode_t_mask(aidl.mode));
+ legacy.channel_mask = VALUE_OR_RETURN(
+ aidl2legacy_int32_t_audio_channel_mask_t(aidl.channelMask));
+ legacy.min_value = VALUE_OR_RETURN(convertIntegral<int>(aidl.minValue));
+ legacy.max_value = VALUE_OR_RETURN(convertIntegral<int>(aidl.maxValue));
+ legacy.default_value = VALUE_OR_RETURN(convertIntegral<int>(aidl.defaultValue));
+ legacy.step_value = VALUE_OR_RETURN(convertIntegral<unsigned int>(aidl.stepValue));
+ legacy.min_ramp_ms = VALUE_OR_RETURN(convertIntegral<unsigned int>(aidl.minRampMs));
+ legacy.max_ramp_ms = VALUE_OR_RETURN(convertIntegral<unsigned int>(aidl.maxRampMs));
+ return legacy;
+}
+
+ConversionResult<media::AudioGain>
+legacy2aidl_audio_gain_AudioGain(const audio_gain& legacy) {
+ media::AudioGain aidl;
+ aidl.mode = VALUE_OR_RETURN(legacy2aidl_audio_gain_mode_t_int32_t_mask(legacy.mode));
+ aidl.channelMask = VALUE_OR_RETURN(
+ legacy2aidl_audio_channel_mask_t_int32_t(legacy.channel_mask));
+ aidl.minValue = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.min_value));
+ aidl.maxValue = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.max_value));
+ aidl.defaultValue = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.default_value));
+ aidl.stepValue = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.step_value));
+ aidl.minRampMs = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.min_ramp_ms));
+ aidl.maxRampMs = VALUE_OR_RETURN(convertIntegral<int32_t>(legacy.max_ramp_ms));
+ return aidl;
+}
+
+ConversionResult<audio_port_v7>
+aidl2legacy_AudioPort_audio_port_v7(const media::AudioPort& aidl) {
+ audio_port_v7 legacy;
+ legacy.id = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_port_handle_t(aidl.id));
+ legacy.role = VALUE_OR_RETURN(aidl2legacy_AudioPortRole_audio_port_role_t(aidl.role));
+ legacy.type = VALUE_OR_RETURN(aidl2legacy_AudioPortType_audio_port_type_t(aidl.type));
+ RETURN_IF_ERROR(aidl2legacy_string(aidl.name, legacy.name, sizeof(legacy.name)));
+
+ if (aidl.profiles.size() > std::size(legacy.audio_profiles)) {
+ return unexpected(BAD_VALUE);
+ }
+ RETURN_IF_ERROR(convertRange(aidl.profiles.begin(), aidl.profiles.end(), legacy.audio_profiles,
+ aidl2legacy_AudioProfile_audio_profile));
+ legacy.num_audio_profiles = aidl.profiles.size();
+
+ if (aidl.extraAudioDescriptors.size() > std::size(legacy.extra_audio_descriptors)) {
+ return unexpected(BAD_VALUE);
+ }
+ RETURN_IF_ERROR(
+ convertRange(aidl.extraAudioDescriptors.begin(), aidl.extraAudioDescriptors.end(),
+ legacy.extra_audio_descriptors,
+ aidl2legacy_ExtraAudioDescriptor_audio_extra_audio_descriptor));
+ legacy.num_extra_audio_descriptors = aidl.extraAudioDescriptors.size();
+
+ if (aidl.gains.size() > std::size(legacy.gains)) {
+ return unexpected(BAD_VALUE);
+ }
+ RETURN_IF_ERROR(convertRange(aidl.gains.begin(), aidl.gains.end(), legacy.gains,
+ aidl2legacy_AudioGain_audio_gain));
+ legacy.num_gains = aidl.gains.size();
+
+ legacy.active_config = VALUE_OR_RETURN(
+ aidl2legacy_AudioPortConfig_audio_port_config(aidl.activeConfig));
+ legacy.ext = VALUE_OR_RETURN(aidl2legacy_AudioPortExt(aidl.ext, aidl.type));
+ return legacy;
+}
+
+ConversionResult<media::AudioPort>
+legacy2aidl_audio_port_v7_AudioPort(const audio_port_v7& legacy) {
+ media::AudioPort aidl;
+ aidl.id = VALUE_OR_RETURN(legacy2aidl_audio_port_handle_t_int32_t(legacy.id));
+ aidl.role = VALUE_OR_RETURN(legacy2aidl_audio_port_role_t_AudioPortRole(legacy.role));
+ aidl.type = VALUE_OR_RETURN(legacy2aidl_audio_port_type_t_AudioPortType(legacy.type));
+ aidl.name = VALUE_OR_RETURN(legacy2aidl_string(legacy.name, sizeof(legacy.name)));
+
+ if (legacy.num_audio_profiles > std::size(legacy.audio_profiles)) {
+ return unexpected(BAD_VALUE);
+ }
+ RETURN_IF_ERROR(
+ convertRange(legacy.audio_profiles, legacy.audio_profiles + legacy.num_audio_profiles,
+ std::back_inserter(aidl.profiles),
+ legacy2aidl_audio_profile_AudioProfile));
+
+ if (legacy.num_extra_audio_descriptors > std::size(legacy.extra_audio_descriptors)) {
+ return unexpected(BAD_VALUE);
+ }
+ RETURN_IF_ERROR(
+ convertRange(legacy.extra_audio_descriptors,
+ legacy.extra_audio_descriptors + legacy.num_extra_audio_descriptors,
+ std::back_inserter(aidl.extraAudioDescriptors),
+ legacy2aidl_audio_extra_audio_descriptor_ExtraAudioDescriptor));
+
+ if (legacy.num_gains > std::size(legacy.gains)) {
+ return unexpected(BAD_VALUE);
+ }
+ RETURN_IF_ERROR(
+ convertRange(legacy.gains, legacy.gains + legacy.num_gains,
+ std::back_inserter(aidl.gains),
+ legacy2aidl_audio_gain_AudioGain));
+
+ aidl.activeConfig = VALUE_OR_RETURN(
+ legacy2aidl_audio_port_config_AudioPortConfig(legacy.active_config));
+ aidl.ext = VALUE_OR_RETURN(legacy2aidl_AudioPortExt(legacy.ext, legacy.type));
+ return aidl;
+}
+
+ConversionResult<audio_mode_t>
+aidl2legacy_AudioMode_audio_mode_t(media::AudioMode aidl) {
+ switch (aidl) {
+ case media::AudioMode::INVALID:
+ return AUDIO_MODE_INVALID;
+ case media::AudioMode::CURRENT:
+ return AUDIO_MODE_CURRENT;
+ case media::AudioMode::NORMAL:
+ return AUDIO_MODE_NORMAL;
+ case media::AudioMode::RINGTONE:
+ return AUDIO_MODE_RINGTONE;
+ case media::AudioMode::IN_CALL:
+ return AUDIO_MODE_IN_CALL;
+ case media::AudioMode::IN_COMMUNICATION:
+ return AUDIO_MODE_IN_COMMUNICATION;
+ case media::AudioMode::CALL_SCREEN:
+ return AUDIO_MODE_CALL_SCREEN;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioMode>
+legacy2aidl_audio_mode_t_AudioMode(audio_mode_t legacy) {
+ switch (legacy) {
+ case AUDIO_MODE_INVALID:
+ return media::AudioMode::INVALID;
+ case AUDIO_MODE_CURRENT:
+ return media::AudioMode::CURRENT;
+ case AUDIO_MODE_NORMAL:
+ return media::AudioMode::NORMAL;
+ case AUDIO_MODE_RINGTONE:
+ return media::AudioMode::RINGTONE;
+ case AUDIO_MODE_IN_CALL:
+ return media::AudioMode::IN_CALL;
+ case AUDIO_MODE_IN_COMMUNICATION:
+ return media::AudioMode::IN_COMMUNICATION;
+ case AUDIO_MODE_CALL_SCREEN:
+ return media::AudioMode::CALL_SCREEN;
+ case AUDIO_MODE_CNT:
+ break;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_unique_id_use_t>
+aidl2legacy_AudioUniqueIdUse_audio_unique_id_use_t(media::AudioUniqueIdUse aidl) {
+ switch (aidl) {
+ case media::AudioUniqueIdUse::UNSPECIFIED:
+ return AUDIO_UNIQUE_ID_USE_UNSPECIFIED;
+ case media::AudioUniqueIdUse::SESSION:
+ return AUDIO_UNIQUE_ID_USE_SESSION;
+ case media::AudioUniqueIdUse::MODULE:
+ return AUDIO_UNIQUE_ID_USE_MODULE;
+ case media::AudioUniqueIdUse::EFFECT:
+ return AUDIO_UNIQUE_ID_USE_EFFECT;
+ case media::AudioUniqueIdUse::PATCH:
+ return AUDIO_UNIQUE_ID_USE_PATCH;
+ case media::AudioUniqueIdUse::OUTPUT:
+ return AUDIO_UNIQUE_ID_USE_OUTPUT;
+ case media::AudioUniqueIdUse::INPUT:
+ return AUDIO_UNIQUE_ID_USE_INPUT;
+ case media::AudioUniqueIdUse::CLIENT:
+ return AUDIO_UNIQUE_ID_USE_CLIENT;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioUniqueIdUse>
+legacy2aidl_audio_unique_id_use_t_AudioUniqueIdUse(audio_unique_id_use_t legacy) {
+ switch (legacy) {
+ case AUDIO_UNIQUE_ID_USE_UNSPECIFIED:
+ return media::AudioUniqueIdUse::UNSPECIFIED;
+ case AUDIO_UNIQUE_ID_USE_SESSION:
+ return media::AudioUniqueIdUse::SESSION;
+ case AUDIO_UNIQUE_ID_USE_MODULE:
+ return media::AudioUniqueIdUse::MODULE;
+ case AUDIO_UNIQUE_ID_USE_EFFECT:
+ return media::AudioUniqueIdUse::EFFECT;
+ case AUDIO_UNIQUE_ID_USE_PATCH:
+ return media::AudioUniqueIdUse::PATCH;
+ case AUDIO_UNIQUE_ID_USE_OUTPUT:
+ return media::AudioUniqueIdUse::OUTPUT;
+ case AUDIO_UNIQUE_ID_USE_INPUT:
+ return media::AudioUniqueIdUse::INPUT;
+ case AUDIO_UNIQUE_ID_USE_CLIENT:
+ return media::AudioUniqueIdUse::CLIENT;
+ case AUDIO_UNIQUE_ID_USE_MAX:
+ break;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<volume_group_t>
+aidl2legacy_int32_t_volume_group_t(int32_t aidl) {
+ return convertReinterpret<volume_group_t>(aidl);
+}
+
+ConversionResult<int32_t>
+legacy2aidl_volume_group_t_int32_t(volume_group_t legacy) {
+ return convertReinterpret<int32_t>(legacy);
+}
+
+ConversionResult<product_strategy_t>
+aidl2legacy_int32_t_product_strategy_t(int32_t aidl) {
+ return convertReinterpret<product_strategy_t>(aidl);
+}
+
+ConversionResult<int32_t>
+legacy2aidl_product_strategy_t_int32_t(product_strategy_t legacy) {
+ return convertReinterpret<int32_t>(legacy);
+}
+
+ConversionResult<audio_dual_mono_mode_t>
+aidl2legacy_AudioDualMonoMode_audio_dual_mono_mode_t(media::AudioDualMonoMode aidl) {
+ switch (aidl) {
+ case media::AudioDualMonoMode::OFF:
+ return AUDIO_DUAL_MONO_MODE_OFF;
+ case media::AudioDualMonoMode::LR:
+ return AUDIO_DUAL_MONO_MODE_LR;
+ case media::AudioDualMonoMode::LL:
+ return AUDIO_DUAL_MONO_MODE_LL;
+ case media::AudioDualMonoMode::RR:
+ return AUDIO_DUAL_MONO_MODE_RR;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioDualMonoMode>
+legacy2aidl_audio_dual_mono_mode_t_AudioDualMonoMode(audio_dual_mono_mode_t legacy) {
+ switch (legacy) {
+ case AUDIO_DUAL_MONO_MODE_OFF:
+ return media::AudioDualMonoMode::OFF;
+ case AUDIO_DUAL_MONO_MODE_LR:
+ return media::AudioDualMonoMode::LR;
+ case AUDIO_DUAL_MONO_MODE_LL:
+ return media::AudioDualMonoMode::LL;
+ case AUDIO_DUAL_MONO_MODE_RR:
+ return media::AudioDualMonoMode::RR;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_timestretch_fallback_mode_t>
+aidl2legacy_int32_t_audio_timestretch_fallback_mode_t(int32_t aidl) {
+ return convertReinterpret<audio_timestretch_fallback_mode_t>(aidl);
+}
+
+ConversionResult<int32_t>
+legacy2aidl_audio_timestretch_fallback_mode_t_int32_t(audio_timestretch_fallback_mode_t legacy) {
+ return convertReinterpret<int32_t>(legacy);
+}
+
+ConversionResult<audio_timestretch_stretch_mode_t>
+aidl2legacy_int32_t_audio_timestretch_stretch_mode_t(int32_t aidl) {
+ return convertReinterpret<audio_timestretch_stretch_mode_t>(aidl);
+}
+
+ConversionResult<int32_t>
+legacy2aidl_audio_timestretch_stretch_mode_t_int32_t(audio_timestretch_stretch_mode_t legacy) {
+ return convertReinterpret<int32_t>(legacy);
+}
+
+ConversionResult<audio_playback_rate_t>
+aidl2legacy_AudioPlaybackRate_audio_playback_rate_t(const media::AudioPlaybackRate& aidl) {
+ audio_playback_rate_t legacy;
+ legacy.mSpeed = aidl.speed;
+ legacy.mPitch = aidl.pitch;
+ legacy.mFallbackMode = VALUE_OR_RETURN(
+ aidl2legacy_int32_t_audio_timestretch_fallback_mode_t(aidl.fallbackMode));
+ legacy.mStretchMode = VALUE_OR_RETURN(
+ aidl2legacy_int32_t_audio_timestretch_stretch_mode_t(aidl.stretchMode));
+ return legacy;
+}
+
+ConversionResult<media::AudioPlaybackRate>
+legacy2aidl_audio_playback_rate_t_AudioPlaybackRate(const audio_playback_rate_t& legacy) {
+ media::AudioPlaybackRate aidl;
+ aidl.speed = legacy.mSpeed;
+ aidl.pitch = legacy.mPitch;
+ aidl.fallbackMode = VALUE_OR_RETURN(
+ legacy2aidl_audio_timestretch_fallback_mode_t_int32_t(legacy.mFallbackMode));
+ aidl.stretchMode = VALUE_OR_RETURN(
+ legacy2aidl_audio_timestretch_stretch_mode_t_int32_t(legacy.mStretchMode));
+ return aidl;
+}
+
+ConversionResult<audio_standard_t>
+aidl2legacy_AudioStandard_audio_standard_t(media::AudioStandard aidl) {
+ switch (aidl) {
+ case media::AudioStandard::NONE:
+ return AUDIO_STANDARD_NONE;
+ case media::AudioStandard::EDID:
+ return AUDIO_STANDARD_EDID;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioStandard>
+legacy2aidl_audio_standard_t_AudioStandard(audio_standard_t legacy) {
+ switch (legacy) {
+ case AUDIO_STANDARD_NONE:
+ return media::AudioStandard::NONE;
+ case AUDIO_STANDARD_EDID:
+ return media::AudioStandard::EDID;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_extra_audio_descriptor>
+aidl2legacy_ExtraAudioDescriptor_audio_extra_audio_descriptor(
+ const media::ExtraAudioDescriptor& aidl) {
+ audio_extra_audio_descriptor legacy;
+ legacy.standard = VALUE_OR_RETURN(aidl2legacy_AudioStandard_audio_standard_t(aidl.standard));
+ if (aidl.audioDescriptor.size() > EXTRA_AUDIO_DESCRIPTOR_SIZE) {
+ return unexpected(BAD_VALUE);
+ }
+ legacy.descriptor_length = aidl.audioDescriptor.size();
+ std::copy(aidl.audioDescriptor.begin(), aidl.audioDescriptor.end(),
+ std::begin(legacy.descriptor));
+ legacy.encapsulation_type =
+ VALUE_OR_RETURN(aidl2legacy_AudioEncapsulationType_audio_encapsulation_type_t(
+ aidl.encapsulationType));
+ return legacy;
+}
+
+ConversionResult<media::ExtraAudioDescriptor>
+legacy2aidl_audio_extra_audio_descriptor_ExtraAudioDescriptor(
+ const audio_extra_audio_descriptor& legacy) {
+ media::ExtraAudioDescriptor aidl;
+ aidl.standard = VALUE_OR_RETURN(legacy2aidl_audio_standard_t_AudioStandard(legacy.standard));
+ if (legacy.descriptor_length > EXTRA_AUDIO_DESCRIPTOR_SIZE) {
+ return unexpected(BAD_VALUE);
+ }
+ aidl.audioDescriptor.resize(legacy.descriptor_length);
+ std::copy(legacy.descriptor, legacy.descriptor + legacy.descriptor_length,
+ aidl.audioDescriptor.begin());
+ aidl.encapsulationType =
+ VALUE_OR_RETURN(legacy2aidl_audio_encapsulation_type_t_AudioEncapsulationType(
+ legacy.encapsulation_type));
+ return aidl;
+}
+
+ConversionResult<audio_encapsulation_type_t>
+aidl2legacy_AudioEncapsulationType_audio_encapsulation_type_t(
+ const media::AudioEncapsulationType& aidl) {
+ switch (aidl) {
+ case media::AudioEncapsulationType::NONE:
+ return AUDIO_ENCAPSULATION_TYPE_NONE;
+ case media::AudioEncapsulationType::IEC61937:
+ return AUDIO_ENCAPSULATION_TYPE_IEC61937;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioEncapsulationType>
+legacy2aidl_audio_encapsulation_type_t_AudioEncapsulationType(
+ const audio_encapsulation_type_t & legacy) {
+ switch (legacy) {
+ case AUDIO_ENCAPSULATION_TYPE_NONE:
+ return media::AudioEncapsulationType::NONE;
+ case AUDIO_ENCAPSULATION_TYPE_IEC61937:
+ return media::AudioEncapsulationType::IEC61937;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+} // namespace android
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index 0c11e0f..19d68a0 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -11,6 +11,7 @@
name: "libaudioclient_headers",
vendor_available: true,
min_sdk_version: "29",
+ host_supported: true,
header_libs: [
"libaudiofoundation_headers",
@@ -21,7 +22,16 @@
export_header_lib_headers: [
"libaudiofoundation_headers",
],
- host_supported: true,
+ static_libs: [
+ "audioflinger-aidl-cpp",
+ "audiopolicy-aidl-cpp",
+ "av-types-aidl-cpp",
+ ],
+ export_static_lib_headers: [
+ "audioflinger-aidl-cpp",
+ "audiopolicy-aidl-cpp",
+ "av-types-aidl-cpp",
+ ],
target: {
darwin: {
enabled: false,
@@ -36,10 +46,16 @@
"AudioPolicy.cpp",
"AudioProductStrategy.cpp",
"AudioVolumeGroup.cpp",
+ "PolicyAidlConversion.cpp"
],
shared_libs: [
+ "audioclient-types-aidl-cpp",
+ "audioflinger-aidl-cpp",
+ "audiopolicy-aidl-cpp",
+ "audiopolicy-types-aidl-cpp",
"capture_state_listener-aidl-cpp",
"libaudiofoundation",
+ "libaudioclient_aidl_conversion",
"libaudioutils",
"libbinder",
"libcutils",
@@ -53,8 +69,15 @@
include_dirs: ["system/media/audio_utils/include"],
export_include_dirs: ["include"],
export_shared_lib_headers: [
+ "audioclient-types-aidl-cpp",
+ "audioflinger-aidl-cpp",
+ "audiopolicy-aidl-cpp",
+ "audiopolicy-types-aidl-cpp",
"capture_state_listener-aidl-cpp",
+ "libaudiofoundation",
+ "libaudioclient_aidl_conversion",
],
+ header_libs: ["libaudioclient_headers"],
}
cc_library {
@@ -64,7 +87,7 @@
export_aidl_headers: true,
local_include_dirs: ["aidl"],
include_dirs: [
- "frameworks/av/media/libaudioclient/aidl",
+ "frameworks/av/aidl",
],
},
@@ -72,30 +95,27 @@
// AIDL files for audioclient interfaces
// The headers for these interfaces will be available to any modules that
// include libaudioclient, at the path "aidl/package/path/BnFoo.h"
- ":libaudioclient_aidl_callback",
- ":libaudioclient_aidl_private",
":libaudioclient_aidl",
"AudioEffect.cpp",
"AudioRecord.cpp",
- "AudioSanitizer.cpp",
"AudioSystem.cpp",
"AudioTrack.cpp",
"AudioTrackShared.cpp",
"IAudioFlinger.cpp",
- "IAudioFlingerClient.cpp",
- "IAudioPolicyService.cpp",
- "IAudioPolicyServiceClient.cpp",
- "IAudioTrack.cpp",
- "IEffect.cpp",
- "IEffectClient.cpp",
"ToneGenerator.cpp",
"PlayerBase.cpp",
"RecordingActivityTracker.cpp",
"TrackPlayerBase.cpp",
],
shared_libs: [
+ "audioclient-types-aidl-cpp",
+ "audioflinger-aidl-cpp",
+ "audiopolicy-aidl-cpp",
+ "audiopolicy-types-aidl-cpp",
+ "av-types-aidl-cpp",
"capture_state_listener-aidl-cpp",
+ "libaudioclient_aidl_conversion",
"libaudiofoundation",
"libaudioutils",
"libaudiopolicy",
@@ -109,16 +129,24 @@
"libmediautils",
"libnblog",
"libprocessgroup",
+ "libshmemcompat",
"libutils",
"libvibrator",
+ "media_permission-aidl-cpp",
],
- export_shared_lib_headers: ["libbinder"],
+ export_shared_lib_headers: [
+ "audioflinger-aidl-cpp",
+ "audiopolicy-aidl-cpp",
+ "media_permission-aidl-cpp",
+ "libbinder",
+ ],
include_dirs: [
"frameworks/av/media/libnbaio/include_mono/",
],
local_include_dirs: [
- "include/media", "aidl"
+ "include/media",
+ "aidl",
],
header_libs: [
"libaudioclient_headers",
@@ -126,10 +154,17 @@
"libmedia_headers",
],
export_header_lib_headers: ["libaudioclient_headers"],
+ export_static_lib_headers: [
+ "effect-aidl-cpp",
+ "shared-file-region-aidl-cpp",
+ ],
- // for memory heap analysis
static_libs: [
+ "effect-aidl-cpp",
+ // for memory heap analysis
"libc_malloc_debug_backtrace",
+ "shared-file-region-aidl-cpp",
+ "media_permission-aidl-cpp",
],
cflags: [
"-Wall",
@@ -137,13 +172,91 @@
"-Wno-error=deprecated-declarations",
],
sanitize: {
- misc_undefined : [
+ misc_undefined: [
"unsigned-integer-overflow",
"signed-integer-overflow",
],
},
}
+// This is intended for clients needing to include AidlConversionUtil.h, without dragging in a lot of extra
+// dependencies.
+cc_library_headers {
+ name: "libaudioclient_aidl_conversion_util",
+ host_supported: true,
+ vendor_available: true,
+ double_loadable: true,
+ min_sdk_version: "29",
+ export_include_dirs: [
+ "include",
+ ],
+ header_libs: [
+ "libbase_headers",
+ ],
+ export_header_lib_headers: [
+ "libbase_headers",
+ ],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.bluetooth.updatable",
+ "com.android.media",
+ "com.android.media.swcodec",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
+
+cc_library {
+ name: "libaudioclient_aidl_conversion",
+ srcs: ["AidlConversion.cpp"],
+ export_include_dirs: ["include"],
+ host_supported: true,
+ vendor_available: true,
+ double_loadable: true,
+ min_sdk_version: "29",
+ header_libs: [
+ "libaudioclient_aidl_conversion_util",
+ "libaudio_system_headers",
+ ],
+ export_header_lib_headers: [
+ "libaudioclient_aidl_conversion_util",
+ ],
+ shared_libs: [
+ "audioclient-types-aidl-cpp",
+ "libbase",
+ "libbinder",
+ "liblog",
+ "libshmemcompat",
+ "libutils",
+ "shared-file-region-aidl-cpp",
+ "media_permission-aidl-cpp",
+ ],
+ export_shared_lib_headers: [
+ "audioclient-types-aidl-cpp",
+ "libbase",
+ "shared-file-region-aidl-cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-error=deprecated-declarations",
+ ],
+ sanitize: {
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
+
// AIDL interface between libaudioclient and framework.jar
filegroup {
name: "libaudioclient_aidl",
@@ -153,30 +266,220 @@
path: "aidl",
}
-// Used to strip the "aidl/" from the path, so the build system can predict the
-// output filename.
-filegroup {
- name: "libaudioclient_aidl_private",
- srcs: [
- "aidl/android/media/IAudioRecord.aidl",
- ],
- path: "aidl",
-}
-
-// AIDL interface for audio track callback
-filegroup {
- name: "libaudioclient_aidl_callback",
- srcs: [
- "aidl/android/media/IAudioTrackCallback.aidl",
- ],
- path: "aidl",
-}
-
aidl_interface {
name: "capture_state_listener-aidl",
unstable: true,
local_include_dir: "aidl",
+ host_supported: true,
+ double_loadable: true,
+ vendor_available: true,
srcs: [
"aidl/android/media/ICaptureStateListener.aidl",
],
}
+
+aidl_interface {
+ name: "effect-aidl",
+ unstable: true,
+ local_include_dir: "aidl",
+ host_supported: true,
+ double_loadable: true,
+ vendor_available: true,
+ srcs: [
+ "aidl/android/media/IEffect.aidl",
+ "aidl/android/media/IEffectClient.aidl",
+ ],
+ imports: [
+ "shared-file-region-aidl",
+ ],
+}
+
+aidl_interface {
+ name: "audioclient-types-aidl",
+ unstable: true,
+ host_supported: true,
+ vendor_available: true,
+ double_loadable: true,
+ local_include_dir: "aidl",
+ srcs: [
+ "aidl/android/media/AudioAttributesInternal.aidl",
+ "aidl/android/media/AudioClient.aidl",
+ "aidl/android/media/AudioConfig.aidl",
+ "aidl/android/media/AudioConfigBase.aidl",
+ "aidl/android/media/AudioContentType.aidl",
+ "aidl/android/media/AudioDevice.aidl",
+ "aidl/android/media/AudioDualMonoMode.aidl",
+ "aidl/android/media/AudioEncapsulationMode.aidl",
+ "aidl/android/media/AudioEncapsulationMetadataType.aidl",
+ "aidl/android/media/AudioEncapsulationType.aidl",
+ "aidl/android/media/AudioFlag.aidl",
+ "aidl/android/media/AudioGain.aidl",
+ "aidl/android/media/AudioGainConfig.aidl",
+ "aidl/android/media/AudioGainMode.aidl",
+ "aidl/android/media/AudioInputFlags.aidl",
+ "aidl/android/media/AudioIoConfigEvent.aidl",
+ "aidl/android/media/AudioIoDescriptor.aidl",
+ "aidl/android/media/AudioIoFlags.aidl",
+ "aidl/android/media/AudioMixLatencyClass.aidl",
+ "aidl/android/media/AudioMode.aidl",
+ "aidl/android/media/AudioOffloadInfo.aidl",
+ "aidl/android/media/AudioOutputFlags.aidl",
+ "aidl/android/media/AudioPatch.aidl",
+ "aidl/android/media/AudioPlaybackRate.aidl",
+ "aidl/android/media/AudioPort.aidl",
+ "aidl/android/media/AudioPortConfig.aidl",
+ "aidl/android/media/AudioPortConfigType.aidl",
+ "aidl/android/media/AudioPortConfigDeviceExt.aidl",
+ "aidl/android/media/AudioPortConfigExt.aidl",
+ "aidl/android/media/AudioPortConfigMixExt.aidl",
+ "aidl/android/media/AudioPortConfigMixExtUseCase.aidl",
+ "aidl/android/media/AudioPortConfigSessionExt.aidl",
+ "aidl/android/media/AudioPortDeviceExt.aidl",
+ "aidl/android/media/AudioPortExt.aidl",
+ "aidl/android/media/AudioPortMixExt.aidl",
+ "aidl/android/media/AudioPortRole.aidl",
+ "aidl/android/media/AudioPortSessionExt.aidl",
+ "aidl/android/media/AudioPortType.aidl",
+ "aidl/android/media/AudioProfile.aidl",
+ "aidl/android/media/AudioSourceType.aidl",
+ "aidl/android/media/AudioStandard.aidl",
+ "aidl/android/media/AudioStreamType.aidl",
+ "aidl/android/media/AudioTimestampInternal.aidl",
+ "aidl/android/media/AudioUniqueIdUse.aidl",
+ "aidl/android/media/AudioUsage.aidl",
+ "aidl/android/media/AudioUuid.aidl",
+ "aidl/android/media/AudioVibratorInfo.aidl",
+ "aidl/android/media/EffectDescriptor.aidl",
+ "aidl/android/media/ExtraAudioDescriptor.aidl",
+ ],
+ imports: [
+ "audio_common-aidl",
+ "media_permission-aidl",
+ ],
+ backend: {
+ cpp: {
+ min_sdk_version: "29",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media",
+ ],
+ },
+ },
+}
+aidl_interface {
+ name: "audiopolicy-types-aidl",
+ unstable: true,
+ host_supported: true,
+ vendor_available: true,
+ double_loadable: true,
+ local_include_dir: "aidl",
+ srcs: [
+ "aidl/android/media/AudioAttributesEx.aidl",
+ "aidl/android/media/AudioMix.aidl",
+ "aidl/android/media/AudioMixCallbackFlag.aidl",
+ "aidl/android/media/AudioMixMatchCriterion.aidl",
+ "aidl/android/media/AudioMixMatchCriterionValue.aidl",
+ "aidl/android/media/AudioMixRouteFlag.aidl",
+ "aidl/android/media/AudioMixType.aidl",
+ "aidl/android/media/AudioOffloadMode.aidl",
+ "aidl/android/media/AudioPolicyDeviceState.aidl",
+ "aidl/android/media/AudioPolicyForceUse.aidl",
+ "aidl/android/media/AudioPolicyForcedConfig.aidl",
+ "aidl/android/media/AudioProductStrategy.aidl",
+ "aidl/android/media/AudioVolumeGroup.aidl",
+ "aidl/android/media/DeviceRole.aidl",
+ "aidl/android/media/SoundTriggerSession.aidl",
+ ],
+ imports: [
+ "audio_common-aidl",
+ "audioclient-types-aidl",
+ ],
+ backend: {
+ cpp: {
+ min_sdk_version: "29",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media",
+ ],
+ },
+ },
+}
+
+aidl_interface {
+ name: "audioflinger-aidl",
+ unstable: true,
+ local_include_dir: "aidl",
+ host_supported: true,
+ vendor_available: true,
+ srcs: [
+ "aidl/android/media/CreateEffectRequest.aidl",
+ "aidl/android/media/CreateEffectResponse.aidl",
+ "aidl/android/media/CreateRecordRequest.aidl",
+ "aidl/android/media/CreateRecordResponse.aidl",
+ "aidl/android/media/CreateTrackRequest.aidl",
+ "aidl/android/media/CreateTrackResponse.aidl",
+ "aidl/android/media/OpenInputRequest.aidl",
+ "aidl/android/media/OpenInputResponse.aidl",
+ "aidl/android/media/OpenOutputRequest.aidl",
+ "aidl/android/media/OpenOutputResponse.aidl",
+ "aidl/android/media/RenderPosition.aidl",
+
+ "aidl/android/media/IAudioFlingerService.aidl",
+ "aidl/android/media/IAudioFlingerClient.aidl",
+ "aidl/android/media/IAudioRecord.aidl",
+ "aidl/android/media/IAudioTrack.aidl",
+ "aidl/android/media/IAudioTrackCallback.aidl",
+ ],
+ imports: [
+ "audio_common-aidl",
+ "audioclient-types-aidl",
+ "av-types-aidl",
+ "effect-aidl",
+ "shared-file-region-aidl",
+ "media_permission-aidl",
+ ],
+ double_loadable: true,
+ backend: {
+ cpp: {
+ min_sdk_version: "29",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media",
+ ],
+ },
+ },
+}
+
+aidl_interface {
+ name: "audiopolicy-aidl",
+ unstable: true,
+ local_include_dir: "aidl",
+ host_supported: true,
+ vendor_available: true,
+ srcs: [
+ "aidl/android/media/GetInputForAttrResponse.aidl",
+ "aidl/android/media/GetOutputForAttrResponse.aidl",
+ "aidl/android/media/Int.aidl",
+ "aidl/android/media/RecordClientInfo.aidl",
+
+ "aidl/android/media/IAudioPolicyService.aidl",
+ "aidl/android/media/IAudioPolicyServiceClient.aidl",
+ ],
+ imports: [
+ "audio_common-aidl",
+ "audioclient-types-aidl",
+ "audiopolicy-types-aidl",
+ "capture_state_listener-aidl",
+ "media_permission-aidl",
+ ],
+ double_loadable: true,
+ backend: {
+ cpp: {
+ min_sdk_version: "29",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media",
+ ],
+ },
+ },
+}
diff --git a/media/libaudioclient/AudioAttributes.cpp b/media/libaudioclient/AudioAttributes.cpp
index ff4ba06..83bf5a7 100644
--- a/media/libaudioclient/AudioAttributes.cpp
+++ b/media/libaudioclient/AudioAttributes.cpp
@@ -20,48 +20,46 @@
#include <binder/Parcel.h>
+#include <media/AidlConversion.h>
#include <media/AudioAttributes.h>
+#include <media/PolicyAidlConversion.h>
+
+#define RETURN_STATUS_IF_ERROR(x) \
+ { auto _tmp = (x); if (_tmp != OK) return _tmp; }
namespace android {
-status_t AudioAttributes::readFromParcel(const Parcel *parcel)
-{
- status_t ret = NO_ERROR;
- mAttributes.content_type = static_cast<audio_content_type_t>(parcel->readInt32());
- mAttributes.usage = static_cast<audio_usage_t>(parcel->readInt32());
- mAttributes.source = static_cast<audio_source_t>(parcel->readInt32());
- mAttributes.flags = static_cast<audio_flags_mask_t>(parcel->readInt32());
- const bool hasFlattenedTag = (parcel->readInt32() == 1);
- if (hasFlattenedTag) {
- std::string tags;
- ret = parcel->readUtf8FromUtf16(&tags);
- if (ret != NO_ERROR) {
- return ret;
- }
- std::strncpy(mAttributes.tags, tags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
- } else {
- strcpy(mAttributes.tags, "");
- }
- mStreamType = static_cast<audio_stream_type_t>(parcel->readInt32());
- mGroupId = static_cast<volume_group_t>(parcel->readUint32());
- return NO_ERROR;
+status_t AudioAttributes::readFromParcel(const Parcel* parcel) {
+ media::AudioAttributesEx aidl;
+ RETURN_STATUS_IF_ERROR(aidl.readFromParcel(parcel));
+ *this = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioAttributesEx_AudioAttributes(aidl));
+ return OK;
}
-status_t AudioAttributes::writeToParcel(Parcel *parcel) const
-{
- parcel->writeInt32(static_cast<int32_t>(mAttributes.content_type));
- parcel->writeInt32(static_cast<int32_t>(mAttributes.usage));
- parcel->writeInt32(static_cast<int32_t>(mAttributes.source));
- parcel->writeInt32(static_cast<int32_t>(mAttributes.flags));
- if (strlen(mAttributes.tags) == 0) {
- parcel->writeInt32(0);
- } else {
- parcel->writeInt32(1);
- parcel->writeUtf8AsUtf16(std::string(mAttributes.tags));
- }
- parcel->writeInt32(static_cast<int32_t>(mStreamType));
- parcel->writeUint32(static_cast<uint32_t>(mGroupId));
- return NO_ERROR;
+status_t AudioAttributes::writeToParcel(Parcel* parcel) const {
+ media::AudioAttributesEx aidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_AudioAttributes_AudioAttributesEx(*this));
+ return aidl.writeToParcel(parcel);
+}
+
+ConversionResult<media::AudioAttributesEx>
+legacy2aidl_AudioAttributes_AudioAttributesEx(const AudioAttributes& legacy) {
+ media::AudioAttributesEx aidl;
+ aidl.attributes = VALUE_OR_RETURN(
+ legacy2aidl_audio_attributes_t_AudioAttributesInternal(legacy.getAttributes()));
+ aidl.streamType = VALUE_OR_RETURN(
+ legacy2aidl_audio_stream_type_t_AudioStreamType(legacy.getStreamType()));
+ aidl.groupId = VALUE_OR_RETURN(legacy2aidl_volume_group_t_int32_t(legacy.getGroupId()));
+ return aidl;
+}
+
+ConversionResult<AudioAttributes>
+aidl2legacy_AudioAttributesEx_AudioAttributes(const media::AudioAttributesEx& aidl) {
+ return AudioAttributes(VALUE_OR_RETURN(aidl2legacy_int32_t_volume_group_t(aidl.groupId)),
+ VALUE_OR_RETURN(aidl2legacy_AudioStreamType_audio_stream_type_t(
+ aidl.streamType)),
+ VALUE_OR_RETURN(aidl2legacy_AudioAttributesInternal_audio_attributes_t(
+ aidl.attributes)));
}
} // namespace android
diff --git a/media/libaudioclient/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp
index 73b96ab..d5047b1 100644
--- a/media/libaudioclient/AudioEffect.cpp
+++ b/media/libaudioclient/AudioEffect.cpp
@@ -23,20 +23,43 @@
#include <sys/types.h>
#include <limits.h>
-#include <private/media/AudioEffectShared.h>
-#include <media/AudioEffect.h>
-
-#include <utils/Log.h>
+#include <android/media/IAudioPolicyService.h>
#include <binder/IPCThreadState.h>
+#include <media/AidlConversion.h>
+#include <media/AudioEffect.h>
+#include <media/PolicyAidlConversion.h>
+#include <media/ShmemCompat.h>
+#include <private/media/AudioEffectShared.h>
+#include <utils/Log.h>
-
+#define RETURN_STATUS_IF_ERROR(x) \
+ { \
+ auto _tmp = (x); \
+ if (_tmp != OK) return _tmp; \
+ }
namespace android {
+using aidl_utils::statusTFromBinderStatus;
+using binder::Status;
+using media::IAudioPolicyService;
+using media::permission::Identity;
+
+namespace {
+
+// Copy from a raw pointer + size into a vector of bytes.
+void appendToBuffer(const void* data,
+ size_t size,
+ std::vector<uint8_t>* buffer) {
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
+ buffer->insert(buffer->end(), p, p + size);
+}
+
+} // namespace
// ---------------------------------------------------------------------------
-AudioEffect::AudioEffect(const String16& opPackageName)
- : mOpPackageName(opPackageName)
+AudioEffect::AudioEffect(const Identity& identity)
+ : mClientIdentity(identity)
{
}
@@ -50,7 +73,7 @@
const AudioDeviceTypeAddr& device,
bool probe)
{
- sp<IEffect> iEffect;
+ sp<media::IEffect> iEffect;
sp<IMemory> cblk;
int enabled;
@@ -85,13 +108,35 @@
mDescriptor.type = *(type != NULL ? type : EFFECT_UUID_NULL);
mDescriptor.uuid = *(uuid != NULL ? uuid : EFFECT_UUID_NULL);
+ // TODO b/182392769: use identity util
mIEffectClient = new EffectClient(this);
- mClientPid = IPCThreadState::self()->getCallingPid();
- mClientUid = IPCThreadState::self()->getCallingUid();
+ pid_t pid = IPCThreadState::self()->getCallingPid();
+ mClientIdentity.pid = VALUE_OR_RETURN_STATUS(legacy2aidl_pid_t_int32_t(pid));
+ pid_t uid = IPCThreadState::self()->getCallingUid();
+ mClientIdentity.uid = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(uid));
- iEffect = audioFlinger->createEffect((effect_descriptor_t *)&mDescriptor,
- mIEffectClient, priority, io, mSessionId, device, mOpPackageName, mClientPid,
- probe, &mStatus, &mId, &enabled);
+ media::CreateEffectRequest request;
+ request.desc = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_effect_descriptor_t_EffectDescriptor(mDescriptor));
+ request.client = mIEffectClient;
+ request.priority = priority;
+ request.output = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(io));
+ request.sessionId = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_session_t_int32_t(mSessionId));
+ request.device = VALUE_OR_RETURN_STATUS(legacy2aidl_AudioDeviceTypeAddress(device));
+ request.identity = mClientIdentity;
+ request.probe = probe;
+
+ media::CreateEffectResponse response;
+
+ mStatus = audioFlinger->createEffect(request, &response);
+
+ if (mStatus == OK) {
+ mId = response.id;
+ enabled = response.enabled;
+ iEffect = response.effect;
+ mDescriptor = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_EffectDescriptor_effect_descriptor_t(response.desc));
+ }
// In probe mode, we stop here and return the status: the IEffect interface to
// audio flinger will not be retained. initCheck() will return the creation status
@@ -112,8 +157,10 @@
mEnabled = (volatile int32_t)enabled;
- cblk = iEffect->getCblk();
- if (cblk == 0) {
+ if (media::SharedFileRegion shmem;
+ !iEffect->getCblk(&shmem).isOk()
+ || !convertSharedFileRegionToIMemory(shmem, &cblk)
+ || cblk == 0) {
mStatus = NO_INIT;
ALOGE("Could not get control block");
return mStatus;
@@ -131,10 +178,10 @@
IInterface::asBinder(iEffect)->linkToDeath(mIEffectClient);
ALOGV("set() %p OK effect: %s id: %d status %d enabled %d pid %d", this, mDescriptor.name, mId,
- mStatus, mEnabled, mClientPid);
+ mStatus, mEnabled, mClientIdentity.pid);
if (!audio_is_global_session(mSessionId)) {
- AudioSystem::acquireAudioSessionId(mSessionId, mClientPid, mClientUid);
+ AudioSystem::acquireAudioSessionId(mSessionId, pid, uid);
}
return mStatus;
@@ -175,7 +222,8 @@
if (!mProbe && (mStatus == NO_ERROR || mStatus == ALREADY_EXISTS)) {
if (!audio_is_global_session(mSessionId)) {
- AudioSystem::releaseAudioSessionId(mSessionId, mClientPid);
+ AudioSystem::releaseAudioSessionId(mSessionId,
+ VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mClientIdentity.pid)));
}
if (mIEffect != NULL) {
mIEffect->disconnect();
@@ -216,15 +264,19 @@
}
status_t status = NO_ERROR;
-
AutoMutex lock(mLock);
if (enabled != mEnabled) {
+ Status bs;
+
if (enabled) {
ALOGV("enable %p", this);
- status = mIEffect->enable();
+ bs = mIEffect->enable(&status);
} else {
ALOGV("disable %p", this);
- status = mIEffect->disable();
+ bs = mIEffect->disable(&status);
+ }
+ if (!bs.isOk()) {
+ status = statusTFromBinderStatus(bs);
}
if (status == NO_ERROR) {
mEnabled = enabled;
@@ -257,7 +309,20 @@
mLock.lock();
}
- status_t status = mIEffect->command(cmdCode, cmdSize, cmdData, replySize, replyData);
+ std::vector<uint8_t> data;
+ appendToBuffer(cmdData, cmdSize, &data);
+
+ status_t status;
+ std::vector<uint8_t> response;
+
+ Status bs = mIEffect->command(cmdCode, data, *replySize, &response, &status);
+ if (!bs.isOk()) {
+ status = statusTFromBinderStatus(bs);
+ }
+ if (status == NO_ERROR) {
+ memcpy(replyData, response.data(), response.size());
+ *replySize = response.size();
+ }
if (cmdCode == EFFECT_CMD_ENABLE || cmdCode == EFFECT_CMD_DISABLE) {
if (status == NO_ERROR) {
@@ -272,7 +337,6 @@
return status;
}
-
status_t AudioEffect::setParameter(effect_param_t *param)
{
if (mProbe) {
@@ -286,14 +350,27 @@
return BAD_VALUE;
}
- uint32_t size = sizeof(int);
uint32_t psize = ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + param->vsize;
ALOGV("setParameter: param: %d, param2: %d", *(int *)param->data,
(param->psize == 8) ? *((int *)param->data + 1): -1);
- return mIEffect->command(EFFECT_CMD_SET_PARAM, sizeof (effect_param_t) + psize, param, &size,
- ¶m->status);
+ std::vector<uint8_t> cmd;
+ appendToBuffer(param, sizeof(effect_param_t) + psize, &cmd);
+ std::vector<uint8_t> response;
+ status_t status;
+ Status bs = mIEffect->command(EFFECT_CMD_SET_PARAM,
+ cmd,
+ sizeof(int),
+ &response,
+ &status);
+ if (!bs.isOk()) {
+ status = statusTFromBinderStatus(bs);
+ return status;
+ }
+ assert(response.size() == sizeof(int));
+ memcpy(¶m->status, response.data(), response.size());
+ return status;
}
status_t AudioEffect::setParameterDeferred(effect_param_t *param)
@@ -338,8 +415,18 @@
if (mCblk->clientIndex == 0) {
return INVALID_OPERATION;
}
- uint32_t size = 0;
- return mIEffect->command(EFFECT_CMD_SET_PARAM_COMMIT, 0, NULL, &size, NULL);
+ std::vector<uint8_t> cmd;
+ std::vector<uint8_t> response;
+ status_t status;
+ Status bs = mIEffect->command(EFFECT_CMD_SET_PARAM_COMMIT,
+ cmd,
+ 0,
+ &response,
+ &status);
+ if (!bs.isOk()) {
+ status = statusTFromBinderStatus(bs);
+ }
+ return status;
}
status_t AudioEffect::getParameter(effect_param_t *param)
@@ -361,8 +448,18 @@
uint32_t psize = sizeof(effect_param_t) + ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) +
param->vsize;
- return mIEffect->command(EFFECT_CMD_GET_PARAM, sizeof(effect_param_t) + param->psize, param,
- &psize, param);
+ status_t status;
+ std::vector<uint8_t> cmd;
+ std::vector<uint8_t> response;
+ appendToBuffer(param, sizeof(effect_param_t) + param->psize, &cmd);
+
+ Status bs = mIEffect->command(EFFECT_CMD_GET_PARAM, cmd, psize, &response, &status);
+ if (!bs.isOk()) {
+ status = statusTFromBinderStatus(bs);
+ return status;
+ }
+ memcpy(param, response.data(), response.size());
+ return status;
}
@@ -410,19 +507,18 @@
}
}
-void AudioEffect::commandExecuted(uint32_t cmdCode,
- uint32_t cmdSize __unused,
- void *cmdData,
- uint32_t replySize __unused,
- void *replyData)
+void AudioEffect::commandExecuted(int32_t cmdCode,
+ const std::vector<uint8_t>& cmdData,
+ const std::vector<uint8_t>& replyData)
{
- if (cmdData == NULL || replyData == NULL) {
+ if (cmdData.empty() || replyData.empty()) {
return;
}
if (mCbf != NULL && cmdCode == EFFECT_CMD_SET_PARAM) {
- effect_param_t *cmd = (effect_param_t *)cmdData;
- cmd->status = *(int32_t *)replyData;
+ std::vector<uint8_t> cmdDataCopy(cmdData);
+ effect_param_t* cmd = reinterpret_cast<effect_param_t *>(cmdDataCopy.data());
+ cmd->status = *reinterpret_cast<const int32_t *>(replyData.data());
mCbf(EVENT_PARAMETER_CHANGED, mUserData, cmd);
}
}
@@ -457,9 +553,23 @@
effect_descriptor_t *descriptors,
uint32_t *count)
{
+ if (descriptors == nullptr || count == nullptr) {
+ return BAD_VALUE;
+ }
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->queryDefaultPreProcessing(audioSession, descriptors, count);
+
+ int32_t audioSessionAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_session_t_int32_t(audioSession));
+ media::Int countAidl;
+ countAidl.value = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(*count));
+ std::vector<media::EffectDescriptor> retAidl;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->queryDefaultPreProcessing(audioSessionAidl, &countAidl, &retAidl)));
+ *count = VALUE_OR_RETURN_STATUS(convertIntegral<uint32_t>(countAidl.value));
+ RETURN_STATUS_IF_ERROR(convertRange(retAidl.begin(), retAidl.end(), descriptors,
+ aidl2legacy_EffectDescriptor_effect_descriptor_t));
+ return OK;
}
status_t AudioEffect::newEffectUniqueId(audio_unique_id_t* id)
@@ -499,7 +609,18 @@
uuid = *EFFECT_UUID_NULL;
}
- return aps->addSourceDefaultEffect(&type, opPackageName, &uuid, priority, source, id);
+ media::AudioUuid typeAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_uuid_t_AudioUuid(type));
+ media::AudioUuid uuidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_uuid_t_AudioUuid(uuid));
+ std::string opPackageNameAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_String16_string(opPackageName));
+ media::AudioSourceType sourceAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_source_t_AudioSourceType(source));
+ int32_t retAidl;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->addSourceDefaultEffect(typeAidl, opPackageNameAidl, uuidAidl, priority, sourceAidl,
+ &retAidl)));
+ *id = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_unique_id_t(retAidl));
+ return OK;
}
status_t AudioEffect::addStreamDefaultEffect(const char *typeStr,
@@ -531,7 +652,18 @@
uuid = *EFFECT_UUID_NULL;
}
- return aps->addStreamDefaultEffect(&type, opPackageName, &uuid, priority, usage, id);
+ media::AudioUuid typeAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_uuid_t_AudioUuid(type));
+ media::AudioUuid uuidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_uuid_t_AudioUuid(uuid));
+ std::string opPackageNameAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_String16_string(opPackageName));
+ media::AudioUsage usageAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_usage_t_AudioUsage(usage));
+ int32_t retAidl;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->addStreamDefaultEffect(typeAidl, opPackageNameAidl, uuidAidl, priority, usageAidl,
+ &retAidl)));
+ *id = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_unique_id_t(retAidl));
+ return OK;
}
status_t AudioEffect::removeSourceDefaultEffect(audio_unique_id_t id)
@@ -539,7 +671,8 @@
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->removeSourceDefaultEffect(id);
+ int32_t idAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_unique_id_t_int32_t(id));
+ return statusTFromBinderStatus(aps->removeSourceDefaultEffect(idAidl));
}
status_t AudioEffect::removeStreamDefaultEffect(audio_unique_id_t id)
@@ -547,7 +680,8 @@
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->removeStreamDefaultEffect(id);
+ int32_t idAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_unique_id_t_int32_t(id));
+ return statusTFromBinderStatus(aps->removeStreamDefaultEffect(idAidl));
}
// -------------------------------------------------------------------------
diff --git a/media/libaudioclient/AudioPolicy.cpp b/media/libaudioclient/AudioPolicy.cpp
index 0522099..c2f7229 100644
--- a/media/libaudioclient/AudioPolicy.cpp
+++ b/media/libaudioclient/AudioPolicy.cpp
@@ -85,7 +85,7 @@
mDeviceType = (audio_devices_t) parcel->readInt32();
mDeviceAddress = parcel->readString8();
mCbFlags = (uint32_t)parcel->readInt32();
- mAllowPrivilegedPlaybackCapture = parcel->readBool();
+ mAllowPrivilegedMediaPlaybackCapture = parcel->readBool();
mVoiceCommunicationCaptureAllowed = parcel->readBool();
size_t size = (size_t)parcel->readInt32();
if (size > MAX_CRITERIA_PER_MIX) {
@@ -110,7 +110,7 @@
parcel->writeInt32(mDeviceType);
parcel->writeString8(mDeviceAddress);
parcel->writeInt32(mCbFlags);
- parcel->writeBool(mAllowPrivilegedPlaybackCapture);
+ parcel->writeBool(mAllowPrivilegedMediaPlaybackCapture);
parcel->writeBool(mVoiceCommunicationCaptureAllowed);
size_t size = mCriteria.size();
if (size > MAX_CRITERIA_PER_MIX) {
diff --git a/media/libaudioclient/AudioProductStrategy.cpp b/media/libaudioclient/AudioProductStrategy.cpp
index cff72fd..f98027a 100644
--- a/media/libaudioclient/AudioProductStrategy.cpp
+++ b/media/libaudioclient/AudioProductStrategy.cpp
@@ -19,55 +19,47 @@
#include <utils/Log.h>
#include <media/AudioProductStrategy.h>
#include <media/AudioAttributes.h>
-#include <media/AudioSystem.h>
+#include <media/PolicyAidlConversion.h>
+
+#define RETURN_STATUS_IF_ERROR(x) \
+ { auto _tmp = (x); if (_tmp != OK) return _tmp; }
namespace android {
-status_t AudioProductStrategy::readFromParcel(const Parcel *parcel)
-{
- mId = static_cast<product_strategy_t>(parcel->readInt32());
- status_t ret = parcel->readUtf8FromUtf16(&mName);
- if (ret != NO_ERROR) {
- return ret;
- }
- size_t size = static_cast<size_t>(parcel->readInt32());
- for (size_t i = 0; i < size; i++) {
- AudioAttributes attribute;
- ret = attribute.readFromParcel(parcel);
- if (ret != NO_ERROR) {
- mAudioAttributes.clear();
- return ret;
- }
- mAudioAttributes.push_back(attribute);
- }
- return NO_ERROR;
+status_t AudioProductStrategy::readFromParcel(const Parcel* parcel) {
+ media::AudioProductStrategy aidl;
+ RETURN_STATUS_IF_ERROR(aidl.readFromParcel(parcel));
+ *this = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioProductStrategy(aidl));
+ return OK;
}
-status_t AudioProductStrategy::writeToParcel(Parcel *parcel) const
-{
- parcel->writeInt32(static_cast<int32_t>(mId));
- parcel->writeUtf8AsUtf16(mName);
- size_t size = mAudioAttributes.size();
- size_t sizePosition = parcel->dataPosition();
- parcel->writeInt32(size);
- size_t finalSize = size;
+status_t AudioProductStrategy::writeToParcel(Parcel* parcel) const {
+ media::AudioProductStrategy aidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_AudioProductStrategy(*this));
+ return aidl.writeToParcel(parcel);
+}
- for (size_t i = 0; i < size; i++) {
- size_t position = parcel->dataPosition();
- AudioAttributes attribute(mAudioAttributes[i]);
- status_t ret = attribute.writeToParcel(parcel);
- if (ret != NO_ERROR) {
- parcel->setDataPosition(position);
- finalSize--;
- }
- }
- if (size != finalSize) {
- size_t position = parcel->dataPosition();
- parcel->setDataPosition(sizePosition);
- parcel->writeInt32(finalSize);
- parcel->setDataPosition(position);
- }
- return NO_ERROR;
+ConversionResult<media::AudioProductStrategy>
+legacy2aidl_AudioProductStrategy(const AudioProductStrategy& legacy) {
+ media::AudioProductStrategy aidl;
+ aidl.name = legacy.getName();
+ aidl.audioAttributes = VALUE_OR_RETURN(
+ convertContainer<std::vector<media::AudioAttributesEx>>(
+ legacy.getAudioAttributes(),
+ legacy2aidl_AudioAttributes_AudioAttributesEx));
+ aidl.id = VALUE_OR_RETURN(legacy2aidl_product_strategy_t_int32_t(legacy.getId()));
+ return aidl;
+}
+
+ConversionResult<AudioProductStrategy>
+aidl2legacy_AudioProductStrategy(const media::AudioProductStrategy& aidl) {
+ return AudioProductStrategy(
+ aidl.name,
+ VALUE_OR_RETURN(
+ convertContainer<std::vector<AudioAttributes>>(
+ aidl.audioAttributes,
+ aidl2legacy_AudioAttributesEx_AudioAttributes)),
+ VALUE_OR_RETURN(aidl2legacy_int32_t_product_strategy_t(aidl.id)));
}
// Keep in sync with android/media/audiopolicy/AudioProductStrategy#attributeMatches
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index 55b836f..1a4bde9 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -38,6 +38,10 @@
#define WAIT_PERIOD_MS 10
namespace android {
+
+using android::media::permission::Identity;
+using aidl_utils::statusTFromBinderStatus;
+
// ---------------------------------------------------------------------------
// static
@@ -103,6 +107,7 @@
mMetricsItem->setInt32(MM_PREFIX "lastError.code", (int32_t)mLastError);
mMetricsItem->setCString(MM_PREFIX "lastError.at", mLastErrorFunc.c_str());
}
+ mMetricsItem->setCString(MM_PREFIX "logSessionId", record->mLogSessionId.c_str());
}
static const char *stateToString(bool active) {
@@ -121,9 +126,8 @@
return NO_ERROR;
}
-AudioRecord::AudioRecord(const String16 &opPackageName)
- : mActive(false), mStatus(NO_INIT), mOpPackageName(opPackageName),
- mSessionId(AUDIO_SESSION_ALLOCATE),
+AudioRecord::AudioRecord(const Identity &client)
+ : mActive(false), mStatus(NO_INIT), mClientIdentity(client), mSessionId(AUDIO_SESSION_ALLOCATE),
mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT),
mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE), mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE),
mSelectedMicDirection(MIC_DIRECTION_UNSPECIFIED),
@@ -136,7 +140,7 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- const String16& opPackageName,
+ const Identity& client,
size_t frameCount,
callback_t cbf,
void* user,
@@ -144,24 +148,24 @@
audio_session_t sessionId,
transfer_type transferType,
audio_input_flags_t flags,
- uid_t uid,
- pid_t pid,
const audio_attributes_t* pAttributes,
audio_port_handle_t selectedDeviceId,
audio_microphone_direction_t selectedMicDirection,
float microphoneFieldDimension)
: mActive(false),
mStatus(NO_INIT),
- mOpPackageName(opPackageName),
+ mClientIdentity(client),
mSessionId(AUDIO_SESSION_ALLOCATE),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
mPreviousSchedulingGroup(SP_DEFAULT),
mProxy(NULL)
{
+ uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(mClientIdentity.uid));
+ pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mClientIdentity.pid));
(void)set(inputSource, sampleRate, format, channelMask, frameCount, cbf, user,
notificationFrames, false /*threadCanCallJava*/, sessionId, transferType, flags,
- uid, pid, pAttributes, selectedDeviceId,
- selectedMicDirection, microphoneFieldDimension);
+ uid, pid, pAttributes, selectedDeviceId, selectedMicDirection,
+ microphoneFieldDimension);
}
AudioRecord::~AudioRecord()
@@ -177,21 +181,9 @@
.set(AMEDIAMETRICS_PROP_STATUS, (int32_t)mStatus)
.record();
+ stopAndJoinCallbacks(); // checks mStatus
+
if (mStatus == NO_ERROR) {
- // Make sure that callback function exits in the case where
- // it is looping on buffer empty condition in obtainBuffer().
- // Otherwise the callback thread will never exit.
- stop();
- if (mAudioRecordThread != 0) {
- mProxy->interrupt();
- mAudioRecordThread->requestExit(); // see comment in AudioRecord.h
- mAudioRecordThread->requestExitAndWait();
- mAudioRecordThread.clear();
- }
- // No lock here: worst case we remove a NULL callback which will be a nop
- if (mDeviceCallback != 0 && mInput != AUDIO_IO_HANDLE_NONE) {
- AudioSystem::removeAudioDeviceCallback(this, mInput, mPortId);
- }
IInterface::asBinder(mAudioRecord)->unlinkToDeath(mDeathNotifier, this);
mAudioRecord.clear();
mCblkMemory.clear();
@@ -199,10 +191,32 @@
IPCThreadState::self()->flushCommands();
ALOGV("%s(%d): releasing session id %d",
__func__, mPortId, mSessionId);
- AudioSystem::releaseAudioSessionId(mSessionId, mClientPid);
+ pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mClientIdentity.pid));
+ AudioSystem::releaseAudioSessionId(mSessionId, pid);
}
}
+void AudioRecord::stopAndJoinCallbacks() {
+ // Prevent nullptr crash if it did not open properly.
+ if (mStatus != NO_ERROR) return;
+
+ // Make sure that callback function exits in the case where
+ // it is looping on buffer empty condition in obtainBuffer().
+ // Otherwise the callback thread will never exit.
+ stop();
+ if (mAudioRecordThread != 0) {
+ mProxy->interrupt();
+ mAudioRecordThread->requestExit(); // see comment in AudioRecord.h
+ mAudioRecordThread->requestExitAndWait();
+ mAudioRecordThread.clear();
+ }
+ // No lock here: worst case we remove a NULL callback which will be a nop
+ if (mDeviceCallback != 0 && mInput != AUDIO_IO_HANDLE_NONE) {
+ // This may not stop all of these device callbacks!
+ // TODO: Add some sort of protection.
+ AudioSystem::removeAudioDeviceCallback(this, mInput, mPortId);
+ }
+}
status_t AudioRecord::set(
audio_source_t inputSource,
uint32_t sampleRate,
@@ -221,26 +235,41 @@
const audio_attributes_t* pAttributes,
audio_port_handle_t selectedDeviceId,
audio_microphone_direction_t selectedMicDirection,
- float microphoneFieldDimension)
+ float microphoneFieldDimension,
+ int32_t maxSharedAudioHistoryMs)
{
status_t status = NO_ERROR;
uint32_t channelCount;
- pid_t callingPid;
- pid_t myPid;
// Note mPortId is not valid until the track is created, so omit mPortId in ALOG for set.
ALOGV("%s(): inputSource %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
- "notificationFrames %u, sessionId %d, transferType %d, flags %#x, opPackageName %s "
+ "notificationFrames %u, sessionId %d, transferType %d, flags %#x, identity %s"
"uid %d, pid %d",
__func__,
inputSource, sampleRate, format, channelMask, frameCount, notificationFrames,
- sessionId, transferType, flags, String8(mOpPackageName).string(), uid, pid);
+ sessionId, transferType, flags, mClientIdentity.toString().c_str(), uid, pid);
+
+ // TODO b/182392553: refactor or remove
+ pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ pid_t myPid = getpid();
+ pid_t adjPid = pid;
+ if (pid == -1 || (callingPid != myPid)) {
+ adjPid = callingPid;
+ }
+ mClientIdentity.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(adjPid));
+
+ uid_t adjUid = uid;
+ if (uid == -1 || (callingPid != myPid)) {
+ adjUid = IPCThreadState::self()->getCallingUid();
+ }
+ mClientIdentity.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(adjUid));
mTracker.reset(new RecordingActivityTracker());
mSelectedDeviceId = selectedDeviceId;
mSelectedMicDirection = selectedMicDirection;
mSelectedMicFieldDimension = microphoneFieldDimension;
+ mMaxSharedAudioHistoryMs = maxSharedAudioHistoryMs;
switch (transferType) {
case TRANSFER_DEFAULT:
@@ -329,19 +358,6 @@
mSessionId = sessionId;
ALOGV("%s(): mSessionId %d", __func__, mSessionId);
- callingPid = IPCThreadState::self()->getCallingPid();
- myPid = getpid();
- if (uid == AUDIO_UID_INVALID || (callingPid != myPid)) {
- mClientUid = IPCThreadState::self()->getCallingUid();
- } else {
- mClientUid = uid;
- }
- if (pid == -1 || (callingPid != myPid)) {
- mClientPid = callingPid;
- } else {
- mClientPid = pid;
- }
-
mOrigFlags = mFlags = flags;
mCbf = cbf;
@@ -354,7 +370,7 @@
// create the IAudioRecord
{
AutoMutex lock(mLock);
- status = createRecord_l(0 /*epoch*/, mOpPackageName);
+ status = createRecord_l(0 /*epoch*/);
}
ALOGV("%s(%d): status %d", __func__, mPortId, status);
@@ -375,7 +391,7 @@
mMarkerReached = false;
mNewPosition = 0;
mUpdatePeriod = 0;
- AudioSystem::acquireAudioSessionId(mSessionId, mClientPid, mClientUid);
+ AudioSystem::acquireAudioSessionId(mSessionId, adjPid, adjUid);
mSequence = 1;
mObservedSequence = mSequence;
mInOverrun = false;
@@ -441,7 +457,7 @@
mActive = true;
if (!(flags & CBLK_INVALID)) {
- status = mAudioRecord->start(event, triggerSession).transactionError();
+ status = statusTFromBinderStatus(mAudioRecord->start(event, triggerSession));
if (status == DEAD_OBJECT) {
flags |= CBLK_INVALID;
}
@@ -732,14 +748,13 @@
}
// must be called with mLock held
-status_t AudioRecord::createRecord_l(const Modulo<uint32_t> &epoch, const String16& opPackageName)
+status_t AudioRecord::createRecord_l(const Modulo<uint32_t> &epoch)
{
const int64_t beginNs = systemTime();
const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
IAudioFlinger::CreateRecordInput input;
IAudioFlinger::CreateRecordOutput output;
audio_session_t originalSessionId;
- sp<media::IAudioRecord> record;
void *iMemPointer;
audio_track_cblk_t* cblk;
status_t status;
@@ -786,15 +801,13 @@
input.config.sample_rate = mSampleRate;
input.config.channel_mask = mChannelMask;
input.config.format = mFormat;
- input.clientInfo.clientUid = mClientUid;
- input.clientInfo.clientPid = mClientPid;
+ input.clientInfo.identity = mClientIdentity;
input.clientInfo.clientTid = -1;
if (mFlags & AUDIO_INPUT_FLAG_FAST) {
if (mAudioRecordThread != 0) {
input.clientInfo.clientTid = mAudioRecordThread->getTid();
}
}
- input.opPackageName = opPackageName;
input.riid = mTracker->getRiid();
input.flags = mFlags;
@@ -805,9 +818,12 @@
input.selectedDeviceId = mSelectedDeviceId;
input.sessionId = mSessionId;
originalSessionId = mSessionId;
+ input.maxSharedAudioHistoryMs = mMaxSharedAudioHistoryMs;
do {
- record = audioFlinger->createRecord(input, output, &status);
+ media::CreateRecordResponse response;
+ status = audioFlinger->createRecord(VALUE_OR_FATAL(input.toAidl()), response);
+ output = VALUE_OR_FATAL(IAudioFlinger::CreateRecordOutput::fromAidl(response));
if (status == NO_ERROR) {
break;
}
@@ -824,7 +840,7 @@
usleep((20 + rand() % 30) * 10000);
} while (1);
- ALOG_ASSERT(record != 0);
+ ALOG_ASSERT(output.audioRecord != 0);
// AudioFlinger now owns the reference to the I/O handle,
// so we are no longer responsible for releasing it.
@@ -882,7 +898,7 @@
IInterface::asBinder(mAudioRecord)->unlinkToDeath(mDeathNotifier, this);
mDeathNotifier.clear();
}
- mAudioRecord = record;
+ mAudioRecord = output.audioRecord;
mCblkMemory = output.cblk;
mBufferMemory = output.buffers;
IPCThreadState::self()->flushCommands();
@@ -912,6 +928,10 @@
AudioSystem::addAudioDeviceCallback(this, output.inputId, output.portId);
}
+ if (!mSharedAudioPackageName.empty()) {
+ mAudioRecord->shareAudioHistory(mSharedAudioPackageName, mSharedAudioStartMs);
+ }
+
mPortId = output.portId;
// We retain a copy of the I/O handle, but don't own the reference
mInput = output.inputId;
@@ -941,6 +961,7 @@
.set(AMEDIAMETRICS_PROP_ORIGINALFLAGS, toString(mOrigFlags).c_str())
.set(AMEDIAMETRICS_PROP_SESSIONID, (int32_t)mSessionId)
.set(AMEDIAMETRICS_PROP_TRACKID, mPortId)
+ .set(AMEDIAMETRICS_PROP_LOGSESSIONID, mLogSessionId)
.set(AMEDIAMETRICS_PROP_SOURCE, toString(mAttributes.source).c_str())
.set(AMEDIAMETRICS_PROP_THREADID, (int32_t)output.inputId)
.set(AMEDIAMETRICS_PROP_SELECTEDDEVICEID, (int32_t)mSelectedDeviceId)
@@ -1423,14 +1444,14 @@
// It will also delete the strong references on previous IAudioRecord and IMemory
Modulo<uint32_t> position(mProxy->getPosition());
mNewPosition = position + mUpdatePeriod;
- result = createRecord_l(position, mOpPackageName);
+ result = createRecord_l(position);
if (result == NO_ERROR) {
if (mActive) {
// callback thread or sync event hasn't changed
// FIXME this fails if we have a new AudioFlinger instance
- result = mAudioRecord->start(
- AudioSystem::SYNC_EVENT_SAME, AUDIO_SESSION_NONE).transactionError();
+ result = statusTFromBinderStatus(mAudioRecord->start(
+ AudioSystem::SYNC_EVENT_SAME, AUDIO_SESSION_NONE));
}
mFramesReadServerOffset = mFramesRead; // server resets to zero so we need an offset.
}
@@ -1520,7 +1541,13 @@
status_t AudioRecord::getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones)
{
AutoMutex lock(mLock);
- return mAudioRecord->getActiveMicrophones(activeMicrophones).transactionError();
+ std::vector<media::MicrophoneInfoData> mics;
+ status_t status = statusTFromBinderStatus(mAudioRecord->getActiveMicrophones(&mics));
+ activeMicrophones->resize(mics.size());
+ for (size_t i = 0; status == OK && i < mics.size(); ++i) {
+ status = activeMicrophones->at(i).readFromParcelable(mics[i]);
+ }
+ return status;
}
status_t AudioRecord::setPreferredMicrophoneDirection(audio_microphone_direction_t direction)
@@ -1536,7 +1563,7 @@
// the internal AudioRecord hasn't be created yet, so just stash the attribute.
return OK;
} else {
- return mAudioRecord->setPreferredMicrophoneDirection(direction).transactionError();
+ return statusTFromBinderStatus(mAudioRecord->setPreferredMicrophoneDirection(direction));
}
}
@@ -1552,10 +1579,39 @@
// the internal AudioRecord hasn't be created yet, so just stash the attribute.
return OK;
} else {
- return mAudioRecord->setPreferredMicrophoneFieldDimension(zoom).transactionError();
+ return statusTFromBinderStatus(mAudioRecord->setPreferredMicrophoneFieldDimension(zoom));
}
}
+void AudioRecord::setLogSessionId(const char *logSessionId)
+{
+ AutoMutex lock(mLock);
+ if (logSessionId == nullptr) logSessionId = ""; // an empty string is an unset session id.
+ if (mLogSessionId == logSessionId) return;
+
+ mLogSessionId = logSessionId;
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETLOGSESSIONID)
+ .set(AMEDIAMETRICS_PROP_LOGSESSIONID, logSessionId)
+ .record();
+}
+
+status_t AudioRecord::shareAudioHistory(const std::string& sharedPackageName,
+ int64_t sharedStartMs)
+{
+ AutoMutex lock(mLock);
+ if (mAudioRecord == 0) {
+ return NO_INIT;
+ }
+ status_t status = statusTFromBinderStatus(
+ mAudioRecord->shareAudioHistory(sharedPackageName, sharedStartMs));
+ if (status == NO_ERROR) {
+ mSharedAudioPackageName = sharedPackageName;
+ mSharedAudioStartMs = sharedStartMs;
+ }
+ return status;
+}
+
// =========================================================================
void AudioRecord::DeathNotifier::binderDied(const wp<IBinder>& who __unused)
diff --git a/media/libaudioclient/AudioSanitizer.cpp b/media/libaudioclient/AudioSanitizer.cpp
deleted file mode 100644
index 44ca956..0000000
--- a/media/libaudioclient/AudioSanitizer.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <media/AudioSanitizer.h>
-
-namespace android {
-
- /** returns true if string overflow was prevented by zero termination */
-template <size_t size>
-bool preventStringOverflow(char (&s)[size]) {
- if (strnlen(s, size) < size) return false;
- s[size - 1] = '\0';
- return true;
-}
-
-status_t safetyNetLog(status_t status, const char *bugNumber) {
- if (status != NO_ERROR && bugNumber != nullptr) {
- android_errorWriteLog(0x534e4554, bugNumber); // SafetyNet logging
- }
- return status;
-}
-
-status_t AudioSanitizer::sanitizeAudioAttributes(
- audio_attributes_t *attr, const char *bugNumber)
-{
- status_t status = NO_ERROR;
- const size_t tagsMaxSize = AUDIO_ATTRIBUTES_TAGS_MAX_SIZE;
- if (strnlen(attr->tags, tagsMaxSize) >= tagsMaxSize) {
- status = BAD_VALUE;
- }
- attr->tags[tagsMaxSize - 1] = '\0';
- return safetyNetLog(status, bugNumber);
-}
-
-/** returns BAD_VALUE if sanitization was required. */
-status_t AudioSanitizer::sanitizeEffectDescriptor(
- effect_descriptor_t *desc, const char *bugNumber)
-{
- status_t status = NO_ERROR;
- if (preventStringOverflow(desc->name)
- | /* always */ preventStringOverflow(desc->implementor)) {
- status = BAD_VALUE;
- }
- return safetyNetLog(status, bugNumber);
-}
-
-/** returns BAD_VALUE if sanitization was required. */
-status_t AudioSanitizer::sanitizeAudioPortConfig(
- struct audio_port_config *config, const char *bugNumber)
-{
- status_t status = NO_ERROR;
- if (config->type == AUDIO_PORT_TYPE_DEVICE &&
- preventStringOverflow(config->ext.device.address)) {
- status = BAD_VALUE;
- }
- return safetyNetLog(status, bugNumber);
-}
-
-/** returns BAD_VALUE if sanitization was required. */
-status_t AudioSanitizer::sanitizeAudioPort(
- struct audio_port *port, const char *bugNumber)
-{
- status_t status = NO_ERROR;
- if (preventStringOverflow(port->name)) {
- status = BAD_VALUE;
- }
- if (sanitizeAudioPortConfig(&port->active_config) != NO_ERROR) {
- status = BAD_VALUE;
- }
- if (port->type == AUDIO_PORT_TYPE_DEVICE &&
- preventStringOverflow(port->ext.device.address)) {
- status = BAD_VALUE;
- }
- return safetyNetLog(status, bugNumber);
-}
-
-/** returns BAD_VALUE if sanitization was required. */
-status_t AudioSanitizer::sanitizeAudioPatch(
- struct audio_patch *patch, const char *bugNumber)
-{
- status_t status = NO_ERROR;
- if (patch->num_sources > AUDIO_PATCH_PORTS_MAX) {
- patch->num_sources = AUDIO_PATCH_PORTS_MAX;
- status = BAD_VALUE;
- }
- if (patch->num_sinks > AUDIO_PATCH_PORTS_MAX) {
- patch->num_sinks = AUDIO_PATCH_PORTS_MAX;
- status = BAD_VALUE;
- }
- for (size_t i = 0; i < patch->num_sources; i++) {
- if (sanitizeAudioPortConfig(&patch->sources[i]) != NO_ERROR) {
- status = BAD_VALUE;
- }
- }
- for (size_t i = 0; i < patch->num_sinks; i++) {
- if (sanitizeAudioPortConfig(&patch->sinks[i]) != NO_ERROR) {
- status = BAD_VALUE;
- }
- }
- return safetyNetLog(status, bugNumber);
-}
-
-}; // namespace android
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index edb0889..0bc592d 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -19,22 +19,40 @@
#include <utils/Log.h>
+#include <android/media/IAudioPolicyService.h>
#include <android/media/BnCaptureStateListener.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <binder/IPCThreadState.h>
+#include <media/AidlConversion.h>
#include <media/AudioResamplerPublic.h>
#include <media/AudioSystem.h>
#include <media/IAudioFlinger.h>
-#include <media/IAudioPolicyService.h>
+#include <media/PolicyAidlConversion.h>
#include <media/TypeConverter.h>
#include <math.h>
#include <system/audio.h>
+#include <android/media/GetInputForAttrResponse.h>
+
+#define VALUE_OR_RETURN_BINDER_STATUS(x) \
+ ({ auto _tmp = (x); \
+ if (!_tmp.ok()) return aidl_utils::binderStatusFromStatusT(_tmp.error()); \
+ std::move(_tmp.value()); })
+
+#define RETURN_STATUS_IF_ERROR(x) \
+ { \
+ auto _tmp = (x); \
+ if (_tmp != OK) return _tmp; \
+ }
// ----------------------------------------------------------------------------
namespace android {
+using aidl_utils::statusTFromBinderStatus;
+using binder::Status;
+using media::IAudioPolicyService;
+using media::permission::Identity;
// client singleton for AudioFlinger binder interface
Mutex AudioSystem::gLock;
@@ -45,15 +63,16 @@
std::set<audio_error_callback> AudioSystem::gAudioErrorCallbacks;
dynamic_policy_callback AudioSystem::gDynPolicyCallback = NULL;
record_config_callback AudioSystem::gRecordConfigCallback = NULL;
+routing_callback AudioSystem::gRoutingCallback = NULL;
// Required to be held while calling into gSoundTriggerCaptureStateListener.
class CaptureStateListenerImpl;
+
Mutex gSoundTriggerCaptureStateListenerLock;
sp<CaptureStateListenerImpl> gSoundTriggerCaptureStateListener = nullptr;
// establish binder interface to AudioFlinger service
-const sp<IAudioFlinger> AudioSystem::get_audio_flinger()
-{
+const sp<IAudioFlinger> AudioSystem::get_audio_flinger() {
sp<IAudioFlinger> af;
sp<AudioFlingerClient> afc;
bool reportNoError = false;
@@ -63,7 +82,7 @@
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
- binder = sm->getService(String16("media.audio_flinger"));
+ binder = sm->getService(String16(IAudioFlinger::DEFAULT_SERVICE_NAME));
if (binder != 0)
break;
ALOGW("AudioFlinger not published, waiting...");
@@ -75,7 +94,8 @@
reportNoError = true;
}
binder->linkToDeath(gAudioFlingerClient);
- gAudioFlinger = interface_cast<IAudioFlinger>(binder);
+ gAudioFlinger = new AudioFlingerClientAdapter(
+ interface_cast<media::IAudioFlingerService>(binder));
LOG_ALWAYS_FATAL_IF(gAudioFlinger == 0);
afc = gAudioFlingerClient;
// Make sure callbacks can be received by gAudioFlingerClient
@@ -92,8 +112,7 @@
return af;
}
-const sp<AudioSystem::AudioFlingerClient> AudioSystem::getAudioFlingerClient()
-{
+const sp<AudioSystem::AudioFlingerClient> AudioSystem::getAudioFlingerClient() {
// calling get_audio_flinger() will initialize gAudioFlingerClient if needed
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return 0;
@@ -101,8 +120,7 @@
return gAudioFlingerClient;
}
-sp<AudioIoDescriptor> AudioSystem::getIoDescriptor(audio_io_handle_t ioHandle)
-{
+sp<AudioIoDescriptor> AudioSystem::getIoDescriptor(audio_io_handle_t ioHandle) {
sp<AudioIoDescriptor> desc;
const sp<AudioFlingerClient> afc = getAudioFlingerClient();
if (afc != 0) {
@@ -111,8 +129,7 @@
return desc;
}
-/* static */ status_t AudioSystem::checkAudioFlinger()
-{
+/* static */ status_t AudioSystem::checkAudioFlinger() {
if (defaultServiceManager()->checkService(String16("media.audio_flinger")) != 0) {
return NO_ERROR;
}
@@ -121,47 +138,41 @@
// FIXME Declare in binder opcode order, similarly to IAudioFlinger.h and IAudioFlinger.cpp
-status_t AudioSystem::muteMicrophone(bool state)
-{
+status_t AudioSystem::muteMicrophone(bool state) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
return af->setMicMute(state);
}
-status_t AudioSystem::isMicrophoneMuted(bool* state)
-{
+status_t AudioSystem::isMicrophoneMuted(bool* state) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
*state = af->getMicMute();
return NO_ERROR;
}
-status_t AudioSystem::setMasterVolume(float value)
-{
+status_t AudioSystem::setMasterVolume(float value) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
af->setMasterVolume(value);
return NO_ERROR;
}
-status_t AudioSystem::setMasterMute(bool mute)
-{
+status_t AudioSystem::setMasterMute(bool mute) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
af->setMasterMute(mute);
return NO_ERROR;
}
-status_t AudioSystem::getMasterVolume(float* volume)
-{
+status_t AudioSystem::getMasterVolume(float* volume) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
*volume = af->masterVolume();
return NO_ERROR;
}
-status_t AudioSystem::getMasterMute(bool* mute)
-{
+status_t AudioSystem::getMasterMute(bool* mute) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
*mute = af->masterMute();
@@ -169,8 +180,7 @@
}
status_t AudioSystem::setStreamVolume(audio_stream_type_t stream, float value,
- audio_io_handle_t output)
-{
+ audio_io_handle_t output) {
if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE;
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
@@ -178,8 +188,7 @@
return NO_ERROR;
}
-status_t AudioSystem::setStreamMute(audio_stream_type_t stream, bool mute)
-{
+status_t AudioSystem::setStreamMute(audio_stream_type_t stream, bool mute) {
if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE;
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
@@ -188,8 +197,7 @@
}
status_t AudioSystem::getStreamVolume(audio_stream_type_t stream, float* volume,
- audio_io_handle_t output)
-{
+ audio_io_handle_t output) {
if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE;
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
@@ -197,8 +205,7 @@
return NO_ERROR;
}
-status_t AudioSystem::getStreamMute(audio_stream_type_t stream, bool* mute)
-{
+status_t AudioSystem::getStreamMute(audio_stream_type_t stream, bool* mute) {
if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE;
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
@@ -206,23 +213,20 @@
return NO_ERROR;
}
-status_t AudioSystem::setMode(audio_mode_t mode)
-{
+status_t AudioSystem::setMode(audio_mode_t mode) {
if (uint32_t(mode) >= AUDIO_MODE_CNT) return BAD_VALUE;
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
return af->setMode(mode);
}
-status_t AudioSystem::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs)
-{
+status_t AudioSystem::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
return af->setParameters(ioHandle, keyValuePairs);
}
-String8 AudioSystem::getParameters(audio_io_handle_t ioHandle, const String8& keys)
-{
+String8 AudioSystem::getParameters(audio_io_handle_t ioHandle, const String8& keys) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
String8 result = String8("");
if (af == 0) return result;
@@ -231,13 +235,11 @@
return result;
}
-status_t AudioSystem::setParameters(const String8& keyValuePairs)
-{
+status_t AudioSystem::setParameters(const String8& keyValuePairs) {
return setParameters(AUDIO_IO_HANDLE_NONE, keyValuePairs);
}
-String8 AudioSystem::getParameters(const String8& keys)
-{
+String8 AudioSystem::getParameters(const String8& keys) {
return getParameters(AUDIO_IO_HANDLE_NONE, keys);
}
@@ -249,16 +251,14 @@
static const float dBConvert = -dBPerStep * 2.302585093f / 20.0f;
static const float dBConvertInverse = 1.0f / dBConvert;
-float AudioSystem::linearToLog(int volume)
-{
+float AudioSystem::linearToLog(int volume) {
// float v = volume ? exp(float(100 - volume) * dBConvert) : 0;
// ALOGD("linearToLog(%d)=%f", volume, v);
// return v;
return volume ? exp(float(100 - volume) * dBConvert) : 0;
}
-int AudioSystem::logToLinear(float volume)
-{
+int AudioSystem::logToLinear(float volume) {
// int v = volume ? 100 - int(dBConvertInverse * log(volume) + 0.5) : 0;
// ALOGD("logTolinear(%d)=%f", v, volume);
// return v;
@@ -267,31 +267,30 @@
/* static */ size_t AudioSystem::calculateMinFrameCount(
uint32_t afLatencyMs, uint32_t afFrameCount, uint32_t afSampleRate,
- uint32_t sampleRate, float speed /*, uint32_t notificationsPerBufferReq*/)
-{
+ uint32_t sampleRate, float speed /*, uint32_t notificationsPerBufferReq*/) {
// Ensure that buffer depth covers at least audio hardware latency
uint32_t minBufCount = afLatencyMs / ((1000 * afFrameCount) / afSampleRate);
if (minBufCount < 2) {
minBufCount = 2;
}
#if 0
- // The notificationsPerBufferReq parameter is not yet used for non-fast tracks,
- // but keeping the code here to make it easier to add later.
- if (minBufCount < notificationsPerBufferReq) {
- minBufCount = notificationsPerBufferReq;
- }
+ // The notificationsPerBufferReq parameter is not yet used for non-fast tracks,
+ // but keeping the code here to make it easier to add later.
+ if (minBufCount < notificationsPerBufferReq) {
+ minBufCount = notificationsPerBufferReq;
+ }
#endif
ALOGV("calculateMinFrameCount afLatency %u afFrameCount %u afSampleRate %u "
- "sampleRate %u speed %f minBufCount: %u" /*" notificationsPerBufferReq %u"*/,
- afLatencyMs, afFrameCount, afSampleRate, sampleRate, speed, minBufCount
- /*, notificationsPerBufferReq*/);
+ "sampleRate %u speed %f minBufCount: %u" /*" notificationsPerBufferReq %u"*/,
+ afLatencyMs, afFrameCount, afSampleRate, sampleRate, speed, minBufCount
+ /*, notificationsPerBufferReq*/);
return minBufCount * sourceFramesNeededWithTimestretch(
sampleRate, afFrameCount, afSampleRate, speed);
}
-status_t AudioSystem::getOutputSamplingRate(uint32_t* samplingRate, audio_stream_type_t streamType)
-{
+status_t
+AudioSystem::getOutputSamplingRate(uint32_t* samplingRate, audio_stream_type_t streamType) {
audio_io_handle_t output;
if (streamType == AUDIO_STREAM_DEFAULT) {
@@ -307,8 +306,7 @@
}
status_t AudioSystem::getSamplingRate(audio_io_handle_t ioHandle,
- uint32_t* samplingRate)
-{
+ uint32_t* samplingRate) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
sp<AudioIoDescriptor> desc = getIoDescriptor(ioHandle);
@@ -327,8 +325,7 @@
return NO_ERROR;
}
-status_t AudioSystem::getOutputFrameCount(size_t* frameCount, audio_stream_type_t streamType)
-{
+status_t AudioSystem::getOutputFrameCount(size_t* frameCount, audio_stream_type_t streamType) {
audio_io_handle_t output;
if (streamType == AUDIO_STREAM_DEFAULT) {
@@ -344,8 +341,7 @@
}
status_t AudioSystem::getFrameCount(audio_io_handle_t ioHandle,
- size_t* frameCount)
-{
+ size_t* frameCount) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
sp<AudioIoDescriptor> desc = getIoDescriptor(ioHandle);
@@ -364,8 +360,7 @@
return NO_ERROR;
}
-status_t AudioSystem::getOutputLatency(uint32_t* latency, audio_stream_type_t streamType)
-{
+status_t AudioSystem::getOutputLatency(uint32_t* latency, audio_stream_type_t streamType) {
audio_io_handle_t output;
if (streamType == AUDIO_STREAM_DEFAULT) {
@@ -381,8 +376,7 @@
}
status_t AudioSystem::getLatency(audio_io_handle_t output,
- uint32_t* latency)
-{
+ uint32_t* latency) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
sp<AudioIoDescriptor> outputDesc = getIoDescriptor(output);
@@ -398,8 +392,7 @@
}
status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, audio_format_t format,
- audio_channel_mask_t channelMask, size_t* buffSize)
-{
+ audio_channel_mask_t channelMask, size_t* buffSize) {
const sp<AudioFlingerClient> afc = getAudioFlingerClient();
if (afc == 0) {
return NO_INIT;
@@ -407,24 +400,21 @@
return afc->getInputBufferSize(sampleRate, format, channelMask, buffSize);
}
-status_t AudioSystem::setVoiceVolume(float value)
-{
+status_t AudioSystem::setVoiceVolume(float value) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
return af->setVoiceVolume(value);
}
-status_t AudioSystem::getRenderPosition(audio_io_handle_t output, uint32_t *halFrames,
- uint32_t *dspFrames)
-{
+status_t AudioSystem::getRenderPosition(audio_io_handle_t output, uint32_t* halFrames,
+ uint32_t* dspFrames) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
return af->getRenderPosition(halFrames, dspFrames, output);
}
-uint32_t AudioSystem::getInputFramesLost(audio_io_handle_t ioHandle)
-{
+uint32_t AudioSystem::getInputFramesLost(audio_io_handle_t ioHandle) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
uint32_t result = 0;
if (af == 0) return result;
@@ -434,47 +424,41 @@
return result;
}
-audio_unique_id_t AudioSystem::newAudioUniqueId(audio_unique_id_use_t use)
-{
+audio_unique_id_t AudioSystem::newAudioUniqueId(audio_unique_id_use_t use) {
// Must not use AF as IDs will re-roll on audioserver restart, b/130369529.
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return AUDIO_UNIQUE_ID_ALLOCATE;
return af->newAudioUniqueId(use);
}
-void AudioSystem::acquireAudioSessionId(audio_session_t audioSession, pid_t pid, uid_t uid)
-{
+void AudioSystem::acquireAudioSessionId(audio_session_t audioSession, pid_t pid, uid_t uid) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af != 0) {
af->acquireAudioSessionId(audioSession, pid, uid);
}
}
-void AudioSystem::releaseAudioSessionId(audio_session_t audioSession, pid_t pid)
-{
+void AudioSystem::releaseAudioSessionId(audio_session_t audioSession, pid_t pid) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af != 0) {
af->releaseAudioSessionId(audioSession, pid);
}
}
-audio_hw_sync_t AudioSystem::getAudioHwSyncForSession(audio_session_t sessionId)
-{
+audio_hw_sync_t AudioSystem::getAudioHwSyncForSession(audio_session_t sessionId) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return AUDIO_HW_SYNC_INVALID;
return af->getAudioHwSyncForSession(sessionId);
}
-status_t AudioSystem::systemReady()
-{
+status_t AudioSystem::systemReady() {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return NO_INIT;
return af->systemReady();
}
status_t AudioSystem::getFrameCountHAL(audio_io_handle_t ioHandle,
- size_t* frameCount)
-{
+ size_t* frameCount) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
sp<AudioIoDescriptor> desc = getIoDescriptor(ioHandle);
@@ -496,8 +480,7 @@
// ---------------------------------------------------------------------------
-void AudioSystem::AudioFlingerClient::clearIoCache()
-{
+void AudioSystem::AudioFlingerClient::clearIoCache() {
Mutex::Autolock _l(mLock);
mIoDescriptors.clear();
mInBuffSize = 0;
@@ -506,8 +489,7 @@
mInChannelMask = AUDIO_CHANNEL_NONE;
}
-void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who __unused)
-{
+void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who __unused) {
{
Mutex::Autolock _l(AudioSystem::gLock);
AudioSystem::gAudioFlinger.clear();
@@ -521,11 +503,18 @@
ALOGW("AudioFlinger server died!");
}
-void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event event,
- const sp<AudioIoDescriptor>& ioDesc) {
+Status AudioSystem::AudioFlingerClient::ioConfigChanged(
+ media::AudioIoConfigEvent _event,
+ const media::AudioIoDescriptor& _ioDesc) {
+ audio_io_config_event event = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioIoConfigEvent_audio_io_config_event(_event));
+ sp<AudioIoDescriptor> ioDesc(
+ VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioIoDescriptor_AudioIoDescriptor(_ioDesc)));
+
ALOGV("ioConfigChanged() event %d", event);
- if (ioDesc == 0 || ioDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) return;
+ if (ioDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) return Status::ok();
audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE;
std::vector<sp<AudioDeviceCallback>> callbacksToCall;
@@ -534,96 +523,102 @@
auto callbacks = std::map<audio_port_handle_t, wp<AudioDeviceCallback>>();
switch (event) {
- case AUDIO_OUTPUT_OPENED:
- case AUDIO_OUTPUT_REGISTERED:
- case AUDIO_INPUT_OPENED:
- case AUDIO_INPUT_REGISTERED: {
- sp<AudioIoDescriptor> oldDesc = getIoDescriptor_l(ioDesc->mIoHandle);
- if (oldDesc == 0) {
- mIoDescriptors.add(ioDesc->mIoHandle, ioDesc);
- } else {
+ case AUDIO_OUTPUT_OPENED:
+ case AUDIO_OUTPUT_REGISTERED:
+ case AUDIO_INPUT_OPENED:
+ case AUDIO_INPUT_REGISTERED: {
+ sp<AudioIoDescriptor> oldDesc = getIoDescriptor_l(ioDesc->mIoHandle);
+ if (oldDesc == 0) {
+ mIoDescriptors.add(ioDesc->mIoHandle, ioDesc);
+ } else {
+ deviceId = oldDesc->getDeviceId();
+ mIoDescriptors.replaceValueFor(ioDesc->mIoHandle, ioDesc);
+ }
+
+ if (ioDesc->getDeviceId() != AUDIO_PORT_HANDLE_NONE) {
+ deviceId = ioDesc->getDeviceId();
+ if (event == AUDIO_OUTPUT_OPENED || event == AUDIO_INPUT_OPENED) {
+ auto it = mAudioDeviceCallbacks.find(ioDesc->mIoHandle);
+ if (it != mAudioDeviceCallbacks.end()) {
+ callbacks = it->second;
+ }
+ }
+ }
+ ALOGV("ioConfigChanged() new %s %s %d samplingRate %u, format %#x channel mask %#x "
+ "frameCount %zu deviceId %d",
+ event == AUDIO_OUTPUT_OPENED || event == AUDIO_OUTPUT_REGISTERED ?
+ "output" : "input",
+ event == AUDIO_OUTPUT_OPENED || event == AUDIO_INPUT_OPENED ?
+ "opened" : "registered",
+ ioDesc->mIoHandle, ioDesc->mSamplingRate, ioDesc->mFormat,
+ ioDesc->mChannelMask,
+ ioDesc->mFrameCount, ioDesc->getDeviceId());
+ }
+ break;
+ case AUDIO_OUTPUT_CLOSED:
+ case AUDIO_INPUT_CLOSED: {
+ if (getIoDescriptor_l(ioDesc->mIoHandle) == 0) {
+ ALOGW("ioConfigChanged() closing unknown %s %d",
+ event == AUDIO_OUTPUT_CLOSED ? "output" : "input", ioDesc->mIoHandle);
+ break;
+ }
+ ALOGV("ioConfigChanged() %s %d closed",
+ event == AUDIO_OUTPUT_CLOSED ? "output" : "input", ioDesc->mIoHandle);
+
+ mIoDescriptors.removeItem(ioDesc->mIoHandle);
+ mAudioDeviceCallbacks.erase(ioDesc->mIoHandle);
+ }
+ break;
+
+ case AUDIO_OUTPUT_CONFIG_CHANGED:
+ case AUDIO_INPUT_CONFIG_CHANGED: {
+ sp<AudioIoDescriptor> oldDesc = getIoDescriptor_l(ioDesc->mIoHandle);
+ if (oldDesc == 0) {
+ ALOGW("ioConfigChanged() modifying unknown %s! %d",
+ event == AUDIO_OUTPUT_CONFIG_CHANGED ? "output" : "input",
+ ioDesc->mIoHandle);
+ break;
+ }
+
deviceId = oldDesc->getDeviceId();
mIoDescriptors.replaceValueFor(ioDesc->mIoHandle, ioDesc);
- }
- if (ioDesc->getDeviceId() != AUDIO_PORT_HANDLE_NONE) {
- deviceId = ioDesc->getDeviceId();
- if (event == AUDIO_OUTPUT_OPENED || event == AUDIO_INPUT_OPENED) {
+ if (deviceId != ioDesc->getDeviceId()) {
+ deviceId = ioDesc->getDeviceId();
auto it = mAudioDeviceCallbacks.find(ioDesc->mIoHandle);
if (it != mAudioDeviceCallbacks.end()) {
callbacks = it->second;
}
}
+ ALOGV("ioConfigChanged() new config for %s %d samplingRate %u, format %#x "
+ "channel mask %#x frameCount %zu frameCountHAL %zu deviceId %d",
+ event == AUDIO_OUTPUT_CONFIG_CHANGED ? "output" : "input",
+ ioDesc->mIoHandle, ioDesc->mSamplingRate, ioDesc->mFormat,
+ ioDesc->mChannelMask, ioDesc->mFrameCount, ioDesc->mFrameCountHAL,
+ ioDesc->getDeviceId());
+
}
- ALOGV("ioConfigChanged() new %s %s %d samplingRate %u, format %#x channel mask %#x "
- "frameCount %zu deviceId %d",
- event == AUDIO_OUTPUT_OPENED || event == AUDIO_OUTPUT_REGISTERED ?
- "output" : "input",
- event == AUDIO_OUTPUT_OPENED || event == AUDIO_INPUT_OPENED ?
- "opened" : "registered",
- ioDesc->mIoHandle, ioDesc->mSamplingRate, ioDesc->mFormat, ioDesc->mChannelMask,
- ioDesc->mFrameCount, ioDesc->getDeviceId());
- } break;
- case AUDIO_OUTPUT_CLOSED:
- case AUDIO_INPUT_CLOSED: {
- if (getIoDescriptor_l(ioDesc->mIoHandle) == 0) {
- ALOGW("ioConfigChanged() closing unknown %s %d",
- event == AUDIO_OUTPUT_CLOSED ? "output" : "input", ioDesc->mIoHandle);
break;
- }
- ALOGV("ioConfigChanged() %s %d closed",
- event == AUDIO_OUTPUT_CLOSED ? "output" : "input", ioDesc->mIoHandle);
-
- mIoDescriptors.removeItem(ioDesc->mIoHandle);
- mAudioDeviceCallbacks.erase(ioDesc->mIoHandle);
- } break;
-
- case AUDIO_OUTPUT_CONFIG_CHANGED:
- case AUDIO_INPUT_CONFIG_CHANGED: {
- sp<AudioIoDescriptor> oldDesc = getIoDescriptor_l(ioDesc->mIoHandle);
- if (oldDesc == 0) {
- ALOGW("ioConfigChanged() modifying unknown %s! %d",
- event == AUDIO_OUTPUT_CONFIG_CHANGED ? "output" : "input", ioDesc->mIoHandle);
- break;
- }
-
- deviceId = oldDesc->getDeviceId();
- mIoDescriptors.replaceValueFor(ioDesc->mIoHandle, ioDesc);
-
- if (deviceId != ioDesc->getDeviceId()) {
- deviceId = ioDesc->getDeviceId();
+ case AUDIO_CLIENT_STARTED: {
+ sp<AudioIoDescriptor> oldDesc = getIoDescriptor_l(ioDesc->mIoHandle);
+ if (oldDesc == 0) {
+ ALOGW("ioConfigChanged() start client on unknown io! %d", ioDesc->mIoHandle);
+ break;
+ }
+ ALOGV("ioConfigChanged() AUDIO_CLIENT_STARTED io %d port %d num callbacks %zu",
+ ioDesc->mIoHandle, ioDesc->mPortId, mAudioDeviceCallbacks.size());
+ oldDesc->mPatch = ioDesc->mPatch;
auto it = mAudioDeviceCallbacks.find(ioDesc->mIoHandle);
if (it != mAudioDeviceCallbacks.end()) {
- callbacks = it->second;
+ auto cbks = it->second;
+ auto it2 = cbks.find(ioDesc->mPortId);
+ if (it2 != cbks.end()) {
+ callbacks.emplace(ioDesc->mPortId, it2->second);
+ deviceId = oldDesc->getDeviceId();
+ }
}
}
- ALOGV("ioConfigChanged() new config for %s %d samplingRate %u, format %#x "
- "channel mask %#x frameCount %zu frameCountHAL %zu deviceId %d",
- event == AUDIO_OUTPUT_CONFIG_CHANGED ? "output" : "input",
- ioDesc->mIoHandle, ioDesc->mSamplingRate, ioDesc->mFormat,
- ioDesc->mChannelMask, ioDesc->mFrameCount, ioDesc->mFrameCountHAL,
- ioDesc->getDeviceId());
-
- } break;
- case AUDIO_CLIENT_STARTED: {
- sp<AudioIoDescriptor> oldDesc = getIoDescriptor_l(ioDesc->mIoHandle);
- if (oldDesc == 0) {
- ALOGW("ioConfigChanged() start client on unknown io! %d", ioDesc->mIoHandle);
break;
- }
- ALOGV("ioConfigChanged() AUDIO_CLIENT_STARTED io %d port %d num callbacks %zu",
- ioDesc->mIoHandle, ioDesc->mPortId, mAudioDeviceCallbacks.size());
- oldDesc->mPatch = ioDesc->mPatch;
- auto it = mAudioDeviceCallbacks.find(ioDesc->mIoHandle);
- if (it != mAudioDeviceCallbacks.end()) {
- auto cbks = it->second;
- auto it2 = cbks.find(ioDesc->mPortId);
- if (it2 != cbks.end()) {
- callbacks.emplace(ioDesc->mPortId, it2->second);
- deviceId = oldDesc->getDeviceId();
- }
- }
- } break;
}
for (auto wpCbk : callbacks) {
@@ -640,12 +635,13 @@
// If callbacksToCall is not empty, it implies ioDesc->mIoHandle and deviceId are valid
cb->onAudioDeviceUpdate(ioDesc->mIoHandle, deviceId);
}
+
+ return Status::ok();
}
status_t AudioSystem::AudioFlingerClient::getInputBufferSize(
- uint32_t sampleRate, audio_format_t format,
- audio_channel_mask_t channelMask, size_t* buffSize)
-{
+ uint32_t sampleRate, audio_format_t format,
+ audio_channel_mask_t channelMask, size_t* buffSize) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) {
return PERMISSION_DENIED;
@@ -657,7 +653,7 @@
size_t inBuffSize = af->getInputBufferSize(sampleRate, format, channelMask);
if (inBuffSize == 0) {
ALOGE("AudioSystem::getInputBufferSize failed sampleRate %d format %#x channelMask %#x",
- sampleRate, format, channelMask);
+ sampleRate, format, channelMask);
return BAD_VALUE;
}
// A benign race is possible here: we could overwrite a fresher cache entry
@@ -674,8 +670,8 @@
return NO_ERROR;
}
-sp<AudioIoDescriptor> AudioSystem::AudioFlingerClient::getIoDescriptor_l(audio_io_handle_t ioHandle)
-{
+sp<AudioIoDescriptor>
+AudioSystem::AudioFlingerClient::getIoDescriptor_l(audio_io_handle_t ioHandle) {
sp<AudioIoDescriptor> desc;
ssize_t index = mIoDescriptors.indexOfKey(ioHandle);
if (index >= 0) {
@@ -684,19 +680,19 @@
return desc;
}
-sp<AudioIoDescriptor> AudioSystem::AudioFlingerClient::getIoDescriptor(audio_io_handle_t ioHandle)
-{
+sp<AudioIoDescriptor> AudioSystem::AudioFlingerClient::getIoDescriptor(audio_io_handle_t ioHandle) {
Mutex::Autolock _l(mLock);
return getIoDescriptor_l(ioHandle);
}
status_t AudioSystem::AudioFlingerClient::addAudioDeviceCallback(
const wp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo,
- audio_port_handle_t portId)
-{
+ audio_port_handle_t portId) {
ALOGV("%s audioIo %d portId %d", __func__, audioIo, portId);
Mutex::Autolock _l(mLock);
- auto& callbacks = mAudioDeviceCallbacks.emplace(audioIo, std::map<audio_port_handle_t, wp<AudioDeviceCallback>>()).first->second;
+ auto& callbacks = mAudioDeviceCallbacks.emplace(
+ audioIo,
+ std::map<audio_port_handle_t, wp<AudioDeviceCallback>>()).first->second;
auto result = callbacks.try_emplace(portId, callback);
if (!result.second) {
return INVALID_OPERATION;
@@ -706,8 +702,7 @@
status_t AudioSystem::AudioFlingerClient::removeAudioDeviceCallback(
const wp<AudioDeviceCallback>& callback __unused, audio_io_handle_t audioIo,
- audio_port_handle_t portId)
-{
+ audio_port_handle_t portId) {
ALOGV("%s audioIo %d portId %d", __func__, audioIo, portId);
Mutex::Autolock _l(mLock);
auto it = mAudioDeviceCallbacks.find(audioIo);
@@ -723,8 +718,7 @@
return NO_ERROR;
}
-/* static */ uintptr_t AudioSystem::addErrorCallback(audio_error_callback cb)
-{
+/* static */ uintptr_t AudioSystem::addErrorCallback(audio_error_callback cb) {
Mutex::Autolock _l(gLockErrorCallbacks);
gAudioErrorCallbacks.insert(cb);
return reinterpret_cast<uintptr_t>(cb);
@@ -738,22 +732,25 @@
/* static */ void AudioSystem::reportError(status_t err) {
Mutex::Autolock _l(gLockErrorCallbacks);
for (auto callback : gAudioErrorCallbacks) {
- callback(err);
+ callback(err);
}
}
-/*static*/ void AudioSystem::setDynPolicyCallback(dynamic_policy_callback cb)
-{
+/*static*/ void AudioSystem::setDynPolicyCallback(dynamic_policy_callback cb) {
Mutex::Autolock _l(gLock);
gDynPolicyCallback = cb;
}
-/*static*/ void AudioSystem::setRecordConfigCallback(record_config_callback cb)
-{
+/*static*/ void AudioSystem::setRecordConfigCallback(record_config_callback cb) {
Mutex::Autolock _l(gLock);
gRecordConfigCallback = cb;
}
+/*static*/ void AudioSystem::setRoutingCallback(routing_callback cb) {
+ Mutex::Autolock _l(gLock);
+ gRoutingCallback = cb;
+}
+
// client singleton for AudioPolicyService binder interface
// protected by gLockAPS
sp<IAudioPolicyService> AudioSystem::gAudioPolicyService;
@@ -761,8 +758,7 @@
// establish binder interface to AudioPolicy service
-const sp<IAudioPolicyService> AudioSystem::get_audio_policy_service()
-{
+const sp<IAudioPolicyService> AudioSystem::get_audio_policy_service() {
sp<IAudioPolicyService> ap;
sp<AudioPolicyServiceClient> apc;
{
@@ -802,8 +798,7 @@
// ---------------------------------------------------------------------------
-void AudioSystem::onNewAudioModulesAvailable()
-{
+void AudioSystem::onNewAudioModulesAvailable() {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return;
aps->onNewAudioModulesAvailable();
@@ -811,13 +806,12 @@
status_t AudioSystem::setDeviceConnectionState(audio_devices_t device,
audio_policy_dev_state_t state,
- const char *device_address,
- const char *device_name,
- audio_format_t encodedFormat)
-{
+ const char* device_address,
+ const char* device_name,
+ audio_format_t encodedFormat) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
- const char *address = "";
- const char *name = "";
+ const char* address = "";
+ const char* name = "";
if (aps == 0) return PERMISSION_DENIED;
@@ -827,26 +821,46 @@
if (device_name != NULL) {
name = device_name;
}
- return aps->setDeviceConnectionState(device, state, address, name, encodedFormat);
+
+ media::AudioDevice deviceAidl;
+ deviceAidl.type = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_devices_t_int32_t(device));
+ deviceAidl.address = address;
+
+ return statusTFromBinderStatus(
+ aps->setDeviceConnectionState(
+ deviceAidl,
+ VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_policy_dev_state_t_AudioPolicyDeviceState(state)),
+ name,
+ VALUE_OR_RETURN_STATUS(legacy2aidl_audio_format_t_AudioFormat(encodedFormat))));
}
audio_policy_dev_state_t AudioSystem::getDeviceConnectionState(audio_devices_t device,
- const char *device_address)
-{
+ const char* device_address) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE;
- return aps->getDeviceConnectionState(device, device_address);
+ auto result = [&]() -> ConversionResult<audio_policy_dev_state_t> {
+ media::AudioDevice deviceAidl;
+ deviceAidl.type = VALUE_OR_RETURN(legacy2aidl_audio_devices_t_int32_t(device));
+ deviceAidl.address = device_address;
+
+ media::AudioPolicyDeviceState result;
+ RETURN_IF_ERROR(statusTFromBinderStatus(
+ aps->getDeviceConnectionState(deviceAidl, &result)));
+
+ return aidl2legacy_AudioPolicyDeviceState_audio_policy_dev_state_t(result);
+ }();
+ return result.value_or(AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE);
}
status_t AudioSystem::handleDeviceConfigChange(audio_devices_t device,
- const char *device_address,
- const char *device_name,
- audio_format_t encodedFormat)
-{
+ const char* device_address,
+ const char* device_name,
+ audio_format_t encodedFormat) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
- const char *address = "";
- const char *name = "";
+ const char* address = "";
+ const char* name = "";
if (aps == 0) return PERMISSION_DENIED;
@@ -856,294 +870,528 @@
if (device_name != NULL) {
name = device_name;
}
- return aps->handleDeviceConfigChange(device, address, name, encodedFormat);
+
+ media::AudioDevice deviceAidl;
+ deviceAidl.type = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_devices_t_int32_t(device));
+ deviceAidl.address = address;
+
+ return statusTFromBinderStatus(
+ aps->handleDeviceConfigChange(deviceAidl, name, VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_format_t_AudioFormat(encodedFormat))));
}
-status_t AudioSystem::setPhoneState(audio_mode_t state, uid_t uid)
-{
+status_t AudioSystem::setPhoneState(audio_mode_t state, uid_t uid) {
if (uint32_t(state) >= AUDIO_MODE_CNT) return BAD_VALUE;
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->setPhoneState(state, uid);
+ return statusTFromBinderStatus(aps->setPhoneState(
+ VALUE_OR_RETURN_STATUS(legacy2aidl_audio_mode_t_AudioMode(state)),
+ VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(uid))));
}
-status_t AudioSystem::setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config)
-{
+status_t
+AudioSystem::setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->setForceUse(usage, config);
+
+ return statusTFromBinderStatus(
+ aps->setForceUse(
+ VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_policy_force_use_t_AudioPolicyForceUse(usage)),
+ VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_policy_forced_cfg_t_AudioPolicyForcedConfig(
+ config))));
}
-audio_policy_forced_cfg_t AudioSystem::getForceUse(audio_policy_force_use_t usage)
-{
+audio_policy_forced_cfg_t AudioSystem::getForceUse(audio_policy_force_use_t usage) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return AUDIO_POLICY_FORCE_NONE;
- return aps->getForceUse(usage);
+
+ auto result = [&]() -> ConversionResult<audio_policy_forced_cfg_t> {
+ media::AudioPolicyForceUse usageAidl = VALUE_OR_RETURN(
+ legacy2aidl_audio_policy_force_use_t_AudioPolicyForceUse(usage));
+ media::AudioPolicyForcedConfig configAidl;
+ RETURN_IF_ERROR(statusTFromBinderStatus(
+ aps->getForceUse(usageAidl, &configAidl)));
+ return aidl2legacy_AudioPolicyForcedConfig_audio_policy_forced_cfg_t(configAidl);
+ }();
+
+ return result.value_or(AUDIO_POLICY_FORCE_NONE);
}
-audio_io_handle_t AudioSystem::getOutput(audio_stream_type_t stream)
-{
+audio_io_handle_t AudioSystem::getOutput(audio_stream_type_t stream) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
- if (aps == 0) return 0;
- return aps->getOutput(stream);
+ if (aps == 0) return AUDIO_IO_HANDLE_NONE;
+
+ auto result = [&]() -> ConversionResult<audio_io_handle_t> {
+ media::AudioStreamType streamAidl = VALUE_OR_RETURN(
+ legacy2aidl_audio_stream_type_t_AudioStreamType(stream));
+ int32_t outputAidl;
+ RETURN_IF_ERROR(
+ statusTFromBinderStatus(aps->getOutput(streamAidl, &outputAidl)));
+ return aidl2legacy_int32_t_audio_io_handle_t(outputAidl);
+ }();
+
+ return result.value_or(AUDIO_IO_HANDLE_NONE);
}
-status_t AudioSystem::getOutputForAttr(audio_attributes_t *attr,
- audio_io_handle_t *output,
- audio_session_t session,
- audio_stream_type_t *stream,
- pid_t pid,
- uid_t uid,
- const audio_config_t *config,
- audio_output_flags_t flags,
- audio_port_handle_t *selectedDeviceId,
- audio_port_handle_t *portId,
- std::vector<audio_io_handle_t> *secondaryOutputs)
-{
+status_t AudioSystem::getOutputForAttr(audio_attributes_t* attr,
+ audio_io_handle_t* output,
+ audio_session_t session,
+ audio_stream_type_t* stream,
+ const Identity& identity,
+ const audio_config_t* config,
+ audio_output_flags_t flags,
+ audio_port_handle_t* selectedDeviceId,
+ audio_port_handle_t* portId,
+ std::vector<audio_io_handle_t>* secondaryOutputs) {
+ if (attr == nullptr) {
+ ALOGE("%s NULL audio attributes", __func__);
+ return BAD_VALUE;
+ }
+ if (output == nullptr) {
+ ALOGE("%s NULL output - shouldn't happen", __func__);
+ return BAD_VALUE;
+ }
+ if (selectedDeviceId == nullptr) {
+ ALOGE("%s NULL selectedDeviceId - shouldn't happen", __func__);
+ return BAD_VALUE;
+ }
+ if (portId == nullptr) {
+ ALOGE("%s NULL portId - shouldn't happen", __func__);
+ return BAD_VALUE;
+ }
+ if (secondaryOutputs == nullptr) {
+ ALOGE("%s NULL secondaryOutputs - shouldn't happen", __func__);
+ return BAD_VALUE;
+ }
+
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return NO_INIT;
- return aps->getOutputForAttr(attr, output, session, stream, pid, uid,
- config,
- flags, selectedDeviceId, portId, secondaryOutputs);
+
+ media::AudioAttributesInternal attrAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_attributes_t_AudioAttributesInternal(*attr));
+ int32_t sessionAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_session_t_int32_t(session));
+ media::AudioConfig configAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_config_t_AudioConfig(*config));
+ int32_t flagsAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_output_flags_t_int32_t_mask(flags));
+ int32_t selectedDeviceIdAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_port_handle_t_int32_t(*selectedDeviceId));
+
+ media::GetOutputForAttrResponse responseAidl;
+
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->getOutputForAttr(attrAidl, sessionAidl, identity, configAidl, flagsAidl,
+ selectedDeviceIdAidl, &responseAidl)));
+
+ *output = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_int32_t_audio_io_handle_t(responseAidl.output));
+
+ if (stream != nullptr) {
+ *stream = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_AudioStreamType_audio_stream_type_t(responseAidl.stream));
+ }
+ *selectedDeviceId = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_int32_t_audio_port_handle_t(responseAidl.selectedDeviceId));
+ *portId = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_port_handle_t(responseAidl.portId));
+ *secondaryOutputs = VALUE_OR_RETURN_STATUS(convertContainer<std::vector<audio_io_handle_t>>(
+ responseAidl.secondaryOutputs, aidl2legacy_int32_t_audio_io_handle_t));
+
+ return OK;
}
-status_t AudioSystem::startOutput(audio_port_handle_t portId)
-{
+status_t AudioSystem::startOutput(audio_port_handle_t portId) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->startOutput(portId);
+
+ int32_t portIdAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_port_handle_t_int32_t(portId));
+ return statusTFromBinderStatus(aps->startOutput(portIdAidl));
}
-status_t AudioSystem::stopOutput(audio_port_handle_t portId)
-{
+status_t AudioSystem::stopOutput(audio_port_handle_t portId) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->stopOutput(portId);
+
+ int32_t portIdAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_port_handle_t_int32_t(portId));
+ return statusTFromBinderStatus(aps->stopOutput(portIdAidl));
}
-void AudioSystem::releaseOutput(audio_port_handle_t portId)
-{
+void AudioSystem::releaseOutput(audio_port_handle_t portId) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return;
- aps->releaseOutput(portId);
+
+ auto status = [&]() -> status_t {
+ int32_t portIdAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_port_handle_t_int32_t(portId));
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(aps->releaseOutput(portIdAidl)));
+ return OK;
+ }();
+
+ // Ignore status.
+ (void) status;
}
-status_t AudioSystem::getInputForAttr(const audio_attributes_t *attr,
- audio_io_handle_t *input,
- audio_unique_id_t riid,
- audio_session_t session,
- pid_t pid,
- uid_t uid,
- const String16& opPackageName,
- const audio_config_base_t *config,
- audio_input_flags_t flags,
- audio_port_handle_t *selectedDeviceId,
- audio_port_handle_t *portId)
-{
+status_t AudioSystem::getInputForAttr(const audio_attributes_t* attr,
+ audio_io_handle_t* input,
+ audio_unique_id_t riid,
+ audio_session_t session,
+ const Identity &identity,
+ const audio_config_base_t* config,
+ audio_input_flags_t flags,
+ audio_port_handle_t* selectedDeviceId,
+ audio_port_handle_t* portId) {
+ if (attr == NULL) {
+ ALOGE("getInputForAttr NULL attr - shouldn't happen");
+ return BAD_VALUE;
+ }
+ if (input == NULL) {
+ ALOGE("getInputForAttr NULL input - shouldn't happen");
+ return BAD_VALUE;
+ }
+ if (selectedDeviceId == NULL) {
+ ALOGE("getInputForAttr NULL selectedDeviceId - shouldn't happen");
+ return BAD_VALUE;
+ }
+ if (portId == NULL) {
+ ALOGE("getInputForAttr NULL portId - shouldn't happen");
+ return BAD_VALUE;
+ }
+
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return NO_INIT;
- return aps->getInputForAttr(
- attr, input, riid, session, pid, uid, opPackageName,
- config, flags, selectedDeviceId, portId);
+
+ media::AudioAttributesInternal attrAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_attributes_t_AudioAttributesInternal(*attr));
+ int32_t inputAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(*input));
+ int32_t riidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_unique_id_t_int32_t(riid));
+ int32_t sessionAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_session_t_int32_t(session));
+ media::AudioConfigBase configAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_config_base_t_AudioConfigBase(*config));
+ int32_t flagsAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_input_flags_t_int32_t_mask(flags));
+ int32_t selectedDeviceIdAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_port_handle_t_int32_t(*selectedDeviceId));
+
+ media::GetInputForAttrResponse response;
+
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->getInputForAttr(attrAidl, inputAidl, riidAidl, sessionAidl, identity,
+ configAidl, flagsAidl, selectedDeviceIdAidl, &response)));
+
+ *input = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_io_handle_t(response.input));
+ *selectedDeviceId = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_int32_t_audio_port_handle_t(response.selectedDeviceId));
+ *portId = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_port_handle_t(response.portId));
+
+ return OK;
}
-status_t AudioSystem::startInput(audio_port_handle_t portId)
-{
+status_t AudioSystem::startInput(audio_port_handle_t portId) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->startInput(portId);
+
+ int32_t portIdAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_port_handle_t_int32_t(portId));
+ return statusTFromBinderStatus(aps->startInput(portIdAidl));
}
-status_t AudioSystem::stopInput(audio_port_handle_t portId)
-{
+status_t AudioSystem::stopInput(audio_port_handle_t portId) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->stopInput(portId);
+
+ int32_t portIdAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_port_handle_t_int32_t(portId));
+ return statusTFromBinderStatus(aps->stopInput(portIdAidl));
}
-void AudioSystem::releaseInput(audio_port_handle_t portId)
-{
+void AudioSystem::releaseInput(audio_port_handle_t portId) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return;
- aps->releaseInput(portId);
+
+ auto status = [&]() -> status_t {
+ int32_t portIdAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_port_handle_t_int32_t(portId));
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(aps->releaseInput(portIdAidl)));
+ return OK;
+ }();
+
+ // Ignore status.
+ (void) status;
}
status_t AudioSystem::initStreamVolume(audio_stream_type_t stream,
- int indexMin,
- int indexMax)
-{
+ int indexMin,
+ int indexMax) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->initStreamVolume(stream, indexMin, indexMax);
+
+ media::AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_stream_type_t_AudioStreamType(stream));
+ int32_t indexMinAidl = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(indexMin));
+ int32_t indexMaxAidl = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(indexMax));
+ return statusTFromBinderStatus(
+ aps->initStreamVolume(streamAidl, indexMinAidl, indexMaxAidl));
}
status_t AudioSystem::setStreamVolumeIndex(audio_stream_type_t stream,
int index,
- audio_devices_t device)
-{
+ audio_devices_t device) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->setStreamVolumeIndex(stream, index, device);
+
+ media::AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_stream_type_t_AudioStreamType(stream));
+ int32_t indexAidl = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(index));
+ int32_t deviceAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_devices_t_int32_t(device));
+ return statusTFromBinderStatus(
+ aps->setStreamVolumeIndex(streamAidl, deviceAidl, indexAidl));
}
status_t AudioSystem::getStreamVolumeIndex(audio_stream_type_t stream,
- int *index,
- audio_devices_t device)
-{
+ int* index,
+ audio_devices_t device) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->getStreamVolumeIndex(stream, index, device);
+
+ media::AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_stream_type_t_AudioStreamType(stream));
+ int32_t deviceAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_devices_t_int32_t(device));
+ int32_t indexAidl;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->getStreamVolumeIndex(streamAidl, deviceAidl, &indexAidl)));
+ if (index != nullptr) {
+ *index = VALUE_OR_RETURN_STATUS(convertIntegral<int>(indexAidl));
+ }
+ return OK;
}
-status_t AudioSystem::setVolumeIndexForAttributes(const audio_attributes_t &attr,
+status_t AudioSystem::setVolumeIndexForAttributes(const audio_attributes_t& attr,
int index,
- audio_devices_t device)
-{
+ audio_devices_t device) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->setVolumeIndexForAttributes(attr, index, device);
+
+ media::AudioAttributesInternal attrAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_attributes_t_AudioAttributesInternal(attr));
+ int32_t indexAidl = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(index));
+ int32_t deviceAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_devices_t_int32_t(device));
+ return statusTFromBinderStatus(
+ aps->setVolumeIndexForAttributes(attrAidl, deviceAidl, indexAidl));
}
-status_t AudioSystem::getVolumeIndexForAttributes(const audio_attributes_t &attr,
- int &index,
- audio_devices_t device)
-{
+status_t AudioSystem::getVolumeIndexForAttributes(const audio_attributes_t& attr,
+ int& index,
+ audio_devices_t device) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->getVolumeIndexForAttributes(attr, index, device);
+
+ media::AudioAttributesInternal attrAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_attributes_t_AudioAttributesInternal(attr));
+ int32_t deviceAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_devices_t_int32_t(device));
+ int32_t indexAidl;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->getVolumeIndexForAttributes(attrAidl, deviceAidl, &indexAidl)));
+ index = VALUE_OR_RETURN_STATUS(convertIntegral<int>(indexAidl));
+ return OK;
}
-status_t AudioSystem::getMaxVolumeIndexForAttributes(const audio_attributes_t &attr, int &index)
-{
+status_t AudioSystem::getMaxVolumeIndexForAttributes(const audio_attributes_t& attr, int& index) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->getMaxVolumeIndexForAttributes(attr, index);
+
+ media::AudioAttributesInternal attrAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_attributes_t_AudioAttributesInternal(attr));
+ int32_t indexAidl;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->getMaxVolumeIndexForAttributes(attrAidl, &indexAidl)));
+ index = VALUE_OR_RETURN_STATUS(convertIntegral<int>(indexAidl));
+ return OK;
}
-status_t AudioSystem::getMinVolumeIndexForAttributes(const audio_attributes_t &attr, int &index)
-{
+status_t AudioSystem::getMinVolumeIndexForAttributes(const audio_attributes_t& attr, int& index) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->getMinVolumeIndexForAttributes(attr, index);
+
+ media::AudioAttributesInternal attrAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_attributes_t_AudioAttributesInternal(attr));
+ int32_t indexAidl;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->getMinVolumeIndexForAttributes(attrAidl, &indexAidl)));
+ index = VALUE_OR_RETURN_STATUS(convertIntegral<int>(indexAidl));
+ return OK;
}
-uint32_t AudioSystem::getStrategyForStream(audio_stream_type_t stream)
-{
+product_strategy_t AudioSystem::getStrategyForStream(audio_stream_type_t stream) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PRODUCT_STRATEGY_NONE;
- return aps->getStrategyForStream(stream);
+
+ auto result = [&]() -> ConversionResult<product_strategy_t> {
+ media::AudioStreamType streamAidl = VALUE_OR_RETURN(
+ legacy2aidl_audio_stream_type_t_AudioStreamType(stream));
+ int32_t resultAidl;
+ RETURN_IF_ERROR(statusTFromBinderStatus(
+ aps->getStrategyForStream(streamAidl, &resultAidl)));
+ return aidl2legacy_int32_t_product_strategy_t(resultAidl);
+ }();
+ return result.value_or(PRODUCT_STRATEGY_NONE);
}
-audio_devices_t AudioSystem::getDevicesForStream(audio_stream_type_t stream)
-{
+audio_devices_t AudioSystem::getDevicesForStream(audio_stream_type_t stream) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return AUDIO_DEVICE_NONE;
- return aps->getDevicesForStream(stream);
+
+ auto result = [&]() -> ConversionResult<audio_devices_t> {
+ media::AudioStreamType streamAidl = VALUE_OR_RETURN(
+ legacy2aidl_audio_stream_type_t_AudioStreamType(stream));
+ int32_t resultAidl;
+ RETURN_IF_ERROR(statusTFromBinderStatus(
+ aps->getDevicesForStream(streamAidl, &resultAidl)));
+ return aidl2legacy_int32_t_audio_devices_t(resultAidl);
+ }();
+ return result.value_or(AUDIO_DEVICE_NONE);
}
-status_t AudioSystem::getDevicesForAttributes(const AudioAttributes &aa,
- AudioDeviceTypeAddrVector *devices) {
+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);
+
+ media::AudioAttributesEx aaAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_AudioAttributes_AudioAttributesEx(aa));
+ std::vector<media::AudioDevice> retAidl;
+ RETURN_STATUS_IF_ERROR(
+ statusTFromBinderStatus(aps->getDevicesForAttributes(aaAidl, &retAidl)));
+ *devices = VALUE_OR_RETURN_STATUS(
+ convertContainer<AudioDeviceTypeAddrVector>(
+ retAidl,
+ aidl2legacy_AudioDeviceTypeAddress));
+ return OK;
}
-audio_io_handle_t AudioSystem::getOutputForEffect(const effect_descriptor_t *desc)
-{
+audio_io_handle_t AudioSystem::getOutputForEffect(const effect_descriptor_t* desc) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
// FIXME change return type to status_t, and return PERMISSION_DENIED here
if (aps == 0) return AUDIO_IO_HANDLE_NONE;
- return aps->getOutputForEffect(desc);
+
+ auto result = [&]() -> ConversionResult<audio_io_handle_t> {
+ media::EffectDescriptor descAidl = VALUE_OR_RETURN(
+ legacy2aidl_effect_descriptor_t_EffectDescriptor(*desc));
+ int32_t retAidl;
+ RETURN_IF_ERROR(
+ statusTFromBinderStatus(aps->getOutputForEffect(descAidl, &retAidl)));
+ return aidl2legacy_int32_t_audio_io_handle_t(retAidl);
+ }();
+
+ return result.value_or(AUDIO_IO_HANDLE_NONE);
}
-status_t AudioSystem::registerEffect(const effect_descriptor_t *desc,
- audio_io_handle_t io,
- uint32_t strategy,
- audio_session_t session,
- int id)
-{
+status_t AudioSystem::registerEffect(const effect_descriptor_t* desc,
+ audio_io_handle_t io,
+ product_strategy_t strategy,
+ audio_session_t session,
+ int id) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->registerEffect(desc, io, strategy, session, id);
+
+ media::EffectDescriptor descAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_effect_descriptor_t_EffectDescriptor(*desc));
+ int32_t ioAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(io));
+ int32_t strategyAidl = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_product_strategy_t(strategy));
+ int32_t sessionAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_session_t_int32_t(session));
+ int32_t idAidl = VALUE_OR_RETURN_STATUS(convertReinterpret<int32_t>(id));
+ return statusTFromBinderStatus(
+ aps->registerEffect(descAidl, ioAidl, strategyAidl, sessionAidl, idAidl));
}
-status_t AudioSystem::unregisterEffect(int id)
-{
+status_t AudioSystem::unregisterEffect(int id) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->unregisterEffect(id);
+
+ int32_t idAidl = VALUE_OR_RETURN_STATUS(convertReinterpret<int32_t>(id));
+ return statusTFromBinderStatus(
+ aps->unregisterEffect(idAidl));
}
-status_t AudioSystem::setEffectEnabled(int id, bool enabled)
-{
+status_t AudioSystem::setEffectEnabled(int id, bool enabled) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->setEffectEnabled(id, enabled);
+
+ int32_t idAidl = VALUE_OR_RETURN_STATUS(convertReinterpret<int32_t>(id));
+ return statusTFromBinderStatus(
+ aps->setEffectEnabled(idAidl, enabled));
}
-status_t AudioSystem::moveEffectsToIo(const std::vector<int>& ids, audio_io_handle_t io)
-{
+status_t AudioSystem::moveEffectsToIo(const std::vector<int>& ids, audio_io_handle_t io) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->moveEffectsToIo(ids, io);
+
+ std::vector<int32_t> idsAidl = VALUE_OR_RETURN_STATUS(
+ convertContainer<std::vector<int32_t>>(ids, convertReinterpret<int32_t, int>));
+ int32_t ioAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(io));
+ return statusTFromBinderStatus(aps->moveEffectsToIo(idsAidl, ioAidl));
}
-status_t AudioSystem::isStreamActive(audio_stream_type_t stream, bool* state, uint32_t inPastMs)
-{
+status_t AudioSystem::isStreamActive(audio_stream_type_t stream, bool* state, uint32_t inPastMs) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
if (state == NULL) return BAD_VALUE;
- *state = aps->isStreamActive(stream, inPastMs);
- return NO_ERROR;
+
+ media::AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_stream_type_t_AudioStreamType(stream));
+ int32_t inPastMsAidl = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(inPastMs));
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->isStreamActive(streamAidl, inPastMsAidl, state)));
+ return OK;
}
status_t AudioSystem::isStreamActiveRemotely(audio_stream_type_t stream, bool* state,
- uint32_t inPastMs)
-{
+ uint32_t inPastMs) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
if (state == NULL) return BAD_VALUE;
- *state = aps->isStreamActiveRemotely(stream, inPastMs);
- return NO_ERROR;
+
+ media::AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_stream_type_t_AudioStreamType(stream));
+ int32_t inPastMsAidl = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(inPastMs));
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->isStreamActiveRemotely(streamAidl, inPastMsAidl, state)));
+ return OK;
}
-status_t AudioSystem::isSourceActive(audio_source_t stream, bool* state)
-{
+status_t AudioSystem::isSourceActive(audio_source_t stream, bool* state) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
if (state == NULL) return BAD_VALUE;
- *state = aps->isSourceActive(stream);
- return NO_ERROR;
+
+ media::AudioSourceType streamAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_source_t_AudioSourceType(stream));
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->isSourceActive(streamAidl, state)));
+ return OK;
}
-uint32_t AudioSystem::getPrimaryOutputSamplingRate()
-{
+uint32_t AudioSystem::getPrimaryOutputSamplingRate() {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return 0;
return af->getPrimaryOutputSamplingRate();
}
-size_t AudioSystem::getPrimaryOutputFrameCount()
-{
+size_t AudioSystem::getPrimaryOutputFrameCount() {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return 0;
return af->getPrimaryOutputFrameCount();
}
-status_t AudioSystem::setLowRamDevice(bool isLowRamDevice, int64_t totalMemory)
-{
+status_t AudioSystem::setLowRamDevice(bool isLowRamDevice, int64_t totalMemory) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
return af->setLowRamDevice(isLowRamDevice, totalMemory);
}
-void AudioSystem::clearAudioConfigCache()
-{
+void AudioSystem::clearAudioConfigCache() {
// called by restoreTrack_l(), which needs new IAudioFlinger and IAudioPolicyService instances
ALOGV("clearAudioConfigCache()");
{
@@ -1162,74 +1410,152 @@
status_t AudioSystem::setSupportedSystemUsages(const std::vector<audio_usage_t>& systemUsages) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == nullptr) return PERMISSION_DENIED;
- return aps->setSupportedSystemUsages(systemUsages);
+
+ std::vector<media::AudioUsage> systemUsagesAidl = VALUE_OR_RETURN_STATUS(
+ convertContainer<std::vector<media::AudioUsage>>(systemUsages,
+ legacy2aidl_audio_usage_t_AudioUsage));
+ return statusTFromBinderStatus(aps->setSupportedSystemUsages(systemUsagesAidl));
}
-status_t AudioSystem::setAllowedCapturePolicy(uid_t uid, audio_flags_mask_t flags) {
+status_t AudioSystem::setAllowedCapturePolicy(uid_t uid, audio_flags_mask_t capturePolicy) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == nullptr) return PERMISSION_DENIED;
- return aps->setAllowedCapturePolicy(uid, flags);
+
+ int32_t uidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(uid));
+ int32_t capturePolicyAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_flags_mask_t_int32_t_mask(capturePolicy));
+ return statusTFromBinderStatus(aps->setAllowedCapturePolicy(uidAidl, capturePolicyAidl));
}
-bool AudioSystem::isOffloadSupported(const audio_offload_info_t& info)
-{
- ALOGV("isOffloadSupported()");
+audio_offload_mode_t AudioSystem::getOffloadSupport(const audio_offload_info_t& info) {
+ ALOGV("%s", __func__);
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
- if (aps == 0) return false;
- return aps->isOffloadSupported(info);
+ if (aps == 0) return AUDIO_OFFLOAD_NOT_SUPPORTED;
+
+ auto result = [&]() -> ConversionResult<audio_offload_mode_t> {
+ media::AudioOffloadInfo infoAidl = VALUE_OR_RETURN(
+ legacy2aidl_audio_offload_info_t_AudioOffloadInfo(info));
+ media::AudioOffloadMode retAidl;
+ RETURN_IF_ERROR(
+ statusTFromBinderStatus(aps->getOffloadSupport(infoAidl, &retAidl)));
+ return aidl2legacy_AudioOffloadMode_audio_offload_mode_t(retAidl);
+ }();
+
+ return result.value_or(static_cast<audio_offload_mode_t>(0));
}
status_t AudioSystem::listAudioPorts(audio_port_role_t role,
audio_port_type_t type,
- unsigned int *num_ports,
- struct audio_port *ports,
- unsigned int *generation)
-{
+ unsigned int* num_ports,
+ struct audio_port_v7* ports,
+ unsigned int* generation) {
+ if (num_ports == nullptr || (*num_ports != 0 && ports == nullptr) ||
+ generation == nullptr) {
+ return BAD_VALUE;
+ }
+
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->listAudioPorts(role, type, num_ports, ports, generation);
+
+ media::AudioPortRole roleAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_port_role_t_AudioPortRole(role));
+ media::AudioPortType typeAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_port_type_t_AudioPortType(type));
+ media::Int numPortsAidl;
+ numPortsAidl.value = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(*num_ports));
+ std::vector<media::AudioPort> portsAidl;
+ int32_t generationAidl;
+
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->listAudioPorts(roleAidl, typeAidl, &numPortsAidl, &portsAidl, &generationAidl)));
+ *num_ports = VALUE_OR_RETURN_STATUS(convertIntegral<unsigned int>(numPortsAidl.value));
+ *generation = VALUE_OR_RETURN_STATUS(convertIntegral<unsigned int>(generationAidl));
+ RETURN_STATUS_IF_ERROR(convertRange(portsAidl.begin(), portsAidl.end(), ports,
+ aidl2legacy_AudioPort_audio_port_v7));
+ return OK;
}
-status_t AudioSystem::getAudioPort(struct audio_port *port)
-{
+status_t AudioSystem::getAudioPort(struct audio_port_v7* port) {
+ if (port == nullptr) {
+ return BAD_VALUE;
+ }
+
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->getAudioPort(port);
+
+ media::AudioPort portAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_port_v7_AudioPort(*port));
+ RETURN_STATUS_IF_ERROR(
+ statusTFromBinderStatus(aps->getAudioPort(portAidl, &portAidl)));
+ *port = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioPort_audio_port_v7(portAidl));
+ return OK;
}
-status_t AudioSystem::createAudioPatch(const struct audio_patch *patch,
- audio_patch_handle_t *handle)
-{
+status_t AudioSystem::createAudioPatch(const struct audio_patch* patch,
+ audio_patch_handle_t* handle) {
+ if (patch == nullptr || handle == nullptr) {
+ return BAD_VALUE;
+ }
+
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->createAudioPatch(patch, handle);
+
+ media::AudioPatch patchAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_patch_AudioPatch(*patch));
+ int32_t handleAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_patch_handle_t_int32_t(*handle));
+ RETURN_STATUS_IF_ERROR(
+ statusTFromBinderStatus(aps->createAudioPatch(patchAidl, handleAidl, &handleAidl)));
+ *handle = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_patch_handle_t(handleAidl));
+ return OK;
}
-status_t AudioSystem::releaseAudioPatch(audio_patch_handle_t handle)
-{
+status_t AudioSystem::releaseAudioPatch(audio_patch_handle_t handle) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->releaseAudioPatch(handle);
+
+ int32_t handleAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_patch_handle_t_int32_t(handle));
+ return statusTFromBinderStatus(aps->releaseAudioPatch(handleAidl));
}
-status_t AudioSystem::listAudioPatches(unsigned int *num_patches,
- struct audio_patch *patches,
- unsigned int *generation)
-{
+status_t AudioSystem::listAudioPatches(unsigned int* num_patches,
+ struct audio_patch* patches,
+ unsigned int* generation) {
+ if (num_patches == nullptr || (*num_patches != 0 && patches == nullptr) ||
+ generation == nullptr) {
+ return BAD_VALUE;
+ }
+
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->listAudioPatches(num_patches, patches, generation);
+
+
+ media::Int numPatchesAidl;
+ numPatchesAidl.value = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(*num_patches));
+ std::vector<media::AudioPatch> patchesAidl;
+ int32_t generationAidl;
+
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->listAudioPatches(&numPatchesAidl, &patchesAidl, &generationAidl)));
+ *num_patches = VALUE_OR_RETURN_STATUS(convertIntegral<unsigned int>(numPatchesAidl.value));
+ *generation = VALUE_OR_RETURN_STATUS(convertIntegral<unsigned int>(generationAidl));
+ RETURN_STATUS_IF_ERROR(convertRange(patchesAidl.begin(), patchesAidl.end(), patches,
+ aidl2legacy_AudioPatch_audio_patch));
+ return OK;
}
-status_t AudioSystem::setAudioPortConfig(const struct audio_port_config *config)
-{
+status_t AudioSystem::setAudioPortConfig(const struct audio_port_config* config) {
+ if (config == nullptr) {
+ return BAD_VALUE;
+ }
+
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->setAudioPortConfig(config);
+
+ media::AudioPortConfig configAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_port_config_AudioPortConfig(*config));
+ return statusTFromBinderStatus(aps->setAudioPortConfig(configAidl));
}
-status_t AudioSystem::addAudioPortCallback(const sp<AudioPortCallback>& callback)
-{
+status_t AudioSystem::addAudioPortCallback(const sp<AudioPortCallback>& callback) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
@@ -1245,8 +1571,7 @@
}
/*static*/
-status_t AudioSystem::removeAudioPortCallback(const sp<AudioPortCallback>& callback)
-{
+status_t AudioSystem::removeAudioPortCallback(const sp<AudioPortCallback>& callback) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
@@ -1261,8 +1586,7 @@
return (ret < 0) ? INVALID_OPERATION : NO_ERROR;
}
-status_t AudioSystem::addAudioVolumeGroupCallback(const sp<AudioVolumeGroupCallback>& callback)
-{
+status_t AudioSystem::addAudioVolumeGroupCallback(const sp<AudioVolumeGroupCallback>& callback) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
@@ -1277,8 +1601,7 @@
return (ret < 0) ? INVALID_OPERATION : NO_ERROR;
}
-status_t AudioSystem::removeAudioVolumeGroupCallback(const sp<AudioVolumeGroupCallback>& callback)
-{
+status_t AudioSystem::removeAudioVolumeGroupCallback(const sp<AudioVolumeGroupCallback>& callback) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
@@ -1295,8 +1618,7 @@
status_t AudioSystem::addAudioDeviceCallback(
const wp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo,
- audio_port_handle_t portId)
-{
+ audio_port_handle_t portId) {
const sp<AudioFlingerClient> afc = getAudioFlingerClient();
if (afc == 0) {
return NO_INIT;
@@ -1313,8 +1635,7 @@
status_t AudioSystem::removeAudioDeviceCallback(
const wp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo,
- audio_port_handle_t portId)
-{
+ audio_port_handle_t portId) {
const sp<AudioFlingerClient> afc = getAudioFlingerClient();
if (afc == 0) {
return NO_INIT;
@@ -1322,8 +1643,7 @@
return afc->removeAudioDeviceCallback(callback, audioIo, portId);
}
-audio_port_handle_t AudioSystem::getDeviceIdForIo(audio_io_handle_t audioIo)
-{
+audio_port_handle_t AudioSystem::getDeviceIdForIo(audio_io_handle_t audioIo) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
const sp<AudioIoDescriptor> desc = getIoDescriptor(audioIo);
@@ -1333,224 +1653,346 @@
return desc->getDeviceId();
}
-status_t AudioSystem::acquireSoundTriggerSession(audio_session_t *session,
- audio_io_handle_t *ioHandle,
- audio_devices_t *device)
-{
+status_t AudioSystem::acquireSoundTriggerSession(audio_session_t* session,
+ audio_io_handle_t* ioHandle,
+ audio_devices_t* device) {
+ if (session == nullptr || ioHandle == nullptr || device == nullptr) {
+ return BAD_VALUE;
+ }
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->acquireSoundTriggerSession(session, ioHandle, device);
+
+ media::SoundTriggerSession retAidl;
+ RETURN_STATUS_IF_ERROR(
+ statusTFromBinderStatus(aps->acquireSoundTriggerSession(&retAidl)));
+ *session = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_session_t(retAidl.session));
+ *ioHandle = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_io_handle_t(retAidl.ioHandle));
+ *device = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_devices_t(retAidl.device));
+ return OK;
}
-status_t AudioSystem::releaseSoundTriggerSession(audio_session_t session)
-{
+status_t AudioSystem::releaseSoundTriggerSession(audio_session_t session) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->releaseSoundTriggerSession(session);
+
+ int32_t sessionAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_session_t_int32_t(session));
+ return statusTFromBinderStatus(aps->releaseSoundTriggerSession(sessionAidl));
}
-audio_mode_t AudioSystem::getPhoneState()
-{
+audio_mode_t AudioSystem::getPhoneState() {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return AUDIO_MODE_INVALID;
- return aps->getPhoneState();
+
+ auto result = [&]() -> ConversionResult<audio_mode_t> {
+ media::AudioMode retAidl;
+ RETURN_IF_ERROR(statusTFromBinderStatus(aps->getPhoneState(&retAidl)));
+ return aidl2legacy_AudioMode_audio_mode_t(retAidl);
+ }();
+
+ return result.value_or(AUDIO_MODE_INVALID);
}
-status_t AudioSystem::registerPolicyMixes(const Vector<AudioMix>& mixes, bool registration)
-{
+status_t AudioSystem::registerPolicyMixes(const Vector<AudioMix>& mixes, bool registration) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->registerPolicyMixes(mixes, registration);
+
+ size_t mixesSize = std::min(mixes.size(), size_t{MAX_MIXES_PER_POLICY});
+ std::vector<media::AudioMix> mixesAidl;
+ RETURN_STATUS_IF_ERROR(
+ convertRange(mixes.begin(), mixes.begin() + mixesSize, std::back_inserter(mixesAidl),
+ legacy2aidl_AudioMix));
+ return statusTFromBinderStatus(aps->registerPolicyMixes(mixesAidl, registration));
}
-status_t AudioSystem::setUidDeviceAffinities(uid_t uid, const AudioDeviceTypeAddrVector& devices)
-{
+status_t AudioSystem::setUidDeviceAffinities(uid_t uid, const AudioDeviceTypeAddrVector& devices) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->setUidDeviceAffinities(uid, devices);
+
+ int32_t uidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(uid));
+ std::vector<media::AudioDevice> devicesAidl = VALUE_OR_RETURN_STATUS(
+ convertContainer<std::vector<media::AudioDevice>>(devices,
+ legacy2aidl_AudioDeviceTypeAddress));
+ return statusTFromBinderStatus(aps->setUidDeviceAffinities(uidAidl, devicesAidl));
}
status_t AudioSystem::removeUidDeviceAffinities(uid_t uid) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->removeUidDeviceAffinities(uid);
+
+ int32_t uidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(uid));
+ return statusTFromBinderStatus(aps->removeUidDeviceAffinities(uidAidl));
}
status_t AudioSystem::setUserIdDeviceAffinities(int userId,
- const AudioDeviceTypeAddrVector& devices)
-{
+ const AudioDeviceTypeAddrVector& devices) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->setUserIdDeviceAffinities(userId, devices);
+
+ int32_t userIdAidl = VALUE_OR_RETURN_STATUS(convertReinterpret<int32_t>(userId));
+ std::vector<media::AudioDevice> devicesAidl = VALUE_OR_RETURN_STATUS(
+ convertContainer<std::vector<media::AudioDevice>>(devices,
+ legacy2aidl_AudioDeviceTypeAddress));
+ return statusTFromBinderStatus(
+ aps->setUserIdDeviceAffinities(userIdAidl, devicesAidl));
}
-status_t AudioSystem::removeUserIdDeviceAffinities(int userId)
-{
+status_t AudioSystem::removeUserIdDeviceAffinities(int userId) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->removeUserIdDeviceAffinities(userId);
+ int32_t userIdAidl = VALUE_OR_RETURN_STATUS(convertReinterpret<int32_t>(userId));
+ return statusTFromBinderStatus(aps->removeUserIdDeviceAffinities(userIdAidl));
}
-status_t AudioSystem::startAudioSource(const struct audio_port_config *source,
- const audio_attributes_t *attributes,
- audio_port_handle_t *portId)
-{
+status_t AudioSystem::startAudioSource(const struct audio_port_config* source,
+ const audio_attributes_t* attributes,
+ audio_port_handle_t* portId) {
+ if (source == nullptr || attributes == nullptr || portId == nullptr) {
+ return BAD_VALUE;
+ }
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->startAudioSource(source, attributes, portId);
+
+ media::AudioPortConfig sourceAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_port_config_AudioPortConfig(*source));
+ media::AudioAttributesInternal attributesAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_attributes_t_AudioAttributesInternal(*attributes));
+ int32_t portIdAidl;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->startAudioSource(sourceAidl, attributesAidl, &portIdAidl)));
+ *portId = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_port_handle_t(portIdAidl));
+ return OK;
}
-status_t AudioSystem::stopAudioSource(audio_port_handle_t portId)
-{
+status_t AudioSystem::stopAudioSource(audio_port_handle_t portId) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->stopAudioSource(portId);
+
+ int32_t portIdAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_port_handle_t_int32_t(portId));
+ return statusTFromBinderStatus(aps->stopAudioSource(portIdAidl));
}
-status_t AudioSystem::setMasterMono(bool mono)
-{
+status_t AudioSystem::setMasterMono(bool mono) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->setMasterMono(mono);
+ return statusTFromBinderStatus(aps->setMasterMono(mono));
}
-status_t AudioSystem::getMasterMono(bool *mono)
-{
+status_t AudioSystem::getMasterMono(bool* mono) {
+ if (mono == nullptr) {
+ return BAD_VALUE;
+ }
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->getMasterMono(mono);
+ return statusTFromBinderStatus(aps->getMasterMono(mono));
}
-status_t AudioSystem::setMasterBalance(float balance)
-{
+status_t AudioSystem::setMasterBalance(float balance) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
return af->setMasterBalance(balance);
}
-status_t AudioSystem::getMasterBalance(float *balance)
-{
+status_t AudioSystem::getMasterBalance(float* balance) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
return af->getMasterBalance(balance);
}
-float AudioSystem::getStreamVolumeDB(audio_stream_type_t stream, int index, audio_devices_t device)
-{
+float
+AudioSystem::getStreamVolumeDB(audio_stream_type_t stream, int index, audio_devices_t device) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return NAN;
- return aps->getStreamVolumeDB(stream, index, device);
+
+ auto result = [&]() -> ConversionResult<float> {
+ media::AudioStreamType streamAidl = VALUE_OR_RETURN(
+ legacy2aidl_audio_stream_type_t_AudioStreamType(stream));
+ int32_t indexAidl = VALUE_OR_RETURN(convertIntegral<int32_t>(index));
+ int32_t deviceAidl = VALUE_OR_RETURN(legacy2aidl_audio_devices_t_int32_t(device));
+ float retAidl;
+ RETURN_IF_ERROR(statusTFromBinderStatus(
+ aps->getStreamVolumeDB(streamAidl, indexAidl, deviceAidl, &retAidl)));
+ return retAidl;
+ }();
+ return result.value_or(NAN);
}
-status_t AudioSystem::getMicrophones(std::vector<media::MicrophoneInfo> *microphones)
-{
+status_t AudioSystem::getMicrophones(std::vector<media::MicrophoneInfo>* microphones) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
return af->getMicrophones(microphones);
}
status_t AudioSystem::setAudioHalPids(const std::vector<pid_t>& pids) {
- const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
- if (af == nullptr) return PERMISSION_DENIED;
- return af->setAudioHalPids(pids);
+ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+ if (af == nullptr) return PERMISSION_DENIED;
+ return af->setAudioHalPids(pids);
}
-status_t AudioSystem::getSurroundFormats(unsigned int *numSurroundFormats,
- audio_format_t *surroundFormats,
- bool *surroundFormatsEnabled,
- bool reported)
-{
+status_t AudioSystem::getSurroundFormats(unsigned int* numSurroundFormats,
+ audio_format_t* surroundFormats,
+ bool* surroundFormatsEnabled) {
+ if (numSurroundFormats == nullptr || (*numSurroundFormats != 0 &&
+ (surroundFormats == nullptr ||
+ surroundFormatsEnabled == nullptr))) {
+ return BAD_VALUE;
+ }
+
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->getSurroundFormats(
- numSurroundFormats, surroundFormats, surroundFormatsEnabled, reported);
+ media::Int numSurroundFormatsAidl;
+ numSurroundFormatsAidl.value =
+ VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(*numSurroundFormats));
+ std::vector<media::audio::common::AudioFormat> surroundFormatsAidl;
+ std::vector<bool> surroundFormatsEnabledAidl;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->getSurroundFormats(&numSurroundFormatsAidl, &surroundFormatsAidl,
+ &surroundFormatsEnabledAidl)));
+
+ *numSurroundFormats = VALUE_OR_RETURN_STATUS(
+ convertIntegral<unsigned int>(numSurroundFormatsAidl.value));
+ RETURN_STATUS_IF_ERROR(
+ convertRange(surroundFormatsAidl.begin(), surroundFormatsAidl.end(), surroundFormats,
+ aidl2legacy_AudioFormat_audio_format_t));
+ std::copy(surroundFormatsEnabledAidl.begin(), surroundFormatsEnabledAidl.end(),
+ surroundFormatsEnabled);
+ return OK;
}
-status_t AudioSystem::setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled)
-{
+status_t AudioSystem::getReportedSurroundFormats(unsigned int* numSurroundFormats,
+ audio_format_t* surroundFormats) {
+ if (numSurroundFormats == nullptr || (*numSurroundFormats != 0 && surroundFormats == nullptr)) {
+ return BAD_VALUE;
+ }
+
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->setSurroundFormatEnabled(audioFormat, enabled);
+ media::Int numSurroundFormatsAidl;
+ numSurroundFormatsAidl.value =
+ VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(*numSurroundFormats));
+ std::vector<media::audio::common::AudioFormat> surroundFormatsAidl;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->getReportedSurroundFormats(&numSurroundFormatsAidl, &surroundFormatsAidl)));
+
+ *numSurroundFormats = VALUE_OR_RETURN_STATUS(
+ convertIntegral<unsigned int>(numSurroundFormatsAidl.value));
+ RETURN_STATUS_IF_ERROR(
+ convertRange(surroundFormatsAidl.begin(), surroundFormatsAidl.end(), surroundFormats,
+ aidl2legacy_AudioFormat_audio_format_t));
+ return OK;
}
-status_t AudioSystem::setAssistantUid(uid_t uid)
-{
- const sp <IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+status_t AudioSystem::setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled) {
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->setAssistantUid(uid);
+ media::audio::common::AudioFormat audioFormatAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_format_t_AudioFormat(audioFormat));
+ return statusTFromBinderStatus(
+ aps->setSurroundFormatEnabled(audioFormatAidl, enabled));
}
-status_t AudioSystem::setA11yServicesUids(const std::vector<uid_t>& uids)
-{
- const sp <IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+status_t AudioSystem::setAssistantUid(uid_t uid) {
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->setA11yServicesUids(uids);
+ int32_t uidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(uid));
+ return statusTFromBinderStatus(aps->setAssistantUid(uidAidl));
}
-status_t AudioSystem::setCurrentImeUid(uid_t uid)
-{
- const sp <IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+status_t AudioSystem::setA11yServicesUids(const std::vector<uid_t>& uids) {
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->setCurrentImeUid(uid);
+ std::vector<int32_t> uidsAidl = VALUE_OR_RETURN_STATUS(
+ convertContainer<std::vector<int32_t>>(uids, legacy2aidl_uid_t_int32_t));
+ return statusTFromBinderStatus(aps->setA11yServicesUids(uidsAidl));
}
-bool AudioSystem::isHapticPlaybackSupported()
-{
+status_t AudioSystem::setCurrentImeUid(uid_t uid) {
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+
+ int32_t uidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(uid));
+ return statusTFromBinderStatus(aps->setCurrentImeUid(uidAidl));
+}
+
+bool AudioSystem::isHapticPlaybackSupported() {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return false;
- return aps->isHapticPlaybackSupported();
+
+ auto result = [&]() -> ConversionResult<bool> {
+ bool retVal;
+ RETURN_IF_ERROR(
+ statusTFromBinderStatus(aps->isHapticPlaybackSupported(&retVal)));
+ return retVal;
+ }();
+ return result.value_or(false);
}
status_t AudioSystem::getHwOffloadEncodingFormatsSupportedForA2DP(
- std::vector<audio_format_t> *formats) {
- const sp <IAudioPolicyService>
- & aps = AudioSystem::get_audio_policy_service();
+ std::vector<audio_format_t>* formats) {
+ if (formats == nullptr) {
+ return BAD_VALUE;
+ }
+
+ const sp<IAudioPolicyService>
+ & aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->getHwOffloadEncodingFormatsSupportedForA2DP(formats);
+
+ std::vector<media::audio::common::AudioFormat> formatsAidl;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->getHwOffloadEncodingFormatsSupportedForA2DP(&formatsAidl)));
+ *formats = VALUE_OR_RETURN_STATUS(
+ convertContainer<std::vector<audio_format_t>>(formatsAidl,
+ aidl2legacy_AudioFormat_audio_format_t));
+ return OK;
}
-status_t AudioSystem::listAudioProductStrategies(AudioProductStrategyVector &strategies)
-{
+status_t AudioSystem::listAudioProductStrategies(AudioProductStrategyVector& strategies) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->listAudioProductStrategies(strategies);
+
+ std::vector<media::AudioProductStrategy> strategiesAidl;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->listAudioProductStrategies(&strategiesAidl)));
+ strategies = VALUE_OR_RETURN_STATUS(
+ convertContainer<AudioProductStrategyVector>(strategiesAidl,
+ aidl2legacy_AudioProductStrategy));
+ return OK;
}
-audio_attributes_t AudioSystem::streamTypeToAttributes(audio_stream_type_t stream)
-{
+audio_attributes_t AudioSystem::streamTypeToAttributes(audio_stream_type_t stream) {
AudioProductStrategyVector strategies;
listAudioProductStrategies(strategies);
- for (const auto &strategy : strategies) {
+ for (const auto& strategy : strategies) {
auto attrVect = strategy.getAudioAttributes();
- auto iter = std::find_if(begin(attrVect), end(attrVect), [&stream](const auto &attributes) {
- return attributes.getStreamType() == stream; });
+ auto iter = std::find_if(begin(attrVect), end(attrVect), [&stream](const auto& attributes) {
+ return attributes.getStreamType() == stream;
+ });
if (iter != end(attrVect)) {
return iter->getAttributes();
}
}
- ALOGE("invalid stream type %s when converting to attributes", toString(stream).c_str());
+ ALOGE("invalid stream type %s when converting to attributes", toString(stream).c_str());
return AUDIO_ATTRIBUTES_INITIALIZER;
}
-audio_stream_type_t AudioSystem::attributesToStreamType(const audio_attributes_t &attr)
-{
+audio_stream_type_t AudioSystem::attributesToStreamType(const audio_attributes_t& attr) {
product_strategy_t psId;
status_t ret = AudioSystem::getProductStrategyFromAudioAttributes(AudioAttributes(attr), psId);
if (ret != NO_ERROR) {
- ALOGE("no strategy found for attributes %s", toString(attr).c_str());
+ ALOGE("no strategy found for attributes %s", toString(attr).c_str());
return AUDIO_STREAM_MUSIC;
}
AudioProductStrategyVector strategies;
listAudioProductStrategies(strategies);
- for (const auto &strategy : strategies) {
+ for (const auto& strategy : strategies) {
if (strategy.getId() == psId) {
auto attrVect = strategy.getAudioAttributes();
- auto iter = std::find_if(begin(attrVect), end(attrVect), [&attr](const auto &refAttr) {
- return AudioProductStrategy::attributesMatches(
- refAttr.getAttributes(), attr); });
+ auto iter = std::find_if(begin(attrVect), end(attrVect), [&attr](const auto& refAttr) {
+ return AudioProductStrategy::attributesMatches(
+ refAttr.getAttributes(), attr);
+ });
if (iter != end(attrVect)) {
return iter->getStreamType();
}
@@ -1561,131 +2003,204 @@
// virtual source is not expected to have an associated product strategy
break;
default:
- ALOGE("invalid attributes %s when converting to stream", toString(attr).c_str());
+ ALOGE("invalid attributes %s when converting to stream", toString(attr).c_str());
break;
}
return AUDIO_STREAM_MUSIC;
}
-status_t AudioSystem::getProductStrategyFromAudioAttributes(const AudioAttributes &aa,
- product_strategy_t &productStrategy)
-{
+status_t AudioSystem::getProductStrategyFromAudioAttributes(const AudioAttributes& aa,
+ product_strategy_t& productStrategy,
+ bool fallbackOnDefault) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->getProductStrategyFromAudioAttributes(aa,productStrategy);
+
+ media::AudioAttributesEx aaAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_AudioAttributes_AudioAttributesEx(aa));
+ int32_t productStrategyAidl;
+
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->getProductStrategyFromAudioAttributes(aaAidl, fallbackOnDefault,
+ &productStrategyAidl)));
+ productStrategy = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_int32_t_product_strategy_t(productStrategyAidl));
+ return OK;
}
-status_t AudioSystem::listAudioVolumeGroups(AudioVolumeGroupVector &groups)
-{
+status_t AudioSystem::listAudioVolumeGroups(AudioVolumeGroupVector& groups) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->listAudioVolumeGroups(groups);
+
+ std::vector<media::AudioVolumeGroup> groupsAidl;
+ RETURN_STATUS_IF_ERROR(
+ statusTFromBinderStatus(aps->listAudioVolumeGroups(&groupsAidl)));
+ groups = VALUE_OR_RETURN_STATUS(
+ convertContainer<AudioVolumeGroupVector>(groupsAidl, aidl2legacy_AudioVolumeGroup));
+ return OK;
}
-status_t AudioSystem::getVolumeGroupFromAudioAttributes(const AudioAttributes &aa,
- volume_group_t &volumeGroup)
-{
+status_t AudioSystem::getVolumeGroupFromAudioAttributes(const AudioAttributes& aa,
+ volume_group_t& volumeGroup,
+ bool fallbackOnDefault) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->getVolumeGroupFromAudioAttributes(aa, volumeGroup);
+
+ media::AudioAttributesEx aaAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_AudioAttributes_AudioAttributesEx(aa));
+ int32_t volumeGroupAidl;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->getVolumeGroupFromAudioAttributes(aaAidl, fallbackOnDefault, &volumeGroupAidl)));
+ volumeGroup = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_volume_group_t(volumeGroupAidl));
+ return OK;
}
-status_t AudioSystem::setRttEnabled(bool enabled)
-{
+status_t AudioSystem::setRttEnabled(bool enabled) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->setRttEnabled(enabled);
+ return statusTFromBinderStatus(aps->setRttEnabled(enabled));
}
-bool AudioSystem::isCallScreenModeSupported()
-{
+bool AudioSystem::isCallScreenModeSupported() {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return false;
- return aps->isCallScreenModeSupported();
+
+ auto result = [&]() -> ConversionResult<bool> {
+ bool retAidl;
+ RETURN_IF_ERROR(
+ statusTFromBinderStatus(aps->isCallScreenModeSupported(&retAidl)));
+ return retAidl;
+ }();
+ return result.value_or(false);
}
status_t AudioSystem::setDevicesRoleForStrategy(product_strategy_t strategy,
device_role_t role,
- const AudioDeviceTypeAddrVector &devices)
-{
+ const AudioDeviceTypeAddrVector& devices) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) {
return PERMISSION_DENIED;
}
- return aps->setDevicesRoleForStrategy(strategy, role, devices);
+
+ int32_t strategyAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_product_strategy_t_int32_t(strategy));
+ media::DeviceRole roleAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_device_role_t_DeviceRole(role));
+ std::vector<media::AudioDevice> devicesAidl = VALUE_OR_RETURN_STATUS(
+ convertContainer<std::vector<media::AudioDevice>>(devices,
+ legacy2aidl_AudioDeviceTypeAddress));
+ return statusTFromBinderStatus(
+ aps->setDevicesRoleForStrategy(strategyAidl, roleAidl, devicesAidl));
}
-status_t AudioSystem::removeDevicesRoleForStrategy(product_strategy_t strategy, device_role_t role)
-{
+status_t
+AudioSystem::removeDevicesRoleForStrategy(product_strategy_t strategy, device_role_t role) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) {
return PERMISSION_DENIED;
}
- return aps->removeDevicesRoleForStrategy(strategy, role);
+ int32_t strategyAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_product_strategy_t_int32_t(strategy));
+ media::DeviceRole roleAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_device_role_t_DeviceRole(role));
+ return statusTFromBinderStatus(
+ aps->removeDevicesRoleForStrategy(strategyAidl, roleAidl));
}
status_t AudioSystem::getDevicesForRoleAndStrategy(product_strategy_t strategy,
device_role_t role,
- AudioDeviceTypeAddrVector &devices)
-{
+ AudioDeviceTypeAddrVector& devices) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) {
return PERMISSION_DENIED;
}
- return aps->getDevicesForRoleAndStrategy(strategy, role, devices);
+ int32_t strategyAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_product_strategy_t_int32_t(strategy));
+ media::DeviceRole roleAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_device_role_t_DeviceRole(role));
+ std::vector<media::AudioDevice> devicesAidl;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->getDevicesForRoleAndStrategy(strategyAidl, roleAidl, &devicesAidl)));
+ devices = VALUE_OR_RETURN_STATUS(
+ convertContainer<AudioDeviceTypeAddrVector>(devicesAidl,
+ aidl2legacy_AudioDeviceTypeAddress));
+ return OK;
}
status_t AudioSystem::setDevicesRoleForCapturePreset(audio_source_t audioSource,
device_role_t role,
- const AudioDeviceTypeAddrVector &devices)
-{
+ const AudioDeviceTypeAddrVector& devices) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) {
return PERMISSION_DENIED;
}
- return aps->setDevicesRoleForCapturePreset(audioSource, role, devices);
+
+ media::AudioSourceType audioSourceAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_source_t_AudioSourceType(audioSource));
+ media::DeviceRole roleAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_device_role_t_DeviceRole(role));
+ std::vector<media::AudioDevice> devicesAidl = VALUE_OR_RETURN_STATUS(
+ convertContainer<std::vector<media::AudioDevice>>(devices,
+ legacy2aidl_AudioDeviceTypeAddress));
+ return statusTFromBinderStatus(
+ aps->setDevicesRoleForCapturePreset(audioSourceAidl, roleAidl, devicesAidl));
}
status_t AudioSystem::addDevicesRoleForCapturePreset(audio_source_t audioSource,
device_role_t role,
- const AudioDeviceTypeAddrVector &devices)
-{
+ const AudioDeviceTypeAddrVector& devices) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) {
return PERMISSION_DENIED;
}
- return aps->addDevicesRoleForCapturePreset(audioSource, role, devices);
+ media::AudioSourceType audioSourceAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_source_t_AudioSourceType(audioSource));
+ media::DeviceRole roleAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_device_role_t_DeviceRole(role));
+ std::vector<media::AudioDevice> devicesAidl = VALUE_OR_RETURN_STATUS(
+ convertContainer<std::vector<media::AudioDevice>>(devices,
+ legacy2aidl_AudioDeviceTypeAddress));
+ return statusTFromBinderStatus(
+ aps->addDevicesRoleForCapturePreset(audioSourceAidl, roleAidl, devicesAidl));
}
status_t AudioSystem::removeDevicesRoleForCapturePreset(
- audio_source_t audioSource, device_role_t role, const AudioDeviceTypeAddrVector& devices)
-{
+ audio_source_t audioSource, device_role_t role, const AudioDeviceTypeAddrVector& devices) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) {
return PERMISSION_DENIED;
}
- return aps->removeDevicesRoleForCapturePreset(audioSource, role, devices);
+ media::AudioSourceType audioSourceAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_source_t_AudioSourceType(audioSource));
+ media::DeviceRole roleAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_device_role_t_DeviceRole(role));
+ std::vector<media::AudioDevice> devicesAidl = VALUE_OR_RETURN_STATUS(
+ convertContainer<std::vector<media::AudioDevice>>(devices,
+ legacy2aidl_AudioDeviceTypeAddress));
+ return statusTFromBinderStatus(
+ aps->removeDevicesRoleForCapturePreset(audioSourceAidl, roleAidl, devicesAidl));
}
status_t AudioSystem::clearDevicesRoleForCapturePreset(audio_source_t audioSource,
- device_role_t role)
-{
+ device_role_t role) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) {
return PERMISSION_DENIED;
}
- return aps->clearDevicesRoleForCapturePreset(audioSource, role);
+ media::AudioSourceType audioSourceAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_source_t_AudioSourceType(audioSource));
+ media::DeviceRole roleAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_device_role_t_DeviceRole(role));
+ return statusTFromBinderStatus(
+ aps->clearDevicesRoleForCapturePreset(audioSourceAidl, roleAidl));
}
status_t AudioSystem::getDevicesForRoleAndCapturePreset(audio_source_t audioSource,
device_role_t role,
- AudioDeviceTypeAddrVector &devices)
-{
+ AudioDeviceTypeAddrVector& devices) {
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) {
return PERMISSION_DENIED;
}
- return aps->getDevicesForRoleAndCapturePreset(audioSource, role, devices);
+ media::AudioSourceType audioSourceAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_source_t_AudioSourceType(audioSource));
+ media::DeviceRole roleAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_device_role_t_DeviceRole(role));
+ std::vector<media::AudioDevice> devicesAidl;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->getDevicesForRoleAndCapturePreset(audioSourceAidl, roleAidl, &devicesAidl)));
+ devices = VALUE_OR_RETURN_STATUS(
+ convertContainer<AudioDeviceTypeAddrVector>(devicesAidl,
+ aidl2legacy_AudioDeviceTypeAddress));
+ return OK;
}
class CaptureStateListenerImpl : public media::BnCaptureStateListener,
@@ -1698,7 +2213,8 @@
void init() {
bool active;
- status_t status = mAps->registerSoundTriggerCaptureStateListener(this, &active);
+ status_t status = statusTFromBinderStatus(
+ mAps->registerSoundTriggerCaptureStateListener(this, &active));
if (status != NO_ERROR) {
mListener->onServiceDied();
return;
@@ -1726,7 +2242,7 @@
};
status_t AudioSystem::registerSoundTriggerCaptureStateListener(
- const sp<CaptureStateListener>& listener) {
+ const sp<CaptureStateListener>& listener) {
LOG_ALWAYS_FATAL_IF(listener == nullptr);
const sp<IAudioPolicyService>& aps =
@@ -1742,11 +2258,19 @@
return NO_ERROR;
}
+status_t AudioSystem::setVibratorInfos(
+ const std::vector<media::AudioVibratorInfo>& vibratorInfos) {
+ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+ if (af == nullptr) {
+ return PERMISSION_DENIED;
+ }
+ return af->setVibratorInfos(vibratorInfos);
+}
+
// ---------------------------------------------------------------------------
int AudioSystem::AudioPolicyServiceClient::addAudioPortCallback(
- const sp<AudioPortCallback>& callback)
-{
+ const sp<AudioPortCallback>& callback) {
Mutex::Autolock _l(mLock);
for (size_t i = 0; i < mAudioPortCallbacks.size(); i++) {
if (mAudioPortCallbacks[i] == callback) {
@@ -1758,8 +2282,7 @@
}
int AudioSystem::AudioPolicyServiceClient::removeAudioPortCallback(
- const sp<AudioPortCallback>& callback)
-{
+ const sp<AudioPortCallback>& callback) {
Mutex::Autolock _l(mLock);
size_t i;
for (i = 0; i < mAudioPortCallbacks.size(); i++) {
@@ -1775,26 +2298,25 @@
}
-void AudioSystem::AudioPolicyServiceClient::onAudioPortListUpdate()
-{
+Status AudioSystem::AudioPolicyServiceClient::onAudioPortListUpdate() {
Mutex::Autolock _l(mLock);
for (size_t i = 0; i < mAudioPortCallbacks.size(); i++) {
mAudioPortCallbacks[i]->onAudioPortListUpdate();
}
+ return Status::ok();
}
-void AudioSystem::AudioPolicyServiceClient::onAudioPatchListUpdate()
-{
+Status AudioSystem::AudioPolicyServiceClient::onAudioPatchListUpdate() {
Mutex::Autolock _l(mLock);
for (size_t i = 0; i < mAudioPortCallbacks.size(); i++) {
mAudioPortCallbacks[i]->onAudioPatchListUpdate();
}
+ return Status::ok();
}
// ----------------------------------------------------------------------------
int AudioSystem::AudioPolicyServiceClient::addAudioVolumeGroupCallback(
- const sp<AudioVolumeGroupCallback>& callback)
-{
+ const sp<AudioVolumeGroupCallback>& callback) {
Mutex::Autolock _l(mLock);
for (size_t i = 0; i < mAudioVolumeGroupCallback.size(); i++) {
if (mAudioVolumeGroupCallback[i] == callback) {
@@ -1806,8 +2328,7 @@
}
int AudioSystem::AudioPolicyServiceClient::removeAudioVolumeGroupCallback(
- const sp<AudioVolumeGroupCallback>& callback)
-{
+ const sp<AudioVolumeGroupCallback>& callback) {
Mutex::Autolock _l(mLock);
size_t i;
for (i = 0; i < mAudioVolumeGroupCallback.size(); i++) {
@@ -1822,20 +2343,26 @@
return mAudioVolumeGroupCallback.size();
}
-void AudioSystem::AudioPolicyServiceClient::onAudioVolumeGroupChanged(volume_group_t group,
- int flags)
-{
+Status AudioSystem::AudioPolicyServiceClient::onAudioVolumeGroupChanged(int32_t group,
+ int32_t flags) {
+ volume_group_t groupLegacy = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_volume_group_t(group));
+ int flagsLegacy = VALUE_OR_RETURN_BINDER_STATUS(convertReinterpret<int>(flags));
+
Mutex::Autolock _l(mLock);
for (size_t i = 0; i < mAudioVolumeGroupCallback.size(); i++) {
- mAudioVolumeGroupCallback[i]->onAudioVolumeGroupChanged(group, flags);
+ mAudioVolumeGroupCallback[i]->onAudioVolumeGroupChanged(groupLegacy, flagsLegacy);
}
+ return Status::ok();
}
// ----------------------------------------------------------------------------
-void AudioSystem::AudioPolicyServiceClient::onDynamicPolicyMixStateUpdate(
- String8 regId, int32_t state)
-{
- ALOGV("AudioPolicyServiceClient::onDynamicPolicyMixStateUpdate(%s, %d)", regId.string(), state);
+Status AudioSystem::AudioPolicyServiceClient::onDynamicPolicyMixStateUpdate(
+ const ::std::string& regId, int32_t state) {
+ ALOGV("AudioPolicyServiceClient::onDynamicPolicyMixStateUpdate(%s, %d)", regId.c_str(), state);
+
+ String8 regIdLegacy = VALUE_OR_RETURN_BINDER_STATUS(aidl2legacy_string_view_String8(regId));
+ int stateLegacy = VALUE_OR_RETURN_BINDER_STATUS(convertReinterpret<int>(state));
dynamic_policy_callback cb = NULL;
{
Mutex::Autolock _l(AudioSystem::gLock);
@@ -1843,19 +2370,20 @@
}
if (cb != NULL) {
- cb(DYNAMIC_POLICY_EVENT_MIX_STATE_UPDATE, regId, state);
+ cb(DYNAMIC_POLICY_EVENT_MIX_STATE_UPDATE, regIdLegacy, stateLegacy);
}
+ return Status::ok();
}
-void AudioSystem::AudioPolicyServiceClient::onRecordingConfigurationUpdate(
- int event,
- const record_client_info_t *clientInfo,
- const audio_config_base_t *clientConfig,
- std::vector<effect_descriptor_t> clientEffects,
- const audio_config_base_t *deviceConfig,
- std::vector<effect_descriptor_t> effects,
- audio_patch_handle_t patchHandle,
- audio_source_t source) {
+Status AudioSystem::AudioPolicyServiceClient::onRecordingConfigurationUpdate(
+ int32_t event,
+ const media::RecordClientInfo& clientInfo,
+ const media::AudioConfigBase& clientConfig,
+ const std::vector<media::EffectDescriptor>& clientEffects,
+ const media::AudioConfigBase& deviceConfig,
+ const std::vector<media::EffectDescriptor>& effects,
+ int32_t patchHandle,
+ media::AudioSourceType source) {
record_config_callback cb = NULL;
{
Mutex::Autolock _l(AudioSystem::gLock);
@@ -1863,13 +2391,45 @@
}
if (cb != NULL) {
- cb(event, clientInfo, clientConfig, clientEffects,
- deviceConfig, effects, patchHandle, source);
+ int eventLegacy = VALUE_OR_RETURN_BINDER_STATUS(convertReinterpret<int>(event));
+ record_client_info_t clientInfoLegacy = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_RecordClientInfo_record_client_info_t(clientInfo));
+ audio_config_base_t clientConfigLegacy = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioConfigBase_audio_config_base_t(clientConfig));
+ std::vector<effect_descriptor_t> clientEffectsLegacy = VALUE_OR_RETURN_BINDER_STATUS(
+ convertContainer<std::vector<effect_descriptor_t>>(
+ clientEffects,
+ aidl2legacy_EffectDescriptor_effect_descriptor_t));
+ audio_config_base_t deviceConfigLegacy = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioConfigBase_audio_config_base_t(deviceConfig));
+ std::vector<effect_descriptor_t> effectsLegacy = VALUE_OR_RETURN_BINDER_STATUS(
+ convertContainer<std::vector<effect_descriptor_t>>(
+ effects,
+ aidl2legacy_EffectDescriptor_effect_descriptor_t));
+ audio_patch_handle_t patchHandleLegacy = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_patch_handle_t(patchHandle));
+ audio_source_t sourceLegacy = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioSourceType_audio_source_t(source));
+ cb(eventLegacy, &clientInfoLegacy, &clientConfigLegacy, clientEffectsLegacy,
+ &deviceConfigLegacy, effectsLegacy, patchHandleLegacy, sourceLegacy);
}
+ return Status::ok();
}
-void AudioSystem::AudioPolicyServiceClient::binderDied(const wp<IBinder>& who __unused)
-{
+Status AudioSystem::AudioPolicyServiceClient::onRoutingUpdated() {
+ routing_callback cb = NULL;
+ {
+ Mutex::Autolock _l(AudioSystem::gLock);
+ cb = gRoutingCallback;
+ }
+
+ if (cb != NULL) {
+ cb();
+ }
+ return Status::ok();
+}
+
+void AudioSystem::AudioPolicyServiceClient::binderDied(const wp<IBinder>& who __unused) {
{
Mutex::Autolock _l(mLock);
for (size_t i = 0; i < mAudioPortCallbacks.size(); i++) {
@@ -1887,4 +2447,28 @@
ALOGW("AudioPolicyService server died!");
}
+ConversionResult<record_client_info_t>
+aidl2legacy_RecordClientInfo_record_client_info_t(const media::RecordClientInfo& aidl) {
+ record_client_info_t legacy;
+ legacy.riid = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_unique_id_t(aidl.riid));
+ legacy.uid = VALUE_OR_RETURN(aidl2legacy_int32_t_uid_t(aidl.uid));
+ legacy.session = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_session_t(aidl.session));
+ legacy.source = VALUE_OR_RETURN(aidl2legacy_AudioSourceType_audio_source_t(aidl.source));
+ legacy.port_id = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_port_handle_t(aidl.portId));
+ legacy.silenced = aidl.silenced;
+ return legacy;
+}
+
+ConversionResult<media::RecordClientInfo>
+legacy2aidl_record_client_info_t_RecordClientInfo(const record_client_info_t& legacy) {
+ media::RecordClientInfo aidl;
+ aidl.riid = VALUE_OR_RETURN(legacy2aidl_audio_unique_id_t_int32_t(legacy.riid));
+ aidl.uid = VALUE_OR_RETURN(legacy2aidl_uid_t_int32_t(legacy.uid));
+ aidl.session = VALUE_OR_RETURN(legacy2aidl_audio_session_t_int32_t(legacy.session));
+ aidl.source = VALUE_OR_RETURN(legacy2aidl_audio_source_t_AudioSourceType(legacy.source));
+ aidl.portId = VALUE_OR_RETURN(legacy2aidl_audio_port_handle_t_int32_t(legacy.port_id));
+ aidl.silenced = legacy.silenced;
+ return aidl;
+}
+
} // namespace android
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index e37cc12..1bc3baa 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -22,6 +22,7 @@
#include <math.h>
#include <sys/resource.h>
+#include <android/media/IAudioPolicyService.h>
#include <android-base/macros.h>
#include <audio_utils/clock.h>
#include <audio_utils/primitives.h>
@@ -31,7 +32,6 @@
#include <private/media/AudioTrackShared.h>
#include <processgroup/sched_policy.h>
#include <media/IAudioFlinger.h>
-#include <media/IAudioPolicyService.h>
#include <media/AudioParameter.h>
#include <media/AudioResamplerPublic.h>
#include <media/AudioSystem.h>
@@ -42,10 +42,13 @@
#define WAIT_STREAM_END_TIMEOUT_SEC 120
static const int kMaxLoopCountNotifications = 32;
+using ::android::aidl_utils::statusTFromBinderStatus;
+
namespace android {
// ---------------------------------------------------------------------------
using media::VolumeShaper;
+using media::permission::Identity;
// TODO: Move to a separate .h
@@ -163,9 +166,20 @@
bool AudioTrack::isDirectOutputSupported(const audio_config_base_t& config,
const audio_attributes_t& attributes) {
ALOGV("%s()", __FUNCTION__);
- const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ const sp<media::IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return false;
- return aps->isDirectOutputSupported(config, attributes);
+
+ auto result = [&]() -> ConversionResult<bool> {
+ media::AudioConfigBase configAidl = VALUE_OR_RETURN(
+ legacy2aidl_audio_config_base_t_AudioConfigBase(config));
+ media::AudioAttributesInternal attributesAidl = VALUE_OR_RETURN(
+ legacy2aidl_audio_attributes_t_AudioAttributesInternal(attributes));
+ bool retAidl;
+ RETURN_IF_ERROR(aidl_utils::statusTFromBinderStatus(
+ aps->isDirectOutputSupported(configAidl, attributesAidl, &retAidl)));
+ return retAidl;
+ }();
+ return result.value_or(false);
}
// ---------------------------------------------------------------------------
@@ -196,6 +210,7 @@
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());
+ mMetricsItem->setCString(MM_PREFIX "logSessionId", track->mLogSessionId.c_str());
}
// hand the user a snapshot of the metrics.
@@ -210,11 +225,11 @@
return NO_ERROR;
}
-AudioTrack::AudioTrack() : AudioTrack("" /*opPackageName*/)
+AudioTrack::AudioTrack() : AudioTrack(Identity())
{
}
-AudioTrack::AudioTrack(const std::string& opPackageName)
+AudioTrack::AudioTrack(const Identity& identity)
: mStatus(NO_INIT),
mState(STATE_STOPPED),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
@@ -222,7 +237,7 @@
mPausedPosition(0),
mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE),
- mOpPackageName(opPackageName),
+ mClientIdentity(identity),
mAudioTrackCallback(new AudioTrackCallback())
{
mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
@@ -244,19 +259,16 @@
audio_session_t sessionId,
transfer_type transferType,
const audio_offload_info_t *offloadInfo,
- uid_t uid,
- pid_t pid,
+ const Identity& identity,
const audio_attributes_t* pAttributes,
bool doNotReconnect,
float maxRequiredSpeed,
- audio_port_handle_t selectedDeviceId,
- const std::string& opPackageName)
+ audio_port_handle_t selectedDeviceId)
: mStatus(NO_INIT),
mState(STATE_STOPPED),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
mPreviousSchedulingGroup(SP_DEFAULT),
mPausedPosition(0),
- mOpPackageName(opPackageName),
mAudioTrackCallback(new AudioTrackCallback())
{
mAttributes = AUDIO_ATTRIBUTES_INITIALIZER;
@@ -264,7 +276,7 @@
(void)set(streamType, sampleRate, format, channelMask,
frameCount, flags, cbf, user, notificationFrames,
0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId, transferType,
- offloadInfo, uid, pid, pAttributes, doNotReconnect, maxRequiredSpeed, selectedDeviceId);
+ offloadInfo, identity, pAttributes, doNotReconnect, maxRequiredSpeed, selectedDeviceId);
}
AudioTrack::AudioTrack(
@@ -280,19 +292,16 @@
audio_session_t sessionId,
transfer_type transferType,
const audio_offload_info_t *offloadInfo,
- uid_t uid,
- pid_t pid,
+ const Identity& identity,
const audio_attributes_t* pAttributes,
bool doNotReconnect,
- float maxRequiredSpeed,
- const std::string& opPackageName)
+ float maxRequiredSpeed)
: mStatus(NO_INIT),
mState(STATE_STOPPED),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
mPreviousSchedulingGroup(SP_DEFAULT),
mPausedPosition(0),
mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
- mOpPackageName(opPackageName),
mAudioTrackCallback(new AudioTrackCallback())
{
mAttributes = AUDIO_ATTRIBUTES_INITIALIZER;
@@ -300,7 +309,7 @@
(void)set(streamType, sampleRate, format, channelMask,
0 /*frameCount*/, flags, cbf, user, notificationFrames,
sharedBuffer, false /*threadCanCallJava*/, sessionId, transferType, offloadInfo,
- uid, pid, pAttributes, doNotReconnect, maxRequiredSpeed);
+ identity, pAttributes, doNotReconnect, maxRequiredSpeed);
}
AudioTrack::~AudioTrack()
@@ -318,30 +327,42 @@
.set(AMEDIAMETRICS_PROP_STATUS, (int32_t)mStatus)
.record();
+ stopAndJoinCallbacks(); // checks mStatus
+
if (mStatus == NO_ERROR) {
- // Make sure that callback function exits in the case where
- // it is looping on buffer full condition in obtainBuffer().
- // Otherwise the callback thread will never exit.
- stop();
- if (mAudioTrackThread != 0) {
- mProxy->interrupt();
- mAudioTrackThread->requestExit(); // see comment in AudioTrack.h
- mAudioTrackThread->requestExitAndWait();
- mAudioTrackThread.clear();
- }
- // No lock here: worst case we remove a NULL callback which will be a nop
- if (mDeviceCallback != 0 && mOutput != AUDIO_IO_HANDLE_NONE) {
- AudioSystem::removeAudioDeviceCallback(this, mOutput, mPortId);
- }
IInterface::asBinder(mAudioTrack)->unlinkToDeath(mDeathNotifier, this);
mAudioTrack.clear();
mCblkMemory.clear();
mSharedBuffer.clear();
IPCThreadState::self()->flushCommands();
+ pid_t clientPid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mClientIdentity.pid));
ALOGV("%s(%d), releasing session id %d from %d on behalf of %d",
__func__, mPortId,
- mSessionId, IPCThreadState::self()->getCallingPid(), mClientPid);
- AudioSystem::releaseAudioSessionId(mSessionId, mClientPid);
+ mSessionId, IPCThreadState::self()->getCallingPid(), clientPid);
+ AudioSystem::releaseAudioSessionId(mSessionId, clientPid);
+ }
+}
+
+void AudioTrack::stopAndJoinCallbacks() {
+ // Prevent nullptr crash if it did not open properly.
+ if (mStatus != NO_ERROR) return;
+
+ // Make sure that callback function exits in the case where
+ // it is looping on buffer full condition in obtainBuffer().
+ // Otherwise the callback thread will never exit.
+ stop();
+ if (mAudioTrackThread != 0) { // not thread safe
+ mProxy->interrupt();
+ mAudioTrackThread->requestExit(); // see comment in AudioTrack.h
+ mAudioTrackThread->requestExitAndWait();
+ mAudioTrackThread.clear();
+ }
+ // No lock here: worst case we remove a NULL callback which will be a nop
+ if (mDeviceCallback != 0 && mOutput != AUDIO_IO_HANDLE_NONE) {
+ // This may not stop all of these device callbacks!
+ // TODO: Add some sort of protection.
+ AudioSystem::removeAudioDeviceCallback(this, mOutput, mPortId);
+ mDeviceCallback.clear();
}
}
@@ -360,8 +381,7 @@
audio_session_t sessionId,
transfer_type transferType,
const audio_offload_info_t *offloadInfo,
- uid_t uid,
- pid_t pid,
+ const Identity& identity,
const audio_attributes_t* pAttributes,
bool doNotReconnect,
float maxRequiredSpeed,
@@ -371,13 +391,15 @@
uint32_t channelCount;
pid_t callingPid;
pid_t myPid;
+ uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
+ pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(identity.pid));
// Note mPortId is not valid until the track is created, so omit mPortId in ALOG for set.
ALOGV("%s(): streamType %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
"flags #%x, notificationFrames %d, sessionId %d, transferType %d, uid %d, pid %d",
__func__,
streamType, sampleRate, format, channelMask, frameCount, flags, notificationFrames,
- sessionId, transferType, uid, pid);
+ sessionId, transferType, identity.uid, identity.pid);
mThreadCanCallJava = threadCanCallJava;
mSelectedDeviceId = selectedDeviceId;
@@ -538,6 +560,7 @@
} else {
mOffloadInfo = NULL;
memset(&mOffloadInfoCopy, 0, sizeof(audio_offload_info_t));
+ mOffloadInfoCopy = AUDIO_INFO_INITIALIZER;
}
mVolume[AUDIO_INTERLEAVE_LEFT] = 1.0f;
@@ -572,17 +595,19 @@
notificationFrames, minNotificationsPerBuffer, maxNotificationsPerBuffer);
}
mNotificationFramesAct = 0;
+ // TODO b/182392553: refactor or remove
callingPid = IPCThreadState::self()->getCallingPid();
myPid = getpid();
- if (uid == AUDIO_UID_INVALID || (callingPid != myPid)) {
- mClientUid = IPCThreadState::self()->getCallingUid();
+ if (uid == -1 || (callingPid != myPid)) {
+ mClientIdentity.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(
+ IPCThreadState::self()->getCallingUid()));
} else {
- mClientUid = uid;
+ mClientIdentity.uid = identity.uid;
}
- if (pid == -1 || (callingPid != myPid)) {
- mClientPid = callingPid;
+ if (pid == (pid_t)-1 || (callingPid != myPid)) {
+ mClientIdentity.pid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(callingPid));
} else {
- mClientPid = pid;
+ mClientIdentity.pid = identity.pid;
}
mAuxEffectId = 0;
mOrigFlags = mFlags = flags;
@@ -621,7 +646,7 @@
mReleased = 0;
mStartNs = 0;
mStartFromZeroUs = 0;
- AudioSystem::acquireAudioSessionId(mSessionId, mClientPid, mClientUid);
+ AudioSystem::acquireAudioSessionId(mSessionId, pid, uid);
mSequence = 1;
mObservedSequence = mSequence;
mInUnderrun = false;
@@ -667,10 +692,13 @@
float maxRequiredSpeed,
audio_port_handle_t selectedDeviceId)
{
+ Identity identity;
+ identity.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(uid));
+ identity.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(pid));
return set(streamType, sampleRate, format,
static_cast<audio_channel_mask_t>(channelMask),
frameCount, flags, cbf, user, notificationFrames, sharedBuffer,
- threadCanCallJava, sessionId, transferType, offloadInfo, uid, pid,
+ threadCanCallJava, sessionId, transferType, offloadInfo, identity,
pAttributes, doNotReconnect, maxRequiredSpeed, selectedDeviceId);
}
@@ -774,7 +802,7 @@
int32_t flags = android_atomic_and(~(CBLK_STREAM_END_DONE | CBLK_DISABLED), &mCblk->mFlags);
if (!(flags & CBLK_INVALID)) {
- status = mAudioTrack->start();
+ mAudioTrack->start(&status);
if (status == DEAD_OBJECT) {
flags |= CBLK_INVALID;
}
@@ -1083,13 +1111,23 @@
status_t AudioTrack::setDualMonoMode_l(audio_dual_mono_mode_t mode)
{
- return mAudioTrack->setDualMonoMode(mode);
+ const status_t status = statusTFromBinderStatus(
+ mAudioTrack->setDualMonoMode(VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_dual_mono_mode_t_AudioDualMonoMode(mode))));
+ if (status == NO_ERROR) mDualMonoMode = mode;
+ return status;
}
status_t AudioTrack::getDualMonoMode(audio_dual_mono_mode_t* mode) const
{
AutoMutex lock(mLock);
- return mAudioTrack->getDualMonoMode(mode);
+ media::AudioDualMonoMode mediaMode;
+ const status_t status = statusTFromBinderStatus(mAudioTrack->getDualMonoMode(&mediaMode));
+ if (status == NO_ERROR) {
+ *mode = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_AudioDualMonoMode_audio_dual_mono_mode_t(mediaMode));
+ }
+ return status;
}
status_t AudioTrack::setAudioDescriptionMixLevel(float leveldB)
@@ -1100,13 +1138,16 @@
status_t AudioTrack::setAudioDescriptionMixLevel_l(float leveldB)
{
- return mAudioTrack->setAudioDescriptionMixLevel(leveldB);
+ const status_t status = statusTFromBinderStatus(
+ mAudioTrack->setAudioDescriptionMixLevel(leveldB));
+ if (status == NO_ERROR) mAudioDescriptionMixLeveldB = leveldB;
+ return status;
}
status_t AudioTrack::getAudioDescriptionMixLevel(float* leveldB) const
{
AutoMutex lock(mLock);
- return mAudioTrack->getAudioDescriptionMixLevel(leveldB);
+ return statusTFromBinderStatus(mAudioTrack->getAudioDescriptionMixLevel(leveldB));
}
status_t AudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate)
@@ -1116,7 +1157,9 @@
return NO_ERROR;
}
if (isOffloadedOrDirect_l()) {
- status_t status = mAudioTrack->setPlaybackRateParameters(playbackRate);
+ const status_t status = statusTFromBinderStatus(mAudioTrack->setPlaybackRateParameters(
+ VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_playback_rate_t_AudioPlaybackRate(playbackRate))));
if (status == NO_ERROR) {
mPlaybackRate = playbackRate;
}
@@ -1189,10 +1232,12 @@
{
AutoMutex lock(mLock);
if (isOffloadedOrDirect_l()) {
- audio_playback_rate_t playbackRateTemp;
- const status_t status = mAudioTrack->getPlaybackRateParameters(&playbackRateTemp);
+ media::AudioPlaybackRate playbackRateTemp;
+ const status_t status = statusTFromBinderStatus(
+ mAudioTrack->getPlaybackRateParameters(&playbackRateTemp));
if (status == NO_ERROR) { // update local version if changed.
- mPlaybackRate = playbackRateTemp;
+ mPlaybackRate =
+ aidl2legacy_AudioPlaybackRate_audio_playback_rate_t(playbackRateTemp).value();
}
}
return mPlaybackRate;
@@ -1552,7 +1597,8 @@
status_t AudioTrack::attachAuxEffect(int effectId)
{
AutoMutex lock(mLock);
- status_t status = mAudioTrack->attachAuxEffect(effectId);
+ status_t status;
+ mAudioTrack->attachAuxEffect(effectId, &status);
if (status == NO_ERROR) {
mAuxEffectId = effectId;
}
@@ -1654,8 +1700,7 @@
input.config.channel_mask = mChannelMask;
input.config.format = mFormat;
input.config.offload_info = mOffloadInfoCopy;
- input.clientInfo.clientUid = mClientUid;
- input.clientInfo.clientPid = mClientPid;
+ input.clientInfo.identity = mClientIdentity;
input.clientInfo.clientTid = -1;
if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
// It is currently meaningless to request SCHED_FIFO for a Java thread. Even if the
@@ -1679,13 +1724,14 @@
input.selectedDeviceId = mSelectedDeviceId;
input.sessionId = mSessionId;
input.audioTrackCallback = mAudioTrackCallback;
- input.opPackageName = mOpPackageName;
- IAudioFlinger::CreateTrackOutput output;
+ media::CreateTrackResponse response;
+ status = audioFlinger->createTrack(VALUE_OR_FATAL(input.toAidl()), response);
- sp<IAudioTrack> track = audioFlinger->createTrack(input,
- output,
- &status);
+ IAudioFlinger::CreateTrackOutput output{};
+ if (status == NO_ERROR) {
+ output = VALUE_OR_FATAL(IAudioFlinger::CreateTrackOutput::fromAidl(response));
+ }
if (status != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) {
ALOGE("%s(%d): AudioFlinger could not create track, status: %d output %d",
@@ -1695,7 +1741,7 @@
}
goto exit;
}
- ALOG_ASSERT(track != 0);
+ ALOG_ASSERT(output.audioTrack != 0);
mFrameCount = output.frameCount;
mNotificationFramesAct = (uint32_t)output.notificationFrameCount;
@@ -1717,7 +1763,9 @@
// so we are no longer responsible for releasing it.
// FIXME compare to AudioRecord
- sp<IMemory> iMem = track->getCblk();
+ std::optional<media::SharedFileRegion> sfr;
+ output.audioTrack->getCblk(&sfr);
+ sp<IMemory> iMem = VALUE_OR_FATAL(aidl2legacy_NullableSharedFileRegion_IMemory(sfr));
if (iMem == 0) {
ALOGE("%s(%d): Could not get control block", __func__, mPortId);
status = NO_INIT;
@@ -1738,7 +1786,7 @@
IInterface::asBinder(mAudioTrack)->unlinkToDeath(mDeathNotifier, this);
mDeathNotifier.clear();
}
- mAudioTrack = track;
+ mAudioTrack = output.audioTrack;
mCblkMemory = iMem;
IPCThreadState::self()->flushCommands();
@@ -1794,7 +1842,7 @@
}
}
- mAudioTrack->attachAuxEffect(mAuxEffectId);
+ mAudioTrack->attachAuxEffect(mAuxEffectId, &status);
// If IAudioTrack is re-created, don't let the requested frameCount
// decrease. This can confuse clients that cache frameCount().
@@ -1852,6 +1900,8 @@
.set(AMEDIAMETRICS_PROP_FLAGS, toString(mFlags).c_str())
.set(AMEDIAMETRICS_PROP_ORIGINALFLAGS, toString(mOrigFlags).c_str())
.set(AMEDIAMETRICS_PROP_SESSIONID, (int32_t)mSessionId)
+ .set(AMEDIAMETRICS_PROP_LOGSESSIONID, mLogSessionId)
+ .set(AMEDIAMETRICS_PROP_PLAYERIID, mPlayerIId)
.set(AMEDIAMETRICS_PROP_TRACKID, mPortId) // dup from key
.set(AMEDIAMETRICS_PROP_CONTENTTYPE, toString(mAttributes.content_type).c_str())
.set(AMEDIAMETRICS_PROP_USAGE, toString(mAttributes.usage).c_str())
@@ -2045,7 +2095,8 @@
ALOGW("%s(%d): releaseBuffer() track %p disabled due to previous underrun, restarting",
__func__, mPortId, this);
// FIXME ignoring status
- mAudioTrack->start();
+ status_t status;
+ mAudioTrack->start(&status);
}
}
@@ -2657,7 +2708,13 @@
if (shaper.isStarted()) {
operationToEnd->setNormalizedTime(1.f);
}
- return mAudioTrack->applyVolumeShaper(shaper.mConfiguration, operationToEnd);
+ media::VolumeShaperConfiguration config;
+ shaper.mConfiguration->writeToParcelable(&config);
+ media::VolumeShaperOperation operation;
+ operationToEnd->writeToParcelable(&operation);
+ status_t status;
+ mAudioTrack->applyVolumeShaper(config, operation, &status);
+ return status;
});
// restore the original start threshold if different than frameCount.
@@ -2673,7 +2730,7 @@
__func__, mPortId, originalStartThresholdInFrames, currentThreshold);
}
if (mState == STATE_ACTIVE) {
- result = mAudioTrack->start();
+ mAudioTrack->start(&result);
}
// server resets to zero so we offset
mFramesWrittenServerOffset =
@@ -2743,7 +2800,9 @@
status_t AudioTrack::setParameters(const String8& keyValuePairs)
{
AutoMutex lock(mLock);
- return mAudioTrack->setParameters(keyValuePairs);
+ status_t status;
+ mAudioTrack->setParameters(keyValuePairs.c_str(), &status);
+ return status;
}
status_t AudioTrack::selectPresentation(int presentationId, int programId)
@@ -2755,7 +2814,9 @@
ALOGV("%s(%d): PresentationId/ProgramId[%s]",
__func__, mPortId, param.toString().string());
- return mAudioTrack->setParameters(param.toString());
+ status_t status;
+ mAudioTrack->setParameters(param.toString().c_str(), &status);
+ return status;
}
VolumeShaper::Status AudioTrack::applyVolumeShaper(
@@ -2764,11 +2825,16 @@
{
AutoMutex lock(mLock);
mVolumeHandler->setIdIfNecessary(configuration);
- VolumeShaper::Status status = mAudioTrack->applyVolumeShaper(configuration, operation);
+ media::VolumeShaperConfiguration config;
+ configuration->writeToParcelable(&config);
+ media::VolumeShaperOperation op;
+ operation->writeToParcelable(&op);
+ VolumeShaper::Status status;
+ mAudioTrack->applyVolumeShaper(config, op, &status);
if (status == DEAD_OBJECT) {
if (restoreTrack_l("applyVolumeShaper") == OK) {
- status = mAudioTrack->applyVolumeShaper(configuration, operation);
+ mAudioTrack->applyVolumeShaper(config, op, &status);
}
}
if (status >= 0) {
@@ -2788,10 +2854,20 @@
sp<VolumeShaper::State> AudioTrack::getVolumeShaperState(int id)
{
AutoMutex lock(mLock);
- sp<VolumeShaper::State> state = mAudioTrack->getVolumeShaperState(id);
+ std::optional<media::VolumeShaperState> vss;
+ mAudioTrack->getVolumeShaperState(id, &vss);
+ sp<VolumeShaper::State> state;
+ if (vss.has_value()) {
+ state = new VolumeShaper::State();
+ state->readFromParcelable(vss.value());
+ }
if (state.get() == nullptr && (mCblk->mFlags & CBLK_INVALID) != 0) {
if (restoreTrack_l("getVolumeShaperState") == OK) {
- state = mAudioTrack->getVolumeShaperState(id);
+ mAudioTrack->getVolumeShaperState(id, &vss);
+ if (vss.has_value()) {
+ state = new VolumeShaper::State();
+ state->readFromParcelable(vss.value());
+ }
}
}
return state;
@@ -2885,7 +2961,11 @@
status_t status;
if (isOffloadedOrDirect_l()) {
// use Binder to get timestamp
- status = mAudioTrack->getTimestamp(timestamp);
+ media::AudioTimestampInternal ts;
+ mAudioTrack->getTimestamp(&ts, &status);
+ if (status == OK) {
+ timestamp = VALUE_OR_FATAL(aidl2legacy_AudioTimestampInternal_AudioTimestamp(ts));
+ }
} else {
// read timestamp from shared memory
ExtendedTimestamp ets;
@@ -3244,6 +3324,31 @@
return mProxy->getUnderrunFrames();
}
+void AudioTrack::setLogSessionId(const char *logSessionId)
+{
+ AutoMutex lock(mLock);
+ if (logSessionId == nullptr) logSessionId = ""; // an empty string is an unset session id.
+ if (mLogSessionId == logSessionId) return;
+
+ mLogSessionId = logSessionId;
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETLOGSESSIONID)
+ .set(AMEDIAMETRICS_PROP_LOGSESSIONID, logSessionId)
+ .record();
+}
+
+void AudioTrack::setPlayerIId(int playerIId)
+{
+ AutoMutex lock(mLock);
+ if (mPlayerIId == playerIId) return;
+
+ mPlayerIId = playerIId;
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETPLAYERIID)
+ .set(AMEDIAMETRICS_PROP_PLAYERIID, playerIId)
+ .record();
+}
+
status_t AudioTrack::addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback)
{
diff --git a/media/libaudioclient/AudioVolumeGroup.cpp b/media/libaudioclient/AudioVolumeGroup.cpp
index e79a362..361f7b8 100644
--- a/media/libaudioclient/AudioVolumeGroup.cpp
+++ b/media/libaudioclient/AudioVolumeGroup.cpp
@@ -21,64 +21,57 @@
#include <utils/Log.h>
#include <binder/Parcel.h>
+#include <media/AidlConversion.h>
#include <media/AudioVolumeGroup.h>
#include <media/AudioAttributes.h>
+#include <media/PolicyAidlConversion.h>
+
+#define RETURN_STATUS_IF_ERROR(x) \
+ { auto _tmp = (x); if (_tmp != OK) return _tmp; }
namespace android {
status_t AudioVolumeGroup::readFromParcel(const Parcel *parcel)
{
- status_t ret = parcel->readUtf8FromUtf16(&mName);
- if (ret != NO_ERROR) {
- return ret;
- }
- mGroupId = static_cast<volume_group_t>(parcel->readInt32());
- size_t size = static_cast<size_t>(parcel->readInt32());
- for (size_t i = 0; i < size; i++) {
- AudioAttributes attribute;
- attribute.readFromParcel(parcel);
- if (ret != NO_ERROR) {
- mAudioAttributes.clear();
- return ret;
- }
- mAudioAttributes.push_back(attribute.getAttributes());
- }
- size = static_cast<size_t>(parcel->readInt32());
- for (size_t i = 0; i < size; i++) {
- audio_stream_type_t stream = static_cast<audio_stream_type_t>(parcel->readInt32());
- mStreams.push_back(stream);
- }
- return NO_ERROR;
+ media::AudioVolumeGroup aidl;
+ RETURN_STATUS_IF_ERROR(aidl.readFromParcel(parcel));
+ *this = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioVolumeGroup(aidl));
+ return OK;
}
status_t AudioVolumeGroup::writeToParcel(Parcel *parcel) const
{
- parcel->writeUtf8AsUtf16(mName);
- parcel->writeInt32(static_cast<int32_t>(mGroupId));
- size_t size = mAudioAttributes.size();
- size_t sizePosition = parcel->dataPosition();
- parcel->writeInt32(size);
- size_t finalSize = size;
- for (const auto &attributes : mAudioAttributes) {
- size_t position = parcel->dataPosition();
- AudioAttributes attribute(attributes);
- status_t ret = attribute.writeToParcel(parcel);
- if (ret != NO_ERROR) {
- parcel->setDataPosition(position);
- finalSize--;
- }
- }
- if (size != finalSize) {
- size_t position = parcel->dataPosition();
- parcel->setDataPosition(sizePosition);
- parcel->writeInt32(finalSize);
- parcel->setDataPosition(position);
- }
- parcel->writeInt32(mStreams.size());
- for (const auto &stream : mStreams) {
- parcel->writeInt32(static_cast<int32_t>(stream));
- }
- return NO_ERROR;
+ media::AudioVolumeGroup aidl = VALUE_OR_RETURN_STATUS(legacy2aidl_AudioVolumeGroup(*this));
+ return aidl.writeToParcel(parcel);
+}
+
+ConversionResult<media::AudioVolumeGroup>
+legacy2aidl_AudioVolumeGroup(const AudioVolumeGroup& legacy) {
+ media::AudioVolumeGroup aidl;
+ aidl.groupId = VALUE_OR_RETURN(legacy2aidl_volume_group_t_int32_t(legacy.getId()));
+ aidl.name = legacy.getName();
+ aidl.audioAttributes = VALUE_OR_RETURN(
+ convertContainer<std::vector<media::AudioAttributesInternal>>(
+ legacy.getAudioAttributes(),
+ legacy2aidl_audio_attributes_t_AudioAttributesInternal));
+ aidl.streams = VALUE_OR_RETURN(
+ convertContainer<std::vector<media::AudioStreamType>>(legacy.getStreamTypes(),
+ legacy2aidl_audio_stream_type_t_AudioStreamType));
+ return aidl;
+}
+
+ConversionResult<AudioVolumeGroup>
+aidl2legacy_AudioVolumeGroup(const media::AudioVolumeGroup& aidl) {
+ return AudioVolumeGroup(
+ aidl.name,
+ VALUE_OR_RETURN(aidl2legacy_int32_t_volume_group_t(aidl.groupId)),
+ VALUE_OR_RETURN(convertContainer<AttributesVector>(
+ aidl.audioAttributes,
+ aidl2legacy_AudioAttributesInternal_audio_attributes_t)),
+ VALUE_OR_RETURN(convertContainer<StreamTypeVector>(
+ aidl.streams,
+ aidl2legacy_AudioStreamType_audio_stream_type_t))
+ );
}
} // namespace android
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 08f307d..389b73f 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -24,1608 +24,1179 @@
#include <binder/IPCThreadState.h>
#include <binder/Parcel.h>
-#include <media/AudioSanitizer.h>
-#include <mediautils/ServiceUtilities.h>
-#include <mediautils/TimeCheck.h>
#include "IAudioFlinger.h"
namespace android {
-enum {
- CREATE_TRACK = IBinder::FIRST_CALL_TRANSACTION,
- CREATE_RECORD,
- SAMPLE_RATE,
- RESERVED, // obsolete, was CHANNEL_COUNT
- FORMAT,
- FRAME_COUNT,
- LATENCY,
- SET_MASTER_VOLUME,
- SET_MASTER_MUTE,
- MASTER_VOLUME,
- MASTER_MUTE,
- SET_STREAM_VOLUME,
- SET_STREAM_MUTE,
- STREAM_VOLUME,
- STREAM_MUTE,
- SET_MODE,
- SET_MIC_MUTE,
- GET_MIC_MUTE,
- SET_RECORD_SILENCED,
- SET_PARAMETERS,
- GET_PARAMETERS,
- REGISTER_CLIENT,
- GET_INPUTBUFFERSIZE,
- OPEN_OUTPUT,
- OPEN_DUPLICATE_OUTPUT,
- CLOSE_OUTPUT,
- SUSPEND_OUTPUT,
- RESTORE_OUTPUT,
- OPEN_INPUT,
- CLOSE_INPUT,
- INVALIDATE_STREAM,
- SET_VOICE_VOLUME,
- GET_RENDER_POSITION,
- GET_INPUT_FRAMES_LOST,
- NEW_AUDIO_UNIQUE_ID,
- ACQUIRE_AUDIO_SESSION_ID,
- RELEASE_AUDIO_SESSION_ID,
- QUERY_NUM_EFFECTS,
- QUERY_EFFECT,
- GET_EFFECT_DESCRIPTOR,
- CREATE_EFFECT,
- MOVE_EFFECTS,
- LOAD_HW_MODULE,
- GET_PRIMARY_OUTPUT_SAMPLING_RATE,
- GET_PRIMARY_OUTPUT_FRAME_COUNT,
- SET_LOW_RAM_DEVICE,
- LIST_AUDIO_PORTS,
- GET_AUDIO_PORT,
- CREATE_AUDIO_PATCH,
- RELEASE_AUDIO_PATCH,
- LIST_AUDIO_PATCHES,
- SET_AUDIO_PORT_CONFIG,
- GET_AUDIO_HW_SYNC_FOR_SESSION,
- SYSTEM_READY,
- FRAME_COUNT_HAL,
- GET_MICROPHONES,
- SET_MASTER_BALANCE,
- GET_MASTER_BALANCE,
- SET_EFFECT_SUSPENDED,
- SET_AUDIO_HAL_PIDS
-};
+using aidl_utils::statusTFromBinderStatus;
+using binder::Status;
#define MAX_ITEMS_PER_LIST 1024
+#define VALUE_OR_RETURN_BINDER(x) \
+ ({ \
+ auto _tmp = (x); \
+ if (!_tmp.ok()) return Status::fromStatusT(_tmp.error()); \
+ std::move(_tmp.value()); \
+ })
-class BpAudioFlinger : public BpInterface<IAudioFlinger>
-{
-public:
- explicit BpAudioFlinger(const sp<IBinder>& impl)
- : BpInterface<IAudioFlinger>(impl)
- {
+#define RETURN_STATUS_IF_ERROR(x) \
+ { \
+ auto _tmp = (x); \
+ if (_tmp != OK) return _tmp; \
}
- virtual sp<IAudioTrack> createTrack(const CreateTrackInput& input,
- CreateTrackOutput& output,
- status_t *status)
- {
- Parcel data, reply;
- sp<IAudioTrack> track;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
-
- if (status == nullptr) {
- return track;
- }
-
- input.writeToParcel(&data);
-
- status_t lStatus = remote()->transact(CREATE_TRACK, data, &reply);
- if (lStatus != NO_ERROR) {
- ALOGE("createTrack transaction error %d", lStatus);
- *status = DEAD_OBJECT;
- return track;
- }
- *status = reply.readInt32();
- if (*status != NO_ERROR) {
- ALOGE("createTrack returned error %d", *status);
- return track;
- }
- track = interface_cast<IAudioTrack>(reply.readStrongBinder());
- if (track == 0) {
- ALOGE("createTrack returned an NULL IAudioTrack with status OK");
- *status = DEAD_OBJECT;
- return track;
- }
- output.readFromParcel(&reply);
- return track;
+#define RETURN_BINDER_IF_ERROR(x) \
+ { \
+ auto _tmp = (x); \
+ if (_tmp != OK) return Status::fromStatusT(_tmp); \
}
- virtual sp<media::IAudioRecord> createRecord(const CreateRecordInput& input,
- CreateRecordOutput& output,
- status_t *status)
- {
- Parcel data, reply;
- sp<media::IAudioRecord> record;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
-
- if (status == nullptr) {
- return record;
- }
-
- input.writeToParcel(&data);
-
- status_t lStatus = remote()->transact(CREATE_RECORD, data, &reply);
- if (lStatus != NO_ERROR) {
- ALOGE("createRecord transaction error %d", lStatus);
- *status = DEAD_OBJECT;
- return record;
- }
- *status = reply.readInt32();
- if (*status != NO_ERROR) {
- ALOGE("createRecord returned error %d", *status);
- return record;
- }
-
- record = interface_cast<media::IAudioRecord>(reply.readStrongBinder());
- if (record == 0) {
- ALOGE("createRecord returned a NULL IAudioRecord with status OK");
- *status = DEAD_OBJECT;
- return record;
- }
- output.readFromParcel(&reply);
- return record;
- }
-
- virtual uint32_t sampleRate(audio_io_handle_t ioHandle) const
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32((int32_t) ioHandle);
- remote()->transact(SAMPLE_RATE, data, &reply);
- return reply.readInt32();
- }
-
- // RESERVED for channelCount()
-
- virtual audio_format_t format(audio_io_handle_t output) const
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32((int32_t) output);
- remote()->transact(FORMAT, data, &reply);
- return (audio_format_t) reply.readInt32();
- }
-
- virtual size_t frameCount(audio_io_handle_t ioHandle) const
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32((int32_t) ioHandle);
- remote()->transact(FRAME_COUNT, data, &reply);
- return reply.readInt64();
- }
-
- virtual uint32_t latency(audio_io_handle_t output) const
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32((int32_t) output);
- remote()->transact(LATENCY, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t setMasterVolume(float value)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeFloat(value);
- remote()->transact(SET_MASTER_VOLUME, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t setMasterMute(bool muted)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32(muted);
- remote()->transact(SET_MASTER_MUTE, data, &reply);
- return reply.readInt32();
- }
-
- virtual float masterVolume() const
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- remote()->transact(MASTER_VOLUME, data, &reply);
- return reply.readFloat();
- }
-
- virtual bool masterMute() const
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- remote()->transact(MASTER_MUTE, data, &reply);
- return reply.readInt32();
- }
-
- status_t setMasterBalance(float balance) override
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeFloat(balance);
- status_t status = remote()->transact(SET_MASTER_BALANCE, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- return reply.readInt32();
- }
-
- status_t getMasterBalance(float *balance) const override
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- status_t status = remote()->transact(GET_MASTER_BALANCE, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = (status_t)reply.readInt32();
- if (status != NO_ERROR) {
- return status;
- }
- *balance = reply.readFloat();
- return NO_ERROR;
- }
-
- virtual status_t setStreamVolume(audio_stream_type_t stream, float value,
- audio_io_handle_t output)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32((int32_t) stream);
- data.writeFloat(value);
- data.writeInt32((int32_t) output);
- remote()->transact(SET_STREAM_VOLUME, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t setStreamMute(audio_stream_type_t stream, bool muted)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32((int32_t) stream);
- data.writeInt32(muted);
- remote()->transact(SET_STREAM_MUTE, data, &reply);
- return reply.readInt32();
- }
-
- virtual float streamVolume(audio_stream_type_t stream, audio_io_handle_t output) const
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32((int32_t) stream);
- data.writeInt32((int32_t) output);
- remote()->transact(STREAM_VOLUME, data, &reply);
- return reply.readFloat();
- }
-
- virtual bool streamMute(audio_stream_type_t stream) const
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32((int32_t) stream);
- remote()->transact(STREAM_MUTE, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t setMode(audio_mode_t mode)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32(mode);
- remote()->transact(SET_MODE, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t setMicMute(bool state)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32(state);
- remote()->transact(SET_MIC_MUTE, data, &reply);
- return reply.readInt32();
- }
-
- virtual bool getMicMute() const
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- remote()->transact(GET_MIC_MUTE, data, &reply);
- return reply.readInt32();
- }
-
- virtual void setRecordSilenced(audio_port_handle_t portId, bool silenced)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32(portId);
- data.writeInt32(silenced ? 1 : 0);
- remote()->transact(SET_RECORD_SILENCED, data, &reply);
- }
-
- virtual status_t setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32((int32_t) ioHandle);
- data.writeString8(keyValuePairs);
- remote()->transact(SET_PARAMETERS, data, &reply);
- return reply.readInt32();
- }
-
- virtual String8 getParameters(audio_io_handle_t ioHandle, const String8& keys) const
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32((int32_t) ioHandle);
- data.writeString8(keys);
- remote()->transact(GET_PARAMETERS, data, &reply);
- return reply.readString8();
- }
-
- virtual void registerClient(const sp<IAudioFlingerClient>& client)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeStrongBinder(IInterface::asBinder(client));
- remote()->transact(REGISTER_CLIENT, data, &reply);
- }
-
- virtual size_t getInputBufferSize(uint32_t sampleRate, audio_format_t format,
- audio_channel_mask_t channelMask) const
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32(sampleRate);
- data.writeInt32(format);
- data.writeInt32(channelMask);
- remote()->transact(GET_INPUTBUFFERSIZE, data, &reply);
- return reply.readInt64();
- }
-
- virtual status_t openOutput(audio_module_handle_t module,
- audio_io_handle_t *output,
- audio_config_t *config,
- const sp<DeviceDescriptorBase>& device,
- uint32_t *latencyMs,
- audio_output_flags_t flags)
- {
- if (output == nullptr || config == nullptr || device == nullptr || latencyMs == nullptr) {
- return BAD_VALUE;
- }
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32(module);
- data.write(config, sizeof(audio_config_t));
- data.writeParcelable(*device);
- data.writeInt32((int32_t) flags);
- status_t status = remote()->transact(OPEN_OUTPUT, data, &reply);
- if (status != NO_ERROR) {
- *output = AUDIO_IO_HANDLE_NONE;
- return status;
- }
- status = (status_t)reply.readInt32();
- if (status != NO_ERROR) {
- *output = AUDIO_IO_HANDLE_NONE;
- return status;
- }
- *output = (audio_io_handle_t)reply.readInt32();
- ALOGV("openOutput() returned output, %d", *output);
- reply.read(config, sizeof(audio_config_t));
- *latencyMs = reply.readInt32();
- return NO_ERROR;
- }
-
- virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1,
- audio_io_handle_t output2)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32((int32_t) output1);
- data.writeInt32((int32_t) output2);
- remote()->transact(OPEN_DUPLICATE_OUTPUT, data, &reply);
- return (audio_io_handle_t) reply.readInt32();
- }
-
- virtual status_t closeOutput(audio_io_handle_t output)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32((int32_t) output);
- remote()->transact(CLOSE_OUTPUT, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t suspendOutput(audio_io_handle_t output)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32((int32_t) output);
- remote()->transact(SUSPEND_OUTPUT, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t restoreOutput(audio_io_handle_t output)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32((int32_t) output);
- remote()->transact(RESTORE_OUTPUT, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t openInput(audio_module_handle_t module,
- audio_io_handle_t *input,
- audio_config_t *config,
- audio_devices_t *device,
- const String8& address,
- audio_source_t source,
- audio_input_flags_t flags)
- {
- if (input == NULL || config == NULL || device == NULL) {
- return BAD_VALUE;
- }
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32(module);
- data.writeInt32(*input);
- data.write(config, sizeof(audio_config_t));
- data.writeInt32(*device);
- data.writeString8(address);
- data.writeInt32(source);
- data.writeInt32(flags);
- status_t status = remote()->transact(OPEN_INPUT, data, &reply);
- if (status != NO_ERROR) {
- *input = AUDIO_IO_HANDLE_NONE;
- return status;
- }
- status = (status_t)reply.readInt32();
- if (status != NO_ERROR) {
- *input = AUDIO_IO_HANDLE_NONE;
- return status;
- }
- *input = (audio_io_handle_t)reply.readInt32();
- reply.read(config, sizeof(audio_config_t));
- *device = (audio_devices_t)reply.readInt32();
- return NO_ERROR;
- }
-
- virtual status_t closeInput(int input)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32(input);
- remote()->transact(CLOSE_INPUT, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t invalidateStream(audio_stream_type_t stream)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32((int32_t) stream);
- remote()->transact(INVALIDATE_STREAM, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t setVoiceVolume(float volume)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeFloat(volume);
- remote()->transact(SET_VOICE_VOLUME, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames,
- audio_io_handle_t output) const
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32((int32_t) output);
- remote()->transact(GET_RENDER_POSITION, data, &reply);
- status_t status = reply.readInt32();
- if (status == NO_ERROR) {
- uint32_t tmp = reply.readInt32();
- if (halFrames != NULL) {
- *halFrames = tmp;
- }
- tmp = reply.readInt32();
- if (dspFrames != NULL) {
- *dspFrames = tmp;
- }
- }
- return status;
- }
-
- virtual uint32_t getInputFramesLost(audio_io_handle_t ioHandle) const
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32((int32_t) ioHandle);
- status_t status = remote()->transact(GET_INPUT_FRAMES_LOST, data, &reply);
- if (status != NO_ERROR) {
- return 0;
- }
- return (uint32_t) reply.readInt32();
- }
-
- virtual audio_unique_id_t newAudioUniqueId(audio_unique_id_use_t use)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32((int32_t) use);
- status_t status = remote()->transact(NEW_AUDIO_UNIQUE_ID, data, &reply);
- audio_unique_id_t id = AUDIO_UNIQUE_ID_ALLOCATE;
- if (status == NO_ERROR) {
- id = reply.readInt32();
- }
- return id;
- }
-
- void acquireAudioSessionId(audio_session_t audioSession, pid_t pid, uid_t uid) override
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32(audioSession);
- data.writeInt32((int32_t)pid);
- data.writeInt32((int32_t)uid);
- remote()->transact(ACQUIRE_AUDIO_SESSION_ID, data, &reply);
- }
-
- virtual void releaseAudioSessionId(audio_session_t audioSession, int pid)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32(audioSession);
- data.writeInt32(pid);
- remote()->transact(RELEASE_AUDIO_SESSION_ID, data, &reply);
- }
-
- virtual status_t queryNumberEffects(uint32_t *numEffects) const
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- status_t status = remote()->transact(QUERY_NUM_EFFECTS, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = reply.readInt32();
- if (status != NO_ERROR) {
- return status;
- }
- if (numEffects != NULL) {
- *numEffects = (uint32_t)reply.readInt32();
- }
- return NO_ERROR;
- }
-
- virtual status_t queryEffect(uint32_t index, effect_descriptor_t *pDescriptor) const
- {
- if (pDescriptor == NULL) {
- return BAD_VALUE;
- }
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32(index);
- status_t status = remote()->transact(QUERY_EFFECT, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = reply.readInt32();
- if (status != NO_ERROR) {
- return status;
- }
- reply.read(pDescriptor, sizeof(effect_descriptor_t));
- return NO_ERROR;
- }
-
- virtual status_t getEffectDescriptor(const effect_uuid_t *pUuid,
- const effect_uuid_t *pType,
- uint32_t preferredTypeFlag,
- effect_descriptor_t *pDescriptor) const
- {
- if (pUuid == NULL || pType == NULL || pDescriptor == NULL) {
- return BAD_VALUE;
- }
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.write(pUuid, sizeof(effect_uuid_t));
- data.write(pType, sizeof(effect_uuid_t));
- data.writeUint32(preferredTypeFlag);
- status_t status = remote()->transact(GET_EFFECT_DESCRIPTOR, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = reply.readInt32();
- if (status != NO_ERROR) {
- return status;
- }
- reply.read(pDescriptor, sizeof(effect_descriptor_t));
- return NO_ERROR;
- }
-
- virtual sp<IEffect> createEffect(
- effect_descriptor_t *pDesc,
- const sp<IEffectClient>& client,
- int32_t priority,
- audio_io_handle_t output,
- audio_session_t sessionId,
- const AudioDeviceTypeAddr& device,
- const String16& opPackageName,
- pid_t pid,
- bool probe,
- status_t *status,
- int *id,
- int *enabled)
- {
- Parcel data, reply;
- sp<IEffect> effect;
- if (pDesc == NULL) {
- if (status != NULL) {
- *status = BAD_VALUE;
- }
- return nullptr;
- }
-
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.write(pDesc, sizeof(effect_descriptor_t));
- data.writeStrongBinder(IInterface::asBinder(client));
- data.writeInt32(priority);
- data.writeInt32((int32_t) output);
- data.writeInt32(sessionId);
- if (data.writeParcelable(device) != NO_ERROR) {
- if (status != NULL) {
- *status = NO_INIT;
- }
- return nullptr;
- }
- data.writeString16(opPackageName);
- data.writeInt32((int32_t) pid);
- data.writeInt32(probe ? 1 : 0);
-
- status_t lStatus = remote()->transact(CREATE_EFFECT, data, &reply);
- if (lStatus != NO_ERROR) {
- ALOGE("createEffect error: %s", strerror(-lStatus));
- } else {
- lStatus = reply.readInt32();
- int tmp = reply.readInt32();
- if (id != NULL) {
- *id = tmp;
- }
- tmp = reply.readInt32();
- if (enabled != NULL) {
- *enabled = tmp;
- }
- effect = interface_cast<IEffect>(reply.readStrongBinder());
- reply.read(pDesc, sizeof(effect_descriptor_t));
- }
- if (status != NULL) {
- *status = lStatus;
- }
-
- return effect;
- }
-
- virtual status_t moveEffects(audio_session_t session, audio_io_handle_t srcOutput,
- audio_io_handle_t dstOutput)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32(session);
- data.writeInt32((int32_t) srcOutput);
- data.writeInt32((int32_t) dstOutput);
- remote()->transact(MOVE_EFFECTS, data, &reply);
- return reply.readInt32();
- }
-
- virtual void setEffectSuspended(int effectId,
- audio_session_t sessionId,
- bool suspended)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32(effectId);
- data.writeInt32(sessionId);
- data.writeInt32(suspended ? 1 : 0);
- remote()->transact(SET_EFFECT_SUSPENDED, data, &reply);
- }
-
- virtual audio_module_handle_t loadHwModule(const char *name)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeCString(name);
- remote()->transact(LOAD_HW_MODULE, data, &reply);
- return (audio_module_handle_t) reply.readInt32();
- }
-
- virtual uint32_t getPrimaryOutputSamplingRate()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- remote()->transact(GET_PRIMARY_OUTPUT_SAMPLING_RATE, data, &reply);
- return reply.readInt32();
- }
-
- virtual size_t getPrimaryOutputFrameCount()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- remote()->transact(GET_PRIMARY_OUTPUT_FRAME_COUNT, data, &reply);
- return reply.readInt64();
- }
-
- virtual status_t setLowRamDevice(bool isLowRamDevice, int64_t totalMemory) override
- {
- Parcel data, reply;
-
- static_assert(NO_ERROR == 0, "NO_ERROR must be 0");
- return data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor())
- ?: data.writeInt32((int) isLowRamDevice)
- ?: data.writeInt64(totalMemory)
- ?: remote()->transact(SET_LOW_RAM_DEVICE, data, &reply)
- ?: reply.readInt32();
- }
-
- virtual status_t listAudioPorts(unsigned int *num_ports,
- struct audio_port *ports)
- {
- if (num_ports == NULL || *num_ports == 0 || ports == NULL) {
- return BAD_VALUE;
- }
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32(*num_ports);
- status_t status = remote()->transact(LIST_AUDIO_PORTS, data, &reply);
- if (status != NO_ERROR ||
- (status = (status_t)reply.readInt32()) != NO_ERROR) {
- return status;
- }
- *num_ports = (unsigned int)reply.readInt32();
- reply.read(ports, *num_ports * sizeof(struct audio_port));
- return status;
- }
- virtual status_t getAudioPort(struct audio_port *port)
- {
- if (port == NULL) {
- return BAD_VALUE;
- }
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.write(port, sizeof(struct audio_port));
- status_t status = remote()->transact(GET_AUDIO_PORT, data, &reply);
- if (status != NO_ERROR ||
- (status = (status_t)reply.readInt32()) != NO_ERROR) {
- return status;
- }
- reply.read(port, sizeof(struct audio_port));
- return status;
- }
- virtual status_t createAudioPatch(const struct audio_patch *patch,
- audio_patch_handle_t *handle)
- {
- if (patch == NULL || handle == NULL) {
- return BAD_VALUE;
- }
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.write(patch, sizeof(struct audio_patch));
- data.write(handle, sizeof(audio_patch_handle_t));
- status_t status = remote()->transact(CREATE_AUDIO_PATCH, data, &reply);
- if (status != NO_ERROR ||
- (status = (status_t)reply.readInt32()) != NO_ERROR) {
- return status;
- }
- reply.read(handle, sizeof(audio_patch_handle_t));
- return status;
- }
- virtual status_t releaseAudioPatch(audio_patch_handle_t handle)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.write(&handle, sizeof(audio_patch_handle_t));
- status_t status = remote()->transact(RELEASE_AUDIO_PATCH, data, &reply);
- if (status != NO_ERROR) {
- status = (status_t)reply.readInt32();
- }
- return status;
- }
- virtual status_t listAudioPatches(unsigned int *num_patches,
- struct audio_patch *patches)
- {
- if (num_patches == NULL || *num_patches == 0 || patches == NULL) {
- return BAD_VALUE;
- }
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32(*num_patches);
- status_t status = remote()->transact(LIST_AUDIO_PATCHES, data, &reply);
- if (status != NO_ERROR ||
- (status = (status_t)reply.readInt32()) != NO_ERROR) {
- return status;
- }
- *num_patches = (unsigned int)reply.readInt32();
- reply.read(patches, *num_patches * sizeof(struct audio_patch));
- return status;
- }
- virtual status_t setAudioPortConfig(const struct audio_port_config *config)
- {
- if (config == NULL) {
- return BAD_VALUE;
- }
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.write(config, sizeof(struct audio_port_config));
- status_t status = remote()->transact(SET_AUDIO_PORT_CONFIG, data, &reply);
- if (status != NO_ERROR) {
- status = (status_t)reply.readInt32();
- }
- return status;
- }
- virtual audio_hw_sync_t getAudioHwSyncForSession(audio_session_t sessionId)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32(sessionId);
- status_t status = remote()->transact(GET_AUDIO_HW_SYNC_FOR_SESSION, data, &reply);
- if (status != NO_ERROR) {
- return AUDIO_HW_SYNC_INVALID;
- }
- return (audio_hw_sync_t)reply.readInt32();
- }
- virtual status_t systemReady()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- return remote()->transact(SYSTEM_READY, data, &reply, IBinder::FLAG_ONEWAY);
- }
- virtual size_t frameCountHAL(audio_io_handle_t ioHandle) const
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32((int32_t) ioHandle);
- status_t status = remote()->transact(FRAME_COUNT_HAL, data, &reply);
- if (status != NO_ERROR) {
- return 0;
- }
- return reply.readInt64();
- }
- virtual status_t getMicrophones(std::vector<media::MicrophoneInfo> *microphones)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- status_t status = remote()->transact(GET_MICROPHONES, data, &reply);
- if (status != NO_ERROR ||
- (status = (status_t)reply.readInt32()) != NO_ERROR) {
- return status;
- }
- status = reply.readParcelableVector(microphones);
- return status;
- }
- virtual status_t setAudioHalPids(const std::vector<pid_t>& pids)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32(pids.size());
- for (auto pid : pids) {
- data.writeInt32(pid);
- }
- status_t status = remote()->transact(SET_AUDIO_HAL_PIDS, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- return static_cast <status_t> (reply.readInt32());
- }
-};
-
-IMPLEMENT_META_INTERFACE(AudioFlinger, "android.media.IAudioFlinger");
-
-// ----------------------------------------------------------------------
-
-status_t BnAudioFlinger::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- // make sure transactions reserved to AudioPolicyManager do not come from other processes
- switch (code) {
- case SET_STREAM_VOLUME:
- case SET_STREAM_MUTE:
- case OPEN_OUTPUT:
- case OPEN_DUPLICATE_OUTPUT:
- case CLOSE_OUTPUT:
- case SUSPEND_OUTPUT:
- case RESTORE_OUTPUT:
- case OPEN_INPUT:
- case CLOSE_INPUT:
- case INVALIDATE_STREAM:
- case SET_VOICE_VOLUME:
- case MOVE_EFFECTS:
- case SET_EFFECT_SUSPENDED:
- case LOAD_HW_MODULE:
- case LIST_AUDIO_PORTS:
- case GET_AUDIO_PORT:
- case CREATE_AUDIO_PATCH:
- case RELEASE_AUDIO_PATCH:
- case LIST_AUDIO_PATCHES:
- case SET_AUDIO_PORT_CONFIG:
- case SET_RECORD_SILENCED:
- ALOGW("%s: transaction %d received from PID %d",
- __func__, code, IPCThreadState::self()->getCallingPid());
- // return status only for non void methods
- switch (code) {
- case SET_RECORD_SILENCED:
- case SET_EFFECT_SUSPENDED:
- break;
- default:
- reply->writeInt32(static_cast<int32_t> (INVALID_OPERATION));
- break;
- }
- return OK;
- default:
- break;
- }
-
- // make sure the following transactions come from system components
- switch (code) {
- case SET_MASTER_VOLUME:
- case SET_MASTER_MUTE:
- case SET_MODE:
- case SET_MIC_MUTE:
- case SET_LOW_RAM_DEVICE:
- case SYSTEM_READY:
- case SET_AUDIO_HAL_PIDS: {
- if (!isServiceUid(IPCThreadState::self()->getCallingUid())) {
- ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
- __func__, code, IPCThreadState::self()->getCallingPid(),
- IPCThreadState::self()->getCallingUid());
- // return status only for non void methods
- switch (code) {
- case SYSTEM_READY:
- break;
- default:
- reply->writeInt32(static_cast<int32_t> (INVALID_OPERATION));
- break;
- }
- return OK;
- }
- } break;
- default:
- break;
- }
-
- // List of relevant events that trigger log merging.
- // Log merging should activate during audio activity of any kind. This are considered the
- // most relevant events.
- // TODO should select more wisely the items from the list
- switch (code) {
- case CREATE_TRACK:
- case CREATE_RECORD:
- case SET_MASTER_VOLUME:
- case SET_MASTER_MUTE:
- case SET_MIC_MUTE:
- case SET_PARAMETERS:
- case CREATE_EFFECT:
- case SYSTEM_READY: {
- requestLogMerge();
- break;
- }
- default:
- break;
- }
-
- std::string tag("IAudioFlinger command " + std::to_string(code));
- TimeCheck check(tag.c_str());
-
- switch (code) {
- case CREATE_TRACK: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
-
- CreateTrackInput input;
- if (input.readFromParcel((Parcel*)&data) != NO_ERROR) {
- reply->writeInt32(DEAD_OBJECT);
- return NO_ERROR;
- }
-
- status_t status;
- CreateTrackOutput output;
-
- sp<IAudioTrack> track= createTrack(input,
- output,
- &status);
-
- LOG_ALWAYS_FATAL_IF((track != 0) != (status == NO_ERROR));
- reply->writeInt32(status);
- if (status != NO_ERROR) {
- return NO_ERROR;
- }
- reply->writeStrongBinder(IInterface::asBinder(track));
- output.writeToParcel(reply);
- return NO_ERROR;
- } break;
- case CREATE_RECORD: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
-
- CreateRecordInput input;
- if (input.readFromParcel((Parcel*)&data) != NO_ERROR) {
- reply->writeInt32(DEAD_OBJECT);
- return NO_ERROR;
- }
-
- status_t status;
- CreateRecordOutput output;
-
- sp<media::IAudioRecord> record = createRecord(input,
- output,
- &status);
-
- LOG_ALWAYS_FATAL_IF((record != 0) != (status == NO_ERROR));
- reply->writeInt32(status);
- if (status != NO_ERROR) {
- return NO_ERROR;
- }
- reply->writeStrongBinder(IInterface::asBinder(record));
- output.writeToParcel(reply);
- return NO_ERROR;
- } break;
- case SAMPLE_RATE: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- reply->writeInt32( sampleRate((audio_io_handle_t) data.readInt32()) );
- return NO_ERROR;
- } break;
-
- // RESERVED for channelCount()
-
- case FORMAT: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- reply->writeInt32( format((audio_io_handle_t) data.readInt32()) );
- return NO_ERROR;
- } break;
- case FRAME_COUNT: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- reply->writeInt64( frameCount((audio_io_handle_t) data.readInt32()) );
- return NO_ERROR;
- } break;
- case LATENCY: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- reply->writeInt32( latency((audio_io_handle_t) data.readInt32()) );
- return NO_ERROR;
- } break;
- case SET_MASTER_VOLUME: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- reply->writeInt32( setMasterVolume(data.readFloat()) );
- return NO_ERROR;
- } break;
- case SET_MASTER_MUTE: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- reply->writeInt32( setMasterMute(data.readInt32()) );
- return NO_ERROR;
- } break;
- case MASTER_VOLUME: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- reply->writeFloat( masterVolume() );
- return NO_ERROR;
- } break;
- case MASTER_MUTE: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- reply->writeInt32( masterMute() );
- return NO_ERROR;
- } break;
- case SET_MASTER_BALANCE: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- reply->writeInt32( setMasterBalance(data.readFloat()) );
- return NO_ERROR;
- } break;
- case GET_MASTER_BALANCE: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- float f;
- const status_t status = getMasterBalance(&f);
- reply->writeInt32((int32_t)status);
- if (status == NO_ERROR) {
- (void)reply->writeFloat(f);
- }
- return NO_ERROR;
- } break;
- case SET_STREAM_VOLUME: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- int stream = data.readInt32();
- float volume = data.readFloat();
- audio_io_handle_t output = (audio_io_handle_t) data.readInt32();
- reply->writeInt32( setStreamVolume((audio_stream_type_t) stream, volume, output) );
- return NO_ERROR;
- } break;
- case SET_STREAM_MUTE: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- int stream = data.readInt32();
- reply->writeInt32( setStreamMute((audio_stream_type_t) stream, data.readInt32()) );
- return NO_ERROR;
- } break;
- case STREAM_VOLUME: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- int stream = data.readInt32();
- int output = data.readInt32();
- reply->writeFloat( streamVolume((audio_stream_type_t) stream, output) );
- return NO_ERROR;
- } break;
- case STREAM_MUTE: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- int stream = data.readInt32();
- reply->writeInt32( streamMute((audio_stream_type_t) stream) );
- return NO_ERROR;
- } break;
- case SET_MODE: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- audio_mode_t mode = (audio_mode_t) data.readInt32();
- reply->writeInt32( setMode(mode) );
- return NO_ERROR;
- } break;
- case SET_MIC_MUTE: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- int state = data.readInt32();
- reply->writeInt32( setMicMute(state) );
- return NO_ERROR;
- } break;
- case GET_MIC_MUTE: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- reply->writeInt32( getMicMute() );
- return NO_ERROR;
- } break;
- case SET_RECORD_SILENCED: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- audio_port_handle_t portId = data.readInt32();
- bool silenced = data.readInt32() == 1;
- setRecordSilenced(portId, silenced);
- return NO_ERROR;
- } break;
- case SET_PARAMETERS: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- audio_io_handle_t ioHandle = (audio_io_handle_t) data.readInt32();
- String8 keyValuePairs(data.readString8());
- reply->writeInt32(setParameters(ioHandle, keyValuePairs));
- return NO_ERROR;
- } break;
- case GET_PARAMETERS: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- audio_io_handle_t ioHandle = (audio_io_handle_t) data.readInt32();
- String8 keys(data.readString8());
- reply->writeString8(getParameters(ioHandle, keys));
- return NO_ERROR;
- } break;
-
- case REGISTER_CLIENT: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient>(
- data.readStrongBinder());
- registerClient(client);
- return NO_ERROR;
- } break;
- case GET_INPUTBUFFERSIZE: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- uint32_t sampleRate = data.readInt32();
- audio_format_t format = (audio_format_t) data.readInt32();
- audio_channel_mask_t channelMask = (audio_channel_mask_t) data.readInt32();
- reply->writeInt64( getInputBufferSize(sampleRate, format, channelMask) );
- return NO_ERROR;
- } break;
- case OPEN_OUTPUT: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- audio_module_handle_t module = (audio_module_handle_t)data.readInt32();
- audio_config_t config = {};
- if (data.read(&config, sizeof(audio_config_t)) != NO_ERROR) {
- ALOGE("b/23905951");
- }
- sp<DeviceDescriptorBase> device = new DeviceDescriptorBase(AUDIO_DEVICE_NONE);
- status_t status = NO_ERROR;
- if ((status = data.readParcelable(device.get())) != NO_ERROR) {
- reply->writeInt32((int32_t)status);
- return NO_ERROR;
- }
- audio_output_flags_t flags = (audio_output_flags_t) data.readInt32();
- uint32_t latencyMs = 0;
- audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
- status = openOutput(module, &output, &config, device, &latencyMs, flags);
- ALOGV("OPEN_OUTPUT output, %d", output);
- reply->writeInt32((int32_t)status);
- if (status == NO_ERROR) {
- reply->writeInt32((int32_t)output);
- reply->write(&config, sizeof(audio_config_t));
- reply->writeInt32(latencyMs);
- }
- return NO_ERROR;
- } break;
- case OPEN_DUPLICATE_OUTPUT: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- audio_io_handle_t output1 = (audio_io_handle_t) data.readInt32();
- audio_io_handle_t output2 = (audio_io_handle_t) data.readInt32();
- reply->writeInt32((int32_t) openDuplicateOutput(output1, output2));
- return NO_ERROR;
- } break;
- case CLOSE_OUTPUT: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- reply->writeInt32(closeOutput((audio_io_handle_t) data.readInt32()));
- return NO_ERROR;
- } break;
- case SUSPEND_OUTPUT: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- reply->writeInt32(suspendOutput((audio_io_handle_t) data.readInt32()));
- return NO_ERROR;
- } break;
- case RESTORE_OUTPUT: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- reply->writeInt32(restoreOutput((audio_io_handle_t) data.readInt32()));
- return NO_ERROR;
- } break;
- case OPEN_INPUT: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- audio_module_handle_t module = (audio_module_handle_t)data.readInt32();
- audio_io_handle_t input = (audio_io_handle_t)data.readInt32();
- audio_config_t config = {};
- if (data.read(&config, sizeof(audio_config_t)) != NO_ERROR) {
- ALOGE("b/23905951");
- }
- audio_devices_t device = (audio_devices_t)data.readInt32();
- String8 address(data.readString8());
- audio_source_t source = (audio_source_t)data.readInt32();
- audio_input_flags_t flags = (audio_input_flags_t) data.readInt32();
-
- status_t status = openInput(module, &input, &config,
- &device, address, source, flags);
- reply->writeInt32((int32_t) status);
- if (status == NO_ERROR) {
- reply->writeInt32((int32_t) input);
- reply->write(&config, sizeof(audio_config_t));
- reply->writeInt32(device);
- }
- return NO_ERROR;
- } break;
- case CLOSE_INPUT: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- reply->writeInt32(closeInput((audio_io_handle_t) data.readInt32()));
- return NO_ERROR;
- } break;
- case INVALIDATE_STREAM: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- audio_stream_type_t stream = (audio_stream_type_t) data.readInt32();
- reply->writeInt32(invalidateStream(stream));
- return NO_ERROR;
- } break;
- case SET_VOICE_VOLUME: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- float volume = data.readFloat();
- reply->writeInt32( setVoiceVolume(volume) );
- return NO_ERROR;
- } break;
- case GET_RENDER_POSITION: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- audio_io_handle_t output = (audio_io_handle_t) data.readInt32();
- uint32_t halFrames = 0;
- uint32_t dspFrames = 0;
- status_t status = getRenderPosition(&halFrames, &dspFrames, output);
- reply->writeInt32(status);
- if (status == NO_ERROR) {
- reply->writeInt32(halFrames);
- reply->writeInt32(dspFrames);
- }
- return NO_ERROR;
- }
- case GET_INPUT_FRAMES_LOST: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- audio_io_handle_t ioHandle = (audio_io_handle_t) data.readInt32();
- reply->writeInt32((int32_t) getInputFramesLost(ioHandle));
- return NO_ERROR;
- } break;
- case NEW_AUDIO_UNIQUE_ID: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- reply->writeInt32(newAudioUniqueId((audio_unique_id_use_t) data.readInt32()));
- return NO_ERROR;
- } break;
- case ACQUIRE_AUDIO_SESSION_ID: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- audio_session_t audioSession = (audio_session_t) data.readInt32();
- const pid_t pid = (pid_t)data.readInt32();
- const uid_t uid = (uid_t)data.readInt32();
- acquireAudioSessionId(audioSession, pid, uid);
- return NO_ERROR;
- } break;
- case RELEASE_AUDIO_SESSION_ID: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- audio_session_t audioSession = (audio_session_t) data.readInt32();
- int pid = data.readInt32();
- releaseAudioSessionId(audioSession, pid);
- return NO_ERROR;
- } break;
- case QUERY_NUM_EFFECTS: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- uint32_t numEffects = 0;
- status_t status = queryNumberEffects(&numEffects);
- reply->writeInt32(status);
- if (status == NO_ERROR) {
- reply->writeInt32((int32_t)numEffects);
- }
- return NO_ERROR;
- }
- case QUERY_EFFECT: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- effect_descriptor_t desc = {};
- status_t status = queryEffect(data.readInt32(), &desc);
- reply->writeInt32(status);
- if (status == NO_ERROR) {
- reply->write(&desc, sizeof(effect_descriptor_t));
- }
- return NO_ERROR;
- }
- case GET_EFFECT_DESCRIPTOR: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- effect_uuid_t uuid = {};
- if (data.read(&uuid, sizeof(effect_uuid_t)) != NO_ERROR) {
- android_errorWriteLog(0x534e4554, "139417189");
- }
- effect_uuid_t type = {};
- if (data.read(&type, sizeof(effect_uuid_t)) != NO_ERROR) {
- android_errorWriteLog(0x534e4554, "139417189");
- }
- uint32_t preferredTypeFlag = data.readUint32();
- effect_descriptor_t desc = {};
- status_t status = getEffectDescriptor(&uuid, &type, preferredTypeFlag, &desc);
- reply->writeInt32(status);
- if (status == NO_ERROR) {
- reply->write(&desc, sizeof(effect_descriptor_t));
- }
- return NO_ERROR;
- }
- case CREATE_EFFECT: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- effect_descriptor_t desc = {};
- if (data.read(&desc, sizeof(effect_descriptor_t)) != NO_ERROR) {
- ALOGE("b/23905951");
- }
- sp<IEffectClient> client = interface_cast<IEffectClient>(data.readStrongBinder());
- int32_t priority = data.readInt32();
- audio_io_handle_t output = (audio_io_handle_t) data.readInt32();
- audio_session_t sessionId = (audio_session_t) data.readInt32();
- AudioDeviceTypeAddr device;
- status_t status = NO_ERROR;
- if ((status = data.readParcelable(&device)) != NO_ERROR) {
- return status;
- }
- const String16 opPackageName = data.readString16();
- pid_t pid = (pid_t)data.readInt32();
- bool probe = data.readInt32() == 1;
-
- int id = 0;
- int enabled = 0;
-
- sp<IEffect> effect = createEffect(&desc, client, priority, output, sessionId, device,
- opPackageName, pid, probe, &status, &id, &enabled);
- reply->writeInt32(status);
- reply->writeInt32(id);
- reply->writeInt32(enabled);
- reply->writeStrongBinder(IInterface::asBinder(effect));
- reply->write(&desc, sizeof(effect_descriptor_t));
- return NO_ERROR;
- } break;
- case MOVE_EFFECTS: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- audio_session_t session = (audio_session_t) data.readInt32();
- audio_io_handle_t srcOutput = (audio_io_handle_t) data.readInt32();
- audio_io_handle_t dstOutput = (audio_io_handle_t) data.readInt32();
- reply->writeInt32(moveEffects(session, srcOutput, dstOutput));
- return NO_ERROR;
- } break;
- case SET_EFFECT_SUSPENDED: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- int effectId = data.readInt32();
- audio_session_t sessionId = (audio_session_t) data.readInt32();
- bool suspended = data.readInt32() == 1;
- setEffectSuspended(effectId, sessionId, suspended);
- return NO_ERROR;
- } break;
- case LOAD_HW_MODULE: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- reply->writeInt32(loadHwModule(data.readCString()));
- return NO_ERROR;
- } break;
- case GET_PRIMARY_OUTPUT_SAMPLING_RATE: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- reply->writeInt32(getPrimaryOutputSamplingRate());
- return NO_ERROR;
- } break;
- case GET_PRIMARY_OUTPUT_FRAME_COUNT: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- reply->writeInt64(getPrimaryOutputFrameCount());
- return NO_ERROR;
- } break;
- case SET_LOW_RAM_DEVICE: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- int32_t isLowRamDevice;
- int64_t totalMemory;
- const status_t status =
- data.readInt32(&isLowRamDevice) ?:
- data.readInt64(&totalMemory) ?:
- setLowRamDevice(isLowRamDevice != 0, totalMemory);
- (void)reply->writeInt32(status);
- return NO_ERROR;
- } break;
- case LIST_AUDIO_PORTS: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- unsigned int numPortsReq = data.readInt32();
- if (numPortsReq > MAX_ITEMS_PER_LIST) {
- numPortsReq = MAX_ITEMS_PER_LIST;
- }
- unsigned int numPorts = numPortsReq;
- struct audio_port *ports =
- (struct audio_port *)calloc(numPortsReq,
- sizeof(struct audio_port));
- if (ports == NULL) {
- reply->writeInt32(NO_MEMORY);
- reply->writeInt32(0);
- return NO_ERROR;
- }
- status_t status = listAudioPorts(&numPorts, ports);
- reply->writeInt32(status);
- reply->writeInt32(numPorts);
- if (status == NO_ERROR) {
- if (numPortsReq > numPorts) {
- numPortsReq = numPorts;
- }
- reply->write(ports, numPortsReq * sizeof(struct audio_port));
- }
- free(ports);
- return NO_ERROR;
- } break;
- case GET_AUDIO_PORT: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- struct audio_port port = {};
- status_t status = data.read(&port, sizeof(struct audio_port));
- if (status != NO_ERROR) {
- ALOGE("b/23905951");
- return status;
- }
- status = AudioSanitizer::sanitizeAudioPort(&port);
- if (status == NO_ERROR) {
- status = getAudioPort(&port);
- }
- reply->writeInt32(status);
- if (status == NO_ERROR) {
- reply->write(&port, sizeof(struct audio_port));
- }
- return NO_ERROR;
- } break;
- case CREATE_AUDIO_PATCH: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- struct audio_patch patch;
- status_t status = data.read(&patch, sizeof(struct audio_patch));
- if (status != NO_ERROR) {
- return status;
- }
- audio_patch_handle_t handle = AUDIO_PATCH_HANDLE_NONE;
- status = data.read(&handle, sizeof(audio_patch_handle_t));
- if (status != NO_ERROR) {
- ALOGE("b/23905951");
- return status;
- }
- status = AudioSanitizer::sanitizeAudioPatch(&patch);
- if (status == NO_ERROR) {
- status = createAudioPatch(&patch, &handle);
- }
- reply->writeInt32(status);
- if (status == NO_ERROR) {
- reply->write(&handle, sizeof(audio_patch_handle_t));
- }
- return NO_ERROR;
- } break;
- case RELEASE_AUDIO_PATCH: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- audio_patch_handle_t handle;
- data.read(&handle, sizeof(audio_patch_handle_t));
- status_t status = releaseAudioPatch(handle);
- reply->writeInt32(status);
- return NO_ERROR;
- } break;
- case LIST_AUDIO_PATCHES: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- unsigned int numPatchesReq = data.readInt32();
- if (numPatchesReq > MAX_ITEMS_PER_LIST) {
- numPatchesReq = MAX_ITEMS_PER_LIST;
- }
- unsigned int numPatches = numPatchesReq;
- struct audio_patch *patches =
- (struct audio_patch *)calloc(numPatchesReq,
- sizeof(struct audio_patch));
- if (patches == NULL) {
- reply->writeInt32(NO_MEMORY);
- reply->writeInt32(0);
- return NO_ERROR;
- }
- status_t status = listAudioPatches(&numPatches, patches);
- reply->writeInt32(status);
- reply->writeInt32(numPatches);
- if (status == NO_ERROR) {
- if (numPatchesReq > numPatches) {
- numPatchesReq = numPatches;
- }
- reply->write(patches, numPatchesReq * sizeof(struct audio_patch));
- }
- free(patches);
- return NO_ERROR;
- } break;
- case SET_AUDIO_PORT_CONFIG: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- struct audio_port_config config;
- status_t status = data.read(&config, sizeof(struct audio_port_config));
- if (status != NO_ERROR) {
- return status;
- }
- status = AudioSanitizer::sanitizeAudioPortConfig(&config);
- if (status == NO_ERROR) {
- status = setAudioPortConfig(&config);
- }
- reply->writeInt32(status);
- return NO_ERROR;
- } break;
- case GET_AUDIO_HW_SYNC_FOR_SESSION: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- reply->writeInt32(getAudioHwSyncForSession((audio_session_t) data.readInt32()));
- return NO_ERROR;
- } break;
- case SYSTEM_READY: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- systemReady();
- return NO_ERROR;
- } break;
- case FRAME_COUNT_HAL: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- reply->writeInt64( frameCountHAL((audio_io_handle_t) data.readInt32()) );
- return NO_ERROR;
- } break;
- case GET_MICROPHONES: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- std::vector<media::MicrophoneInfo> microphones;
- status_t status = getMicrophones(µphones);
- reply->writeInt32(status);
- if (status == NO_ERROR) {
- reply->writeParcelableVector(microphones);
- }
- return NO_ERROR;
- }
- case SET_AUDIO_HAL_PIDS: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- std::vector<pid_t> pids;
- int32_t size;
- status_t status = data.readInt32(&size);
- if (status != NO_ERROR) {
- return status;
- }
- if (size < 0) {
- return BAD_VALUE;
- }
- if (size > MAX_ITEMS_PER_LIST) {
- size = MAX_ITEMS_PER_LIST;
- }
- for (int32_t i = 0; i < size; i++) {
- int32_t pid;
- status = data.readInt32(&pid);
- if (status != NO_ERROR) {
- return status;
- }
- pids.push_back(pid);
- }
- reply->writeInt32(setAudioHalPids(pids));
- return NO_ERROR;
- }
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
+ConversionResult<media::CreateTrackRequest> IAudioFlinger::CreateTrackInput::toAidl() const {
+ media::CreateTrackRequest aidl;
+ aidl.attr = VALUE_OR_RETURN(legacy2aidl_audio_attributes_t_AudioAttributesInternal(attr));
+ aidl.config = VALUE_OR_RETURN(legacy2aidl_audio_config_t_AudioConfig(config));
+ aidl.clientInfo = VALUE_OR_RETURN(legacy2aidl_AudioClient_AudioClient(clientInfo));
+ aidl.sharedBuffer = VALUE_OR_RETURN(legacy2aidl_NullableIMemory_SharedFileRegion(sharedBuffer));
+ aidl.notificationsPerBuffer = VALUE_OR_RETURN(convertIntegral<int32_t>(notificationsPerBuffer));
+ aidl.speed = speed;
+ aidl.audioTrackCallback = audioTrackCallback;
+ aidl.flags = VALUE_OR_RETURN(legacy2aidl_audio_output_flags_t_int32_t_mask(flags));
+ aidl.frameCount = VALUE_OR_RETURN(convertIntegral<int64_t>(frameCount));
+ aidl.notificationFrameCount = VALUE_OR_RETURN(convertIntegral<int64_t>(notificationFrameCount));
+ aidl.selectedDeviceId = VALUE_OR_RETURN(
+ legacy2aidl_audio_port_handle_t_int32_t(selectedDeviceId));
+ aidl.sessionId = VALUE_OR_RETURN(legacy2aidl_audio_session_t_int32_t(sessionId));
+ return aidl;
}
-// ----------------------------------------------------------------------------
+ConversionResult<IAudioFlinger::CreateTrackInput>
+IAudioFlinger::CreateTrackInput::fromAidl(const media::CreateTrackRequest& aidl) {
+ IAudioFlinger::CreateTrackInput legacy;
+ legacy.attr = VALUE_OR_RETURN(aidl2legacy_AudioAttributesInternal_audio_attributes_t(aidl.attr));
+ legacy.config = VALUE_OR_RETURN(aidl2legacy_AudioConfig_audio_config_t(aidl.config));
+ legacy.clientInfo = VALUE_OR_RETURN(aidl2legacy_AudioClient_AudioClient(aidl.clientInfo));
+ legacy.sharedBuffer = VALUE_OR_RETURN(aidl2legacy_NullableSharedFileRegion_IMemory(aidl.sharedBuffer));
+ legacy.notificationsPerBuffer = VALUE_OR_RETURN(
+ convertIntegral<uint32_t>(aidl.notificationsPerBuffer));
+ legacy.speed = aidl.speed;
+ legacy.audioTrackCallback = aidl.audioTrackCallback;
+ legacy.flags = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_output_flags_t_mask(aidl.flags));
+ legacy.frameCount = VALUE_OR_RETURN(convertIntegral<size_t>(aidl.frameCount));
+ legacy.notificationFrameCount = VALUE_OR_RETURN(
+ convertIntegral<size_t>(aidl.notificationFrameCount));
+ legacy.selectedDeviceId = VALUE_OR_RETURN(
+ aidl2legacy_int32_t_audio_port_handle_t(aidl.selectedDeviceId));
+ legacy.sessionId = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_session_t(aidl.sessionId));
+ return legacy;
+}
+
+ConversionResult<media::CreateTrackResponse>
+IAudioFlinger::CreateTrackOutput::toAidl() const {
+ media::CreateTrackResponse aidl;
+ aidl.flags = VALUE_OR_RETURN(legacy2aidl_audio_output_flags_t_int32_t_mask(flags));
+ aidl.frameCount = VALUE_OR_RETURN(convertIntegral<int64_t>(frameCount));
+ aidl.notificationFrameCount = VALUE_OR_RETURN(convertIntegral<int64_t>(notificationFrameCount));
+ aidl.selectedDeviceId = VALUE_OR_RETURN(
+ legacy2aidl_audio_port_handle_t_int32_t(selectedDeviceId));
+ aidl.sessionId = VALUE_OR_RETURN(legacy2aidl_audio_session_t_int32_t(sessionId));
+ aidl.sampleRate = VALUE_OR_RETURN(convertIntegral<int32_t>(sampleRate));
+ aidl.afFrameCount = VALUE_OR_RETURN(convertIntegral<int64_t>(afFrameCount));
+ aidl.afSampleRate = VALUE_OR_RETURN(convertIntegral<int32_t>(afSampleRate));
+ aidl.afLatencyMs = VALUE_OR_RETURN(convertIntegral<int32_t>(afLatencyMs));
+ aidl.outputId = VALUE_OR_RETURN(legacy2aidl_audio_io_handle_t_int32_t(outputId));
+ aidl.portId = VALUE_OR_RETURN(legacy2aidl_audio_port_handle_t_int32_t(portId));
+ aidl.audioTrack = audioTrack;
+ return aidl;
+}
+
+ConversionResult<IAudioFlinger::CreateTrackOutput>
+IAudioFlinger::CreateTrackOutput::fromAidl(
+ const media::CreateTrackResponse& aidl) {
+ IAudioFlinger::CreateTrackOutput legacy;
+ legacy.flags = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_output_flags_t_mask(aidl.flags));
+ legacy.frameCount = VALUE_OR_RETURN(convertIntegral<size_t>(aidl.frameCount));
+ legacy.notificationFrameCount = VALUE_OR_RETURN(
+ convertIntegral<size_t>(aidl.notificationFrameCount));
+ legacy.selectedDeviceId = VALUE_OR_RETURN(
+ aidl2legacy_int32_t_audio_port_handle_t(aidl.selectedDeviceId));
+ legacy.sessionId = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_session_t(aidl.sessionId));
+ legacy.sampleRate = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.sampleRate));
+ legacy.afFrameCount = VALUE_OR_RETURN(convertIntegral<size_t>(aidl.afFrameCount));
+ legacy.afSampleRate = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.afSampleRate));
+ legacy.afLatencyMs = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.afLatencyMs));
+ legacy.outputId = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_io_handle_t(aidl.outputId));
+ legacy.portId = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_port_handle_t(aidl.portId));
+ legacy.audioTrack = aidl.audioTrack;
+ return legacy;
+}
+
+ConversionResult<media::CreateRecordRequest>
+IAudioFlinger::CreateRecordInput::toAidl() const {
+ media::CreateRecordRequest aidl;
+ aidl.attr = VALUE_OR_RETURN(legacy2aidl_audio_attributes_t_AudioAttributesInternal(attr));
+ aidl.config = VALUE_OR_RETURN(legacy2aidl_audio_config_base_t_AudioConfigBase(config));
+ aidl.clientInfo = VALUE_OR_RETURN(legacy2aidl_AudioClient_AudioClient(clientInfo));
+ aidl.riid = VALUE_OR_RETURN(legacy2aidl_audio_unique_id_t_int32_t(riid));
+ aidl.maxSharedAudioHistoryMs = VALUE_OR_RETURN(
+ convertIntegral<int32_t>(maxSharedAudioHistoryMs));
+ aidl.flags = VALUE_OR_RETURN(legacy2aidl_audio_input_flags_t_int32_t_mask(flags));
+ aidl.frameCount = VALUE_OR_RETURN(convertIntegral<int64_t>(frameCount));
+ aidl.notificationFrameCount = VALUE_OR_RETURN(convertIntegral<int64_t>(notificationFrameCount));
+ aidl.selectedDeviceId = VALUE_OR_RETURN(
+ legacy2aidl_audio_port_handle_t_int32_t(selectedDeviceId));
+ aidl.sessionId = VALUE_OR_RETURN(legacy2aidl_audio_session_t_int32_t(sessionId));
+ return aidl;
+}
+
+ConversionResult<IAudioFlinger::CreateRecordInput>
+IAudioFlinger::CreateRecordInput::fromAidl(
+ const media::CreateRecordRequest& aidl) {
+ IAudioFlinger::CreateRecordInput legacy;
+ legacy.attr = VALUE_OR_RETURN(
+ aidl2legacy_AudioAttributesInternal_audio_attributes_t(aidl.attr));
+ legacy.config = VALUE_OR_RETURN(aidl2legacy_AudioConfigBase_audio_config_base_t(aidl.config));
+ legacy.clientInfo = VALUE_OR_RETURN(aidl2legacy_AudioClient_AudioClient(aidl.clientInfo));
+ legacy.riid = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_unique_id_t(aidl.riid));
+ legacy.maxSharedAudioHistoryMs = VALUE_OR_RETURN(
+ convertIntegral<int32_t>(aidl.maxSharedAudioHistoryMs));
+ legacy.flags = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_input_flags_t_mask(aidl.flags));
+ legacy.frameCount = VALUE_OR_RETURN(convertIntegral<size_t>(aidl.frameCount));
+ legacy.notificationFrameCount = VALUE_OR_RETURN(
+ convertIntegral<size_t>(aidl.notificationFrameCount));
+ legacy.selectedDeviceId = VALUE_OR_RETURN(
+ aidl2legacy_int32_t_audio_port_handle_t(aidl.selectedDeviceId));
+ legacy.sessionId = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_session_t(aidl.sessionId));
+ return legacy;
+}
+
+ConversionResult<media::CreateRecordResponse>
+IAudioFlinger::CreateRecordOutput::toAidl() const {
+ media::CreateRecordResponse aidl;
+ aidl.flags = VALUE_OR_RETURN(legacy2aidl_audio_input_flags_t_int32_t_mask(flags));
+ aidl.frameCount = VALUE_OR_RETURN(convertIntegral<int64_t>(frameCount));
+ aidl.notificationFrameCount = VALUE_OR_RETURN(convertIntegral<int64_t>(notificationFrameCount));
+ aidl.selectedDeviceId = VALUE_OR_RETURN(
+ legacy2aidl_audio_port_handle_t_int32_t(selectedDeviceId));
+ aidl.sessionId = VALUE_OR_RETURN(legacy2aidl_audio_session_t_int32_t(sessionId));
+ aidl.sampleRate = VALUE_OR_RETURN(convertIntegral<int32_t>(sampleRate));
+ aidl.inputId = VALUE_OR_RETURN(legacy2aidl_audio_io_handle_t_int32_t(inputId));
+ aidl.cblk = VALUE_OR_RETURN(legacy2aidl_NullableIMemory_SharedFileRegion(cblk));
+ aidl.buffers = VALUE_OR_RETURN(legacy2aidl_NullableIMemory_SharedFileRegion(buffers));
+ aidl.portId = VALUE_OR_RETURN(legacy2aidl_audio_port_handle_t_int32_t(portId));
+ aidl.audioRecord = audioRecord;
+ return aidl;
+}
+
+ConversionResult<IAudioFlinger::CreateRecordOutput>
+IAudioFlinger::CreateRecordOutput::fromAidl(
+ const media::CreateRecordResponse& aidl) {
+ IAudioFlinger::CreateRecordOutput legacy;
+ legacy.flags = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_input_flags_t_mask(aidl.flags));
+ legacy.frameCount = VALUE_OR_RETURN(convertIntegral<size_t>(aidl.frameCount));
+ legacy.notificationFrameCount = VALUE_OR_RETURN(
+ convertIntegral<size_t>(aidl.notificationFrameCount));
+ legacy.selectedDeviceId = VALUE_OR_RETURN(
+ aidl2legacy_int32_t_audio_port_handle_t(aidl.selectedDeviceId));
+ legacy.sessionId = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_session_t(aidl.sessionId));
+ legacy.sampleRate = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.sampleRate));
+ legacy.inputId = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_io_handle_t(aidl.inputId));
+ legacy.cblk = VALUE_OR_RETURN(aidl2legacy_NullableSharedFileRegion_IMemory(aidl.cblk));
+ legacy.buffers = VALUE_OR_RETURN(aidl2legacy_NullableSharedFileRegion_IMemory(aidl.buffers));
+ legacy.portId = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_port_handle_t(aidl.portId));
+ legacy.audioRecord = aidl.audioRecord;
+ return legacy;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// AudioFlingerClientAdapter
+
+AudioFlingerClientAdapter::AudioFlingerClientAdapter(
+ const sp<media::IAudioFlingerService> delegate) : mDelegate(delegate) {}
+
+status_t AudioFlingerClientAdapter::createTrack(const media::CreateTrackRequest& input,
+ media::CreateTrackResponse& output) {
+ return statusTFromBinderStatus(mDelegate->createTrack(input, &output));
+}
+
+status_t AudioFlingerClientAdapter::createRecord(const media::CreateRecordRequest& input,
+ media::CreateRecordResponse& output) {
+ return statusTFromBinderStatus(mDelegate->createRecord(input, &output));
+}
+
+uint32_t AudioFlingerClientAdapter::sampleRate(audio_io_handle_t ioHandle) const {
+ auto result = [&]() -> ConversionResult<uint32_t> {
+ int32_t ioHandleAidl = VALUE_OR_RETURN(legacy2aidl_audio_io_handle_t_int32_t(ioHandle));
+ int32_t aidlRet;
+ RETURN_IF_ERROR(statusTFromBinderStatus(mDelegate->sampleRate(ioHandleAidl, &aidlRet)));
+ return convertIntegral<uint32_t>(aidlRet);
+ }();
+ // Failure is ignored.
+ return result.value_or(0);
+}
+
+audio_format_t AudioFlingerClientAdapter::format(audio_io_handle_t output) const {
+ auto result = [&]() -> ConversionResult<audio_format_t> {
+ int32_t outputAidl = VALUE_OR_RETURN(legacy2aidl_audio_io_handle_t_int32_t(output));
+ media::audio::common::AudioFormat aidlRet;
+ RETURN_IF_ERROR(statusTFromBinderStatus(mDelegate->format(outputAidl, &aidlRet)));
+ return aidl2legacy_AudioFormat_audio_format_t(aidlRet);
+ }();
+ return result.value_or(AUDIO_FORMAT_INVALID);
+}
+
+size_t AudioFlingerClientAdapter::frameCount(audio_io_handle_t ioHandle) const {
+ auto result = [&]() -> ConversionResult<size_t> {
+ int32_t ioHandleAidl = VALUE_OR_RETURN(legacy2aidl_audio_io_handle_t_int32_t(ioHandle));
+ int64_t aidlRet;
+ RETURN_IF_ERROR(statusTFromBinderStatus(mDelegate->frameCount(ioHandleAidl, &aidlRet)));
+ return convertIntegral<size_t>(aidlRet);
+ }();
+ // Failure is ignored.
+ return result.value_or(0);
+}
+
+uint32_t AudioFlingerClientAdapter::latency(audio_io_handle_t output) const {
+ auto result = [&]() -> ConversionResult<uint32_t> {
+ int32_t outputAidl = VALUE_OR_RETURN(legacy2aidl_audio_io_handle_t_int32_t(output));
+ int32_t aidlRet;
+ RETURN_IF_ERROR(statusTFromBinderStatus(mDelegate->latency(outputAidl, &aidlRet)));
+ return convertIntegral<uint32_t>(aidlRet);
+ }();
+ // Failure is ignored.
+ return result.value_or(0);
+}
+
+status_t AudioFlingerClientAdapter::setMasterVolume(float value) {
+ return statusTFromBinderStatus(mDelegate->setMasterVolume(value));
+}
+
+status_t AudioFlingerClientAdapter::setMasterMute(bool muted) {
+ return statusTFromBinderStatus(mDelegate->setMasterMute(muted));
+}
+
+float AudioFlingerClientAdapter::masterVolume() const {
+ auto result = [&]() -> ConversionResult<float> {
+ float aidlRet;
+ RETURN_IF_ERROR(statusTFromBinderStatus(mDelegate->masterVolume(&aidlRet)));
+ return aidlRet;
+ }();
+ // Failure is ignored.
+ return result.value_or(0.f);
+}
+
+bool AudioFlingerClientAdapter::masterMute() const {
+ auto result = [&]() -> ConversionResult<bool> {
+ bool aidlRet;
+ RETURN_IF_ERROR(statusTFromBinderStatus(mDelegate->masterMute(&aidlRet)));
+ return aidlRet;
+ }();
+ // Failure is ignored.
+ return result.value_or(false);
+}
+
+status_t AudioFlingerClientAdapter::setMasterBalance(float balance) {
+ return statusTFromBinderStatus(mDelegate->setMasterBalance(balance));
+}
+
+status_t AudioFlingerClientAdapter::getMasterBalance(float* balance) const{
+ return statusTFromBinderStatus(mDelegate->getMasterBalance(balance));
+}
+
+status_t AudioFlingerClientAdapter::setStreamVolume(audio_stream_type_t stream, float value,
+ audio_io_handle_t output) {
+ media::AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_stream_type_t_AudioStreamType(stream));
+ int32_t outputAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(output));
+ return statusTFromBinderStatus(mDelegate->setStreamVolume(streamAidl, value, outputAidl));
+}
+
+status_t AudioFlingerClientAdapter::setStreamMute(audio_stream_type_t stream, bool muted) {
+ media::AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_stream_type_t_AudioStreamType(stream));
+ return statusTFromBinderStatus(mDelegate->setStreamMute(streamAidl, muted));
+}
+
+float AudioFlingerClientAdapter::streamVolume(audio_stream_type_t stream,
+ audio_io_handle_t output) const {
+ auto result = [&]() -> ConversionResult<float> {
+ media::AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_stream_type_t_AudioStreamType(stream));
+ int32_t outputAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(output));
+ float aidlRet;
+ RETURN_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->streamVolume(streamAidl, outputAidl, &aidlRet)));
+ return aidlRet;
+ }();
+ // Failure is ignored.
+ return result.value_or(0.f);
+}
+
+bool AudioFlingerClientAdapter::streamMute(audio_stream_type_t stream) const {
+ auto result = [&]() -> ConversionResult<bool> {
+ media::AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_stream_type_t_AudioStreamType(stream));
+ bool aidlRet;
+ RETURN_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->streamMute(streamAidl, &aidlRet)));
+ return aidlRet;
+ }();
+ // Failure is ignored.
+ return result.value_or(false);
+}
+
+status_t AudioFlingerClientAdapter::setMode(audio_mode_t mode) {
+ media::AudioMode modeAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_mode_t_AudioMode(mode));
+ return statusTFromBinderStatus(mDelegate->setMode(modeAidl));
+}
+
+status_t AudioFlingerClientAdapter::setMicMute(bool state) {
+ return statusTFromBinderStatus(mDelegate->setMicMute(state));
+}
+
+bool AudioFlingerClientAdapter::getMicMute() const {
+ auto result = [&]() -> ConversionResult<bool> {
+ bool aidlRet;
+ RETURN_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->getMicMute(&aidlRet)));
+ return aidlRet;
+ }();
+ // Failure is ignored.
+ return result.value_or(false);
+}
+
+void AudioFlingerClientAdapter::setRecordSilenced(audio_port_handle_t portId, bool silenced) {
+ auto result = [&]() -> status_t {
+ int32_t portIdAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_port_handle_t_int32_t(portId));
+ return statusTFromBinderStatus(mDelegate->setRecordSilenced(portIdAidl, silenced));
+ }();
+ // Failure is ignored.
+ (void) result;
+}
+
+status_t AudioFlingerClientAdapter::setParameters(audio_io_handle_t ioHandle,
+ const String8& keyValuePairs) {
+ int32_t ioHandleAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(ioHandle));
+ std::string keyValuePairsAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_String8_string(keyValuePairs));
+ return statusTFromBinderStatus(mDelegate->setParameters(ioHandleAidl, keyValuePairsAidl));
+}
+
+String8 AudioFlingerClientAdapter::getParameters(audio_io_handle_t ioHandle, const String8& keys)
+const {
+ auto result = [&]() -> ConversionResult<String8> {
+ int32_t ioHandleAidl = VALUE_OR_RETURN(legacy2aidl_audio_io_handle_t_int32_t(ioHandle));
+ std::string keysAidl = VALUE_OR_RETURN(legacy2aidl_String8_string(keys));
+ std::string aidlRet;
+ RETURN_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->getParameters(ioHandleAidl, keysAidl, &aidlRet)));
+ return aidl2legacy_string_view_String8(aidlRet);
+ }();
+ // Failure is ignored.
+ return result.value_or(String8());
+}
+
+void AudioFlingerClientAdapter::registerClient(const sp<media::IAudioFlingerClient>& client) {
+ mDelegate->registerClient(client);
+ // Failure is ignored.
+}
+
+size_t AudioFlingerClientAdapter::getInputBufferSize(uint32_t sampleRate, audio_format_t format,
+ audio_channel_mask_t channelMask) const {
+ auto result = [&]() -> ConversionResult<size_t> {
+ int32_t sampleRateAidl = VALUE_OR_RETURN(convertIntegral<int32_t>(sampleRate));
+ media::audio::common::AudioFormat formatAidl = VALUE_OR_RETURN(
+ legacy2aidl_audio_format_t_AudioFormat(format));
+ int32_t channelMaskAidl = VALUE_OR_RETURN(
+ legacy2aidl_audio_channel_mask_t_int32_t(channelMask));
+ int64_t aidlRet;
+ RETURN_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->getInputBufferSize(sampleRateAidl, formatAidl, channelMaskAidl,
+ &aidlRet)));
+ return convertIntegral<size_t>(aidlRet);
+ }();
+ // Failure is ignored.
+ return result.value_or(0);
+}
+
+status_t AudioFlingerClientAdapter::openOutput(const media::OpenOutputRequest& request,
+ media::OpenOutputResponse* response) {
+ return statusTFromBinderStatus(mDelegate->openOutput(request, response));
+}
+
+audio_io_handle_t AudioFlingerClientAdapter::openDuplicateOutput(audio_io_handle_t output1,
+ audio_io_handle_t output2) {
+ auto result = [&]() -> ConversionResult<audio_io_handle_t> {
+ int32_t output1Aidl = VALUE_OR_RETURN(legacy2aidl_audio_io_handle_t_int32_t(output1));
+ int32_t output2Aidl = VALUE_OR_RETURN(legacy2aidl_audio_io_handle_t_int32_t(output2));
+ int32_t aidlRet;
+ RETURN_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->openDuplicateOutput(output1Aidl, output2Aidl, &aidlRet)));
+ return aidl2legacy_int32_t_audio_io_handle_t(aidlRet);
+ }();
+ // Failure is ignored.
+ return result.value_or(0);
+}
+
+status_t AudioFlingerClientAdapter::closeOutput(audio_io_handle_t output) {
+ int32_t outputAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(output));
+ return statusTFromBinderStatus(mDelegate->closeOutput(outputAidl));
+}
+
+status_t AudioFlingerClientAdapter::suspendOutput(audio_io_handle_t output) {
+ int32_t outputAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(output));
+ return statusTFromBinderStatus(mDelegate->suspendOutput(outputAidl));
+}
+
+status_t AudioFlingerClientAdapter::restoreOutput(audio_io_handle_t output) {
+ int32_t outputAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(output));
+ return statusTFromBinderStatus(mDelegate->restoreOutput(outputAidl));
+}
+
+status_t AudioFlingerClientAdapter::openInput(const media::OpenInputRequest& request,
+ media::OpenInputResponse* response) {
+ return statusTFromBinderStatus(mDelegate->openInput(request, response));
+}
+
+status_t AudioFlingerClientAdapter::closeInput(audio_io_handle_t input) {
+ int32_t inputAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(input));
+ return statusTFromBinderStatus(mDelegate->closeInput(inputAidl));
+}
+
+status_t AudioFlingerClientAdapter::invalidateStream(audio_stream_type_t stream) {
+ media::AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_stream_type_t_AudioStreamType(stream));
+ return statusTFromBinderStatus(mDelegate->invalidateStream(streamAidl));
+}
+
+status_t AudioFlingerClientAdapter::setVoiceVolume(float volume) {
+ return statusTFromBinderStatus(mDelegate->setVoiceVolume(volume));
+}
+
+status_t AudioFlingerClientAdapter::getRenderPosition(uint32_t* halFrames, uint32_t* dspFrames,
+ audio_io_handle_t output) const {
+ int32_t outputAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(output));
+ media::RenderPosition aidlRet;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->getRenderPosition(outputAidl, &aidlRet)));
+ if (halFrames != nullptr) {
+ *halFrames = VALUE_OR_RETURN_STATUS(convertIntegral<uint32_t>(aidlRet.halFrames));
+ }
+ if (dspFrames != nullptr) {
+ *dspFrames = VALUE_OR_RETURN_STATUS(convertIntegral<uint32_t>(aidlRet.dspFrames));
+ }
+ return OK;
+}
+
+uint32_t AudioFlingerClientAdapter::getInputFramesLost(audio_io_handle_t ioHandle) const {
+ auto result = [&]() -> ConversionResult<uint32_t> {
+ int32_t ioHandleAidl = VALUE_OR_RETURN(legacy2aidl_audio_io_handle_t_int32_t(ioHandle));
+ int32_t aidlRet;
+ RETURN_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->getInputFramesLost(ioHandleAidl, &aidlRet)));
+ return convertIntegral<uint32_t>(aidlRet);
+ }();
+ // Failure is ignored.
+ return result.value_or(0);
+}
+
+audio_unique_id_t AudioFlingerClientAdapter::newAudioUniqueId(audio_unique_id_use_t use) {
+ auto result = [&]() -> ConversionResult<audio_unique_id_t> {
+ media::AudioUniqueIdUse useAidl = VALUE_OR_RETURN(
+ legacy2aidl_audio_unique_id_use_t_AudioUniqueIdUse(use));
+ int32_t aidlRet;
+ RETURN_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->newAudioUniqueId(useAidl, &aidlRet)));
+ return aidl2legacy_int32_t_audio_unique_id_t(aidlRet);
+ }();
+ return result.value_or(AUDIO_UNIQUE_ID_ALLOCATE);
+}
+
+void AudioFlingerClientAdapter::acquireAudioSessionId(audio_session_t audioSession, pid_t pid,
+ uid_t uid) {
+ [&]() -> status_t {
+ int32_t audioSessionAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_session_t_int32_t(audioSession));
+ int32_t pidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_pid_t_int32_t(pid));
+ int32_t uidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_pid_t_int32_t(uid));
+ return statusTFromBinderStatus(
+ mDelegate->acquireAudioSessionId(audioSessionAidl, pidAidl, uidAidl));
+ }();
+ // Failure is ignored.
+}
+
+void AudioFlingerClientAdapter::releaseAudioSessionId(audio_session_t audioSession, pid_t pid) {
+ [&]() -> status_t {
+ int32_t audioSessionAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_session_t_int32_t(audioSession));
+ int32_t pidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_pid_t_int32_t(pid));
+ return statusTFromBinderStatus(
+ mDelegate->releaseAudioSessionId(audioSessionAidl, pidAidl));
+ }();
+ // Failure is ignored.
+}
+
+status_t AudioFlingerClientAdapter::queryNumberEffects(uint32_t* numEffects) const {
+ int32_t aidlRet;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->queryNumberEffects(&aidlRet)));
+ if (numEffects != nullptr) {
+ *numEffects = VALUE_OR_RETURN_STATUS(convertIntegral<uint32_t>(aidlRet));
+ }
+ return OK;
+}
+
+status_t
+AudioFlingerClientAdapter::queryEffect(uint32_t index, effect_descriptor_t* pDescriptor) const {
+ int32_t indexAidl = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(index));
+ media::EffectDescriptor aidlRet;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->queryEffect(indexAidl, &aidlRet)));
+ if (pDescriptor != nullptr) {
+ *pDescriptor = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_EffectDescriptor_effect_descriptor_t(aidlRet));
+ }
+ return OK;
+}
+
+status_t AudioFlingerClientAdapter::getEffectDescriptor(const effect_uuid_t* pEffectUUID,
+ const effect_uuid_t* pTypeUUID,
+ uint32_t preferredTypeFlag,
+ effect_descriptor_t* pDescriptor) const {
+ media::AudioUuid effectUuidAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_uuid_t_AudioUuid(*pEffectUUID));
+ media::AudioUuid typeUuidAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_uuid_t_AudioUuid(*pTypeUUID));
+ int32_t preferredTypeFlagAidl = VALUE_OR_RETURN_STATUS(
+ convertReinterpret<int32_t>(preferredTypeFlag));
+ media::EffectDescriptor aidlRet;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->getEffectDescriptor(effectUuidAidl, typeUuidAidl, preferredTypeFlagAidl,
+ &aidlRet)));
+ if (pDescriptor != nullptr) {
+ *pDescriptor = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_EffectDescriptor_effect_descriptor_t(aidlRet));
+ }
+ return OK;
+}
+
+status_t AudioFlingerClientAdapter::createEffect(const media::CreateEffectRequest& request,
+ media::CreateEffectResponse* response) {
+ return statusTFromBinderStatus(mDelegate->createEffect(request, response));
+}
+
+status_t
+AudioFlingerClientAdapter::moveEffects(audio_session_t session, audio_io_handle_t srcOutput,
+ audio_io_handle_t dstOutput) {
+ int32_t sessionAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_session_t_int32_t(session));
+ int32_t srcOutputAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_io_handle_t_int32_t(srcOutput));
+ int32_t dstOutputAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_io_handle_t_int32_t(dstOutput));
+ return statusTFromBinderStatus(
+ mDelegate->moveEffects(sessionAidl, srcOutputAidl, dstOutputAidl));
+}
+
+void AudioFlingerClientAdapter::setEffectSuspended(int effectId,
+ audio_session_t sessionId,
+ bool suspended) {
+ [&]() -> status_t {
+ int32_t effectIdAidl = VALUE_OR_RETURN_STATUS(convertReinterpret<int32_t>(effectId));
+ int32_t sessionIdAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_session_t_int32_t(sessionId));
+ return statusTFromBinderStatus(
+ mDelegate->setEffectSuspended(effectIdAidl, sessionIdAidl, suspended));
+ }();
+ // Failure is ignored.
+}
+
+audio_module_handle_t AudioFlingerClientAdapter::loadHwModule(const char* name) {
+ auto result = [&]() -> ConversionResult<audio_module_handle_t> {
+ std::string nameAidl(name);
+ int32_t aidlRet;
+ RETURN_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->loadHwModule(nameAidl, &aidlRet)));
+ return aidl2legacy_int32_t_audio_module_handle_t(aidlRet);
+ }();
+ // Failure is ignored.
+ return result.value_or(0);
+}
+
+uint32_t AudioFlingerClientAdapter::getPrimaryOutputSamplingRate() {
+ auto result = [&]() -> ConversionResult<uint32_t> {
+ int32_t aidlRet;
+ RETURN_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->getPrimaryOutputSamplingRate(&aidlRet)));
+ return convertIntegral<uint32_t>(aidlRet);
+ }();
+ // Failure is ignored.
+ return result.value_or(0);
+}
+
+size_t AudioFlingerClientAdapter::getPrimaryOutputFrameCount() {
+ auto result = [&]() -> ConversionResult<size_t> {
+ int64_t aidlRet;
+ RETURN_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->getPrimaryOutputFrameCount(&aidlRet)));
+ return convertIntegral<size_t>(aidlRet);
+ }();
+ // Failure is ignored.
+ return result.value_or(0);
+}
+
+status_t AudioFlingerClientAdapter::setLowRamDevice(bool isLowRamDevice, int64_t totalMemory) {
+ return statusTFromBinderStatus(mDelegate->setLowRamDevice(isLowRamDevice, totalMemory));
+}
+
+status_t AudioFlingerClientAdapter::getAudioPort(struct audio_port_v7* port) {
+ media::AudioPort portAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_port_v7_AudioPort(*port));
+ media::AudioPort aidlRet;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->getAudioPort(portAidl, &aidlRet)));
+ *port = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioPort_audio_port_v7(aidlRet));
+ return OK;
+}
+
+status_t AudioFlingerClientAdapter::createAudioPatch(const struct audio_patch* patch,
+ audio_patch_handle_t* handle) {
+ media::AudioPatch patchAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_patch_AudioPatch(*patch));
+ int32_t aidlRet;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->createAudioPatch(patchAidl, &aidlRet)));
+ if (handle != nullptr) {
+ *handle = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_patch_handle_t(aidlRet));
+ }
+ return OK;
+}
+
+status_t AudioFlingerClientAdapter::releaseAudioPatch(audio_patch_handle_t handle) {
+ int32_t handleAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_patch_handle_t_int32_t(handle));
+ return statusTFromBinderStatus(mDelegate->releaseAudioPatch(handleAidl));
+}
+
+status_t AudioFlingerClientAdapter::listAudioPatches(unsigned int* num_patches,
+ struct audio_patch* patches) {
+ std::vector<media::AudioPatch> aidlRet;
+ int32_t maxPatches = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(*num_patches));
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->listAudioPatches(maxPatches, &aidlRet)));
+ *num_patches = VALUE_OR_RETURN_STATUS(convertIntegral<unsigned int>(aidlRet.size()));
+ return convertRange(aidlRet.begin(), aidlRet.end(), patches,
+ aidl2legacy_AudioPatch_audio_patch);
+}
+
+status_t AudioFlingerClientAdapter::setAudioPortConfig(const struct audio_port_config* config) {
+ media::AudioPortConfig configAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_port_config_AudioPortConfig(*config));
+ return statusTFromBinderStatus(mDelegate->setAudioPortConfig(configAidl));
+}
+
+audio_hw_sync_t AudioFlingerClientAdapter::getAudioHwSyncForSession(audio_session_t sessionId) {
+ auto result = [&]() -> ConversionResult<audio_hw_sync_t> {
+ int32_t sessionIdAidl = VALUE_OR_RETURN(legacy2aidl_audio_session_t_int32_t(sessionId));
+ int32_t aidlRet;
+ RETURN_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->getAudioHwSyncForSession(sessionIdAidl, &aidlRet)));
+ return aidl2legacy_int32_t_audio_hw_sync_t(aidlRet);
+ }();
+ return result.value_or(AUDIO_HW_SYNC_INVALID);
+}
+
+status_t AudioFlingerClientAdapter::systemReady() {
+ return statusTFromBinderStatus(mDelegate->systemReady());
+}
+
+size_t AudioFlingerClientAdapter::frameCountHAL(audio_io_handle_t ioHandle) const {
+ auto result = [&]() -> ConversionResult<size_t> {
+ int32_t ioHandleAidl = VALUE_OR_RETURN(legacy2aidl_audio_io_handle_t_int32_t(ioHandle));
+ int64_t aidlRet;
+ RETURN_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->frameCountHAL(ioHandleAidl, &aidlRet)));
+ return convertIntegral<size_t>(aidlRet);
+ }();
+ // Failure is ignored.
+ return result.value_or(0);
+}
+
+status_t
+AudioFlingerClientAdapter::getMicrophones(std::vector<media::MicrophoneInfo>* microphones) {
+ std::vector<media::MicrophoneInfoData> aidlRet;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->getMicrophones(&aidlRet)));
+ if (microphones != nullptr) {
+ *microphones = VALUE_OR_RETURN_STATUS(
+ convertContainer<std::vector<media::MicrophoneInfo>>(aidlRet,
+ media::aidl2legacy_MicrophoneInfo));
+ }
+ return OK;
+}
+
+status_t AudioFlingerClientAdapter::setAudioHalPids(const std::vector<pid_t>& pids) {
+ std::vector<int32_t> pidsAidl = VALUE_OR_RETURN_STATUS(
+ convertContainer<std::vector<int32_t>>(pids, legacy2aidl_pid_t_int32_t));
+ return statusTFromBinderStatus(mDelegate->setAudioHalPids(pidsAidl));
+}
+
+status_t AudioFlingerClientAdapter::setVibratorInfos(
+ const std::vector<media::AudioVibratorInfo>& vibratorInfos) {
+ return statusTFromBinderStatus(mDelegate->setVibratorInfos(vibratorInfos));
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// AudioFlingerServerAdapter
+AudioFlingerServerAdapter::AudioFlingerServerAdapter(
+ const sp<AudioFlingerServerAdapter::Delegate>& delegate) : mDelegate(delegate) {}
+
+status_t AudioFlingerServerAdapter::onTransact(uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags) {
+ return mDelegate->onTransactWrapper(static_cast<Delegate::TransactionCode>(code),
+ data,
+ flags,
+ [&] {
+ return BnAudioFlingerService::onTransact(
+ code,
+ data,
+ reply,
+ flags);
+ });
+}
+
+status_t AudioFlingerServerAdapter::dump(int fd, const Vector<String16>& args) {
+ return mDelegate->dump(fd, args);
+}
+
+Status AudioFlingerServerAdapter::createTrack(const media::CreateTrackRequest& request,
+ media::CreateTrackResponse* _aidl_return) {
+ return Status::fromStatusT(mDelegate->createTrack(request, *_aidl_return));
+}
+
+Status AudioFlingerServerAdapter::createRecord(const media::CreateRecordRequest& request,
+ media::CreateRecordResponse* _aidl_return) {
+ return Status::fromStatusT(mDelegate->createRecord(request, *_aidl_return));
+}
+
+Status AudioFlingerServerAdapter::sampleRate(int32_t ioHandle, int32_t* _aidl_return) {
+ audio_io_handle_t ioHandleLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(ioHandle));
+ *_aidl_return = VALUE_OR_RETURN_BINDER(
+ convertIntegral<int32_t>(mDelegate->sampleRate(ioHandleLegacy)));
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::format(int32_t output,
+ media::audio::common::AudioFormat* _aidl_return) {
+ audio_io_handle_t outputLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(output));
+ *_aidl_return = VALUE_OR_RETURN_BINDER(
+ legacy2aidl_audio_format_t_AudioFormat(mDelegate->format(outputLegacy)));
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::frameCount(int32_t ioHandle, int64_t* _aidl_return) {
+ audio_io_handle_t ioHandleLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(ioHandle));
+ *_aidl_return = VALUE_OR_RETURN_BINDER(
+ convertIntegral<int64_t>(mDelegate->frameCount(ioHandleLegacy)));
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::latency(int32_t output, int32_t* _aidl_return) {
+ audio_io_handle_t outputLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(output));
+ *_aidl_return = VALUE_OR_RETURN_BINDER(
+ convertIntegral<int32_t>(mDelegate->latency(outputLegacy)));
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::setMasterVolume(float value) {
+ return Status::fromStatusT(mDelegate->setMasterVolume(value));
+}
+
+Status AudioFlingerServerAdapter::setMasterMute(bool muted) {
+ return Status::fromStatusT(mDelegate->setMasterMute(muted));
+}
+
+Status AudioFlingerServerAdapter::masterVolume(float* _aidl_return) {
+ *_aidl_return = mDelegate->masterVolume();
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::masterMute(bool* _aidl_return) {
+ *_aidl_return = mDelegate->masterMute();
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::setMasterBalance(float balance) {
+ return Status::fromStatusT(mDelegate->setMasterBalance(balance));
+}
+
+Status AudioFlingerServerAdapter::getMasterBalance(float* _aidl_return) {
+ return Status::fromStatusT(mDelegate->getMasterBalance(_aidl_return));
+}
+
+Status AudioFlingerServerAdapter::setStreamVolume(media::AudioStreamType stream, float value,
+ int32_t output) {
+ audio_stream_type_t streamLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_AudioStreamType_audio_stream_type_t(stream));
+ audio_io_handle_t outputLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(output));
+ return Status::fromStatusT(mDelegate->setStreamVolume(streamLegacy, value, outputLegacy));
+}
+
+Status AudioFlingerServerAdapter::setStreamMute(media::AudioStreamType stream, bool muted) {
+ audio_stream_type_t streamLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_AudioStreamType_audio_stream_type_t(stream));
+ return Status::fromStatusT(mDelegate->setStreamMute(streamLegacy, muted));
+}
+
+Status AudioFlingerServerAdapter::streamVolume(media::AudioStreamType stream, int32_t output,
+ float* _aidl_return) {
+ audio_stream_type_t streamLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_AudioStreamType_audio_stream_type_t(stream));
+ audio_io_handle_t outputLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(output));
+ *_aidl_return = mDelegate->streamVolume(streamLegacy, outputLegacy);
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::streamMute(media::AudioStreamType stream, bool* _aidl_return) {
+ audio_stream_type_t streamLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_AudioStreamType_audio_stream_type_t(stream));
+ *_aidl_return = mDelegate->streamMute(streamLegacy);
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::setMode(media::AudioMode mode) {
+ audio_mode_t modeLegacy = VALUE_OR_RETURN_BINDER(aidl2legacy_AudioMode_audio_mode_t(mode));
+ return Status::fromStatusT(mDelegate->setMode(modeLegacy));
+}
+
+Status AudioFlingerServerAdapter::setMicMute(bool state) {
+ return Status::fromStatusT(mDelegate->setMicMute(state));
+}
+
+Status AudioFlingerServerAdapter::getMicMute(bool* _aidl_return) {
+ *_aidl_return = mDelegate->getMicMute();
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::setRecordSilenced(int32_t portId, bool silenced) {
+ audio_port_handle_t portIdLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_port_handle_t(portId));
+ mDelegate->setRecordSilenced(portIdLegacy, silenced);
+ return Status::ok();
+}
+
+Status
+AudioFlingerServerAdapter::setParameters(int32_t ioHandle, const std::string& keyValuePairs) {
+ audio_io_handle_t ioHandleLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(ioHandle));
+ String8 keyValuePairsLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_string_view_String8(keyValuePairs));
+ return Status::fromStatusT(mDelegate->setParameters(ioHandleLegacy, keyValuePairsLegacy));
+}
+
+Status AudioFlingerServerAdapter::getParameters(int32_t ioHandle, const std::string& keys,
+ std::string* _aidl_return) {
+ audio_io_handle_t ioHandleLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(ioHandle));
+ String8 keysLegacy = VALUE_OR_RETURN_BINDER(aidl2legacy_string_view_String8(keys));
+ *_aidl_return = VALUE_OR_RETURN_BINDER(
+ legacy2aidl_String8_string(mDelegate->getParameters(ioHandleLegacy, keysLegacy)));
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::registerClient(const sp<media::IAudioFlingerClient>& client) {
+ mDelegate->registerClient(client);
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::getInputBufferSize(int32_t sampleRate,
+ media::audio::common::AudioFormat format,
+ int32_t channelMask, int64_t* _aidl_return) {
+ uint32_t sampleRateLegacy = VALUE_OR_RETURN_BINDER(convertIntegral<uint32_t>(sampleRate));
+ audio_format_t formatLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_AudioFormat_audio_format_t(format));
+ audio_channel_mask_t channelMaskLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_channel_mask_t(channelMask));
+ size_t size = mDelegate->getInputBufferSize(sampleRateLegacy, formatLegacy, channelMaskLegacy);
+ *_aidl_return = VALUE_OR_RETURN_BINDER(convertIntegral<int64_t>(size));
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::openOutput(const media::OpenOutputRequest& request,
+ media::OpenOutputResponse* _aidl_return) {
+ return Status::fromStatusT(mDelegate->openOutput(request, _aidl_return));
+}
+
+Status AudioFlingerServerAdapter::openDuplicateOutput(int32_t output1, int32_t output2,
+ int32_t* _aidl_return) {
+ audio_io_handle_t output1Legacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(output1));
+ audio_io_handle_t output2Legacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(output2));
+ audio_io_handle_t result = mDelegate->openDuplicateOutput(output1Legacy, output2Legacy);
+ *_aidl_return = VALUE_OR_RETURN_BINDER(legacy2aidl_audio_io_handle_t_int32_t(result));
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::closeOutput(int32_t output) {
+ audio_io_handle_t outputLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(output));
+ return Status::fromStatusT(mDelegate->closeOutput(outputLegacy));
+}
+
+Status AudioFlingerServerAdapter::suspendOutput(int32_t output) {
+ audio_io_handle_t outputLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(output));
+ return Status::fromStatusT(mDelegate->suspendOutput(outputLegacy));
+}
+
+Status AudioFlingerServerAdapter::restoreOutput(int32_t output) {
+ audio_io_handle_t outputLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(output));
+ return Status::fromStatusT(mDelegate->restoreOutput(outputLegacy));
+}
+
+Status AudioFlingerServerAdapter::openInput(const media::OpenInputRequest& request,
+ media::OpenInputResponse* _aidl_return) {
+ return Status::fromStatusT(mDelegate->openInput(request, _aidl_return));
+}
+
+Status AudioFlingerServerAdapter::closeInput(int32_t input) {
+ audio_io_handle_t inputLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(input));
+ return Status::fromStatusT(mDelegate->closeInput(inputLegacy));
+}
+
+Status AudioFlingerServerAdapter::invalidateStream(media::AudioStreamType stream) {
+ audio_stream_type_t streamLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_AudioStreamType_audio_stream_type_t(stream));
+ return Status::fromStatusT(mDelegate->invalidateStream(streamLegacy));
+}
+
+Status AudioFlingerServerAdapter::setVoiceVolume(float volume) {
+ return Status::fromStatusT(mDelegate->setVoiceVolume(volume));
+}
+
+Status
+AudioFlingerServerAdapter::getRenderPosition(int32_t output, media::RenderPosition* _aidl_return) {
+ audio_io_handle_t outputLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(output));
+ uint32_t halFramesLegacy;
+ uint32_t dspFramesLegacy;
+ RETURN_BINDER_IF_ERROR(
+ mDelegate->getRenderPosition(&halFramesLegacy, &dspFramesLegacy, outputLegacy));
+ _aidl_return->halFrames = VALUE_OR_RETURN_BINDER(convertIntegral<int32_t>(halFramesLegacy));
+ _aidl_return->dspFrames = VALUE_OR_RETURN_BINDER(convertIntegral<int32_t>(dspFramesLegacy));
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::getInputFramesLost(int32_t ioHandle, int32_t* _aidl_return) {
+ audio_io_handle_t ioHandleLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(ioHandle));
+ uint32_t result = mDelegate->getInputFramesLost(ioHandleLegacy);
+ *_aidl_return = VALUE_OR_RETURN_BINDER(convertIntegral<int32_t>(result));
+ return Status::ok();
+}
+
+Status
+AudioFlingerServerAdapter::newAudioUniqueId(media::AudioUniqueIdUse use, int32_t* _aidl_return) {
+ audio_unique_id_use_t useLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_AudioUniqueIdUse_audio_unique_id_use_t(use));
+ audio_unique_id_t result = mDelegate->newAudioUniqueId(useLegacy);
+ *_aidl_return = VALUE_OR_RETURN_BINDER(legacy2aidl_audio_unique_id_t_int32_t(result));
+ return Status::ok();
+}
+
+Status
+AudioFlingerServerAdapter::acquireAudioSessionId(int32_t audioSession, int32_t pid, int32_t uid) {
+ audio_session_t audioSessionLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_session_t(audioSession));
+ pid_t pidLegacy = VALUE_OR_RETURN_BINDER(aidl2legacy_int32_t_pid_t(pid));
+ uid_t uidLegacy = VALUE_OR_RETURN_BINDER(aidl2legacy_int32_t_uid_t(uid));
+ mDelegate->acquireAudioSessionId(audioSessionLegacy, pidLegacy, uidLegacy);
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::releaseAudioSessionId(int32_t audioSession, int32_t pid) {
+ audio_session_t audioSessionLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_session_t(audioSession));
+ pid_t pidLegacy = VALUE_OR_RETURN_BINDER(aidl2legacy_int32_t_pid_t(pid));
+ mDelegate->releaseAudioSessionId(audioSessionLegacy, pidLegacy);
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::queryNumberEffects(int32_t* _aidl_return) {
+ uint32_t result;
+ RETURN_BINDER_IF_ERROR(mDelegate->queryNumberEffects(&result));
+ *_aidl_return = VALUE_OR_RETURN_BINDER(convertIntegral<uint32_t>(result));
+ return Status::ok();
+}
+
+Status
+AudioFlingerServerAdapter::queryEffect(int32_t index, media::EffectDescriptor* _aidl_return) {
+ uint32_t indexLegacy = VALUE_OR_RETURN_BINDER(convertIntegral<uint32_t>(index));
+ effect_descriptor_t result;
+ RETURN_BINDER_IF_ERROR(mDelegate->queryEffect(indexLegacy, &result));
+ *_aidl_return = VALUE_OR_RETURN_BINDER(
+ legacy2aidl_effect_descriptor_t_EffectDescriptor(result));
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::getEffectDescriptor(const media::AudioUuid& effectUUID,
+ const media::AudioUuid& typeUUID,
+ int32_t preferredTypeFlag,
+ media::EffectDescriptor* _aidl_return) {
+ effect_uuid_t effectUuidLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_AudioUuid_audio_uuid_t(effectUUID));
+ effect_uuid_t typeUuidLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_AudioUuid_audio_uuid_t(typeUUID));
+ uint32_t preferredTypeFlagLegacy = VALUE_OR_RETURN_BINDER(
+ convertReinterpret<uint32_t>(preferredTypeFlag));
+ effect_descriptor_t result;
+ RETURN_BINDER_IF_ERROR(mDelegate->getEffectDescriptor(&effectUuidLegacy, &typeUuidLegacy,
+ preferredTypeFlagLegacy, &result));
+ *_aidl_return = VALUE_OR_RETURN_BINDER(
+ legacy2aidl_effect_descriptor_t_EffectDescriptor(result));
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::createEffect(const media::CreateEffectRequest& request,
+ media::CreateEffectResponse* _aidl_return) {
+ return Status::fromStatusT(mDelegate->createEffect(request, _aidl_return));
+}
+
+Status
+AudioFlingerServerAdapter::moveEffects(int32_t session, int32_t srcOutput, int32_t dstOutput) {
+ audio_session_t sessionLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_session_t(session));
+ audio_io_handle_t srcOutputLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(srcOutput));
+ audio_io_handle_t dstOutputLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(dstOutput));
+ return Status::fromStatusT(
+ mDelegate->moveEffects(sessionLegacy, srcOutputLegacy, dstOutputLegacy));
+}
+
+Status AudioFlingerServerAdapter::setEffectSuspended(int32_t effectId, int32_t sessionId,
+ bool suspended) {
+ int effectIdLegacy = VALUE_OR_RETURN_BINDER(convertReinterpret<int>(effectId));
+ audio_session_t sessionIdLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_session_t(sessionId));
+ mDelegate->setEffectSuspended(effectIdLegacy, sessionIdLegacy, suspended);
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::loadHwModule(const std::string& name, int32_t* _aidl_return) {
+ audio_module_handle_t result = mDelegate->loadHwModule(name.c_str());
+ *_aidl_return = VALUE_OR_RETURN_BINDER(legacy2aidl_audio_module_handle_t_int32_t(result));
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::getPrimaryOutputSamplingRate(int32_t* _aidl_return) {
+ *_aidl_return = VALUE_OR_RETURN_BINDER(
+ convertIntegral<int32_t>(mDelegate->getPrimaryOutputSamplingRate()));
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::getPrimaryOutputFrameCount(int64_t* _aidl_return) {
+ *_aidl_return = VALUE_OR_RETURN_BINDER(
+ convertIntegral<int64_t>(mDelegate->getPrimaryOutputFrameCount()));
+ return Status::ok();
+
+}
+
+Status AudioFlingerServerAdapter::setLowRamDevice(bool isLowRamDevice, int64_t totalMemory) {
+ return Status::fromStatusT(mDelegate->setLowRamDevice(isLowRamDevice, totalMemory));
+}
+
+Status AudioFlingerServerAdapter::getAudioPort(const media::AudioPort& port,
+ media::AudioPort* _aidl_return) {
+ audio_port_v7 portLegacy = VALUE_OR_RETURN_BINDER(aidl2legacy_AudioPort_audio_port_v7(port));
+ RETURN_BINDER_IF_ERROR(mDelegate->getAudioPort(&portLegacy));
+ *_aidl_return = VALUE_OR_RETURN_BINDER(legacy2aidl_audio_port_v7_AudioPort(portLegacy));
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::createAudioPatch(const media::AudioPatch& patch,
+ int32_t* _aidl_return) {
+ audio_patch patchLegacy = VALUE_OR_RETURN_BINDER(aidl2legacy_AudioPatch_audio_patch(patch));
+ audio_patch_handle_t handleLegacy;
+ RETURN_BINDER_IF_ERROR(mDelegate->createAudioPatch(&patchLegacy, &handleLegacy));
+ *_aidl_return = VALUE_OR_RETURN_BINDER(legacy2aidl_audio_patch_handle_t_int32_t(handleLegacy));
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::releaseAudioPatch(int32_t handle) {
+ audio_patch_handle_t handleLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_patch_handle_t(handle));
+ return Status::fromStatusT(mDelegate->releaseAudioPatch(handleLegacy));
+}
+
+Status AudioFlingerServerAdapter::listAudioPatches(int32_t maxCount,
+ std::vector<media::AudioPatch>* _aidl_return) {
+ unsigned int count = VALUE_OR_RETURN_BINDER(convertIntegral<unsigned int>(maxCount));
+ count = std::min(count, static_cast<unsigned int>(MAX_ITEMS_PER_LIST));
+ std::unique_ptr<audio_patch[]> patchesLegacy(new audio_patch[count]);
+ RETURN_BINDER_IF_ERROR(mDelegate->listAudioPatches(&count, patchesLegacy.get()));
+ RETURN_BINDER_IF_ERROR(convertRange(&patchesLegacy[0],
+ &patchesLegacy[count],
+ std::back_inserter(*_aidl_return),
+ legacy2aidl_audio_patch_AudioPatch));
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::setAudioPortConfig(const media::AudioPortConfig& config) {
+ audio_port_config configLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_AudioPortConfig_audio_port_config(config));
+ return Status::fromStatusT(mDelegate->setAudioPortConfig(&configLegacy));
+}
+
+Status AudioFlingerServerAdapter::getAudioHwSyncForSession(int32_t sessionId,
+ int32_t* _aidl_return) {
+ audio_session_t sessionIdLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_session_t(sessionId));
+ audio_hw_sync_t result = mDelegate->getAudioHwSyncForSession(sessionIdLegacy);
+ *_aidl_return = VALUE_OR_RETURN_BINDER(legacy2aidl_audio_hw_sync_t_int32_t(result));
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::systemReady() {
+ return Status::fromStatusT(mDelegate->systemReady());
+}
+
+Status AudioFlingerServerAdapter::frameCountHAL(int32_t ioHandle, int64_t* _aidl_return) {
+ audio_io_handle_t ioHandleLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(ioHandle));
+ size_t result = mDelegate->frameCountHAL(ioHandleLegacy);
+ *_aidl_return = VALUE_OR_RETURN_BINDER(convertIntegral<int64_t>(result));
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::getMicrophones(
+ std::vector<media::MicrophoneInfoData>* _aidl_return) {
+ std::vector<media::MicrophoneInfo> resultLegacy;
+ RETURN_BINDER_IF_ERROR(mDelegate->getMicrophones(&resultLegacy));
+ *_aidl_return = VALUE_OR_RETURN_BINDER(convertContainer<std::vector<media::MicrophoneInfoData>>(
+ resultLegacy, media::legacy2aidl_MicrophoneInfo));
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::setAudioHalPids(const std::vector<int32_t>& pids) {
+ std::vector<pid_t> pidsLegacy = VALUE_OR_RETURN_BINDER(
+ convertContainer<std::vector<pid_t>>(pids, aidl2legacy_int32_t_pid_t));
+ RETURN_BINDER_IF_ERROR(mDelegate->setAudioHalPids(pidsLegacy));
+ return Status::ok();
+}
+
+Status AudioFlingerServerAdapter::setVibratorInfos(
+ const std::vector<media::AudioVibratorInfo>& vibratorInfos) {
+ return Status::fromStatusT(mDelegate->setVibratorInfos(vibratorInfos));
+}
} // namespace android
diff --git a/media/libaudioclient/IAudioFlingerClient.cpp b/media/libaudioclient/IAudioFlingerClient.cpp
deleted file mode 100644
index 47eb7dc..0000000
--- a/media/libaudioclient/IAudioFlingerClient.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 "IAudioFlingerClient"
-#include <utils/Log.h>
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-
-#include <media/IAudioFlingerClient.h>
-#include <media/AudioSystem.h>
-
-namespace android {
-
-enum {
- IO_CONFIG_CHANGED = IBinder::FIRST_CALL_TRANSACTION
-};
-
-class BpAudioFlingerClient : public BpInterface<IAudioFlingerClient>
-{
-public:
- explicit BpAudioFlingerClient(const sp<IBinder>& impl)
- : BpInterface<IAudioFlingerClient>(impl)
- {
- }
-
- void ioConfigChanged(audio_io_config_event event, const sp<AudioIoDescriptor>& ioDesc)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioFlingerClient::getInterfaceDescriptor());
- data.writeInt32(event);
- data.writeInt32((int32_t)ioDesc->mIoHandle);
- data.write(&ioDesc->mPatch, sizeof(struct audio_patch));
- data.writeInt32(ioDesc->mSamplingRate);
- data.writeInt32(ioDesc->mFormat);
- data.writeInt32(ioDesc->mChannelMask);
- data.writeInt64(ioDesc->mFrameCount);
- data.writeInt64(ioDesc->mFrameCountHAL);
- data.writeInt32(ioDesc->mLatency);
- data.writeInt32(ioDesc->mPortId);
- remote()->transact(IO_CONFIG_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
- }
-};
-
-IMPLEMENT_META_INTERFACE(AudioFlingerClient, "android.media.IAudioFlingerClient");
-
-// ----------------------------------------------------------------------
-
-status_t BnAudioFlingerClient::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch (code) {
- case IO_CONFIG_CHANGED: {
- CHECK_INTERFACE(IAudioFlingerClient, data, reply);
- audio_io_config_event event = (audio_io_config_event)data.readInt32();
- sp<AudioIoDescriptor> ioDesc = new AudioIoDescriptor();
- ioDesc->mIoHandle = (audio_io_handle_t) data.readInt32();
- data.read(&ioDesc->mPatch, sizeof(struct audio_patch));
- ioDesc->mSamplingRate = data.readInt32();
- ioDesc->mFormat = (audio_format_t) data.readInt32();
- ioDesc->mChannelMask = (audio_channel_mask_t) data.readInt32();
- ioDesc->mFrameCount = data.readInt64();
- ioDesc->mFrameCountHAL = data.readInt64();
- ioDesc->mLatency = data.readInt32();
- ioDesc->mPortId = data.readInt32();
- ioConfigChanged(event, ioDesc);
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-// ----------------------------------------------------------------------------
-
-} // namespace android
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
deleted file mode 100644
index 81f9dff..0000000
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ /dev/null
@@ -1,2965 +0,0 @@
-/*
-**
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT 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 "IAudioPolicyService"
-#include <utils/Log.h>
-
-#include <stdint.h>
-#include <math.h>
-#include <sys/types.h>
-
-#include <android/media/ICaptureStateListener.h>
-#include <binder/IPCThreadState.h>
-#include <binder/Parcel.h>
-#include <media/AudioEffect.h>
-#include <media/AudioSanitizer.h>
-#include <media/IAudioPolicyService.h>
-#include <mediautils/ServiceUtilities.h>
-#include <mediautils/TimeCheck.h>
-#include <system/audio.h>
-
-namespace android {
-
-using media::ICaptureStateListener;
-
-enum {
- SET_DEVICE_CONNECTION_STATE = IBinder::FIRST_CALL_TRANSACTION,
- GET_DEVICE_CONNECTION_STATE,
- HANDLE_DEVICE_CONFIG_CHANGE,
- SET_PHONE_STATE,
- SET_RINGER_MODE, // reserved, no longer used
- SET_FORCE_USE,
- GET_FORCE_USE,
- GET_OUTPUT,
- START_OUTPUT,
- STOP_OUTPUT,
- RELEASE_OUTPUT,
- GET_INPUT_FOR_ATTR,
- START_INPUT,
- STOP_INPUT,
- RELEASE_INPUT,
- INIT_STREAM_VOLUME,
- SET_STREAM_VOLUME,
- GET_STREAM_VOLUME,
- SET_VOLUME_ATTRIBUTES,
- GET_VOLUME_ATTRIBUTES,
- GET_MIN_VOLUME_FOR_ATTRIBUTES,
- GET_MAX_VOLUME_FOR_ATTRIBUTES,
- GET_STRATEGY_FOR_STREAM,
- GET_OUTPUT_FOR_EFFECT,
- REGISTER_EFFECT,
- UNREGISTER_EFFECT,
- IS_STREAM_ACTIVE,
- IS_SOURCE_ACTIVE,
- GET_DEVICES_FOR_STREAM,
- QUERY_DEFAULT_PRE_PROCESSING,
- SET_EFFECT_ENABLED,
- IS_STREAM_ACTIVE_REMOTELY,
- IS_OFFLOAD_SUPPORTED,
- IS_DIRECT_OUTPUT_SUPPORTED,
- LIST_AUDIO_PORTS,
- GET_AUDIO_PORT,
- CREATE_AUDIO_PATCH,
- RELEASE_AUDIO_PATCH,
- LIST_AUDIO_PATCHES,
- SET_AUDIO_PORT_CONFIG,
- REGISTER_CLIENT,
- GET_OUTPUT_FOR_ATTR,
- ACQUIRE_SOUNDTRIGGER_SESSION,
- RELEASE_SOUNDTRIGGER_SESSION,
- GET_PHONE_STATE,
- REGISTER_POLICY_MIXES,
- START_AUDIO_SOURCE,
- STOP_AUDIO_SOURCE,
- SET_AUDIO_PORT_CALLBACK_ENABLED,
- SET_AUDIO_VOLUME_GROUP_CALLBACK_ENABLED,
- SET_MASTER_MONO,
- GET_MASTER_MONO,
- GET_STREAM_VOLUME_DB,
- GET_SURROUND_FORMATS,
- SET_SURROUND_FORMAT_ENABLED,
- ADD_STREAM_DEFAULT_EFFECT,
- REMOVE_STREAM_DEFAULT_EFFECT,
- ADD_SOURCE_DEFAULT_EFFECT,
- REMOVE_SOURCE_DEFAULT_EFFECT,
- SET_ASSISTANT_UID,
- SET_A11Y_SERVICES_UIDS,
- IS_HAPTIC_PLAYBACK_SUPPORTED,
- SET_UID_DEVICE_AFFINITY,
- REMOVE_UID_DEVICE_AFFINITY,
- SET_USERID_DEVICE_AFFINITY,
- REMOVE_USERID_DEVICE_AFFINITY,
- GET_OFFLOAD_FORMATS_A2DP,
- LIST_AUDIO_PRODUCT_STRATEGIES,
- GET_STRATEGY_FOR_ATTRIBUTES,
- LIST_AUDIO_VOLUME_GROUPS,
- GET_VOLUME_GROUP_FOR_ATTRIBUTES,
- SET_SUPPORTED_SYSTEM_USAGES,
- SET_ALLOWED_CAPTURE_POLICY,
- MOVE_EFFECTS_TO_IO,
- SET_RTT_ENABLED,
- IS_CALL_SCREEN_MODE_SUPPORTED,
- SET_DEVICES_ROLE_FOR_PRODUCT_STRATEGY,
- REMOVE_DEVICES_ROLE_FOR_PRODUCT_STRATEGY,
- GET_DEVICES_FOR_ROLE_AND_PRODUCT_STRATEGY,
- GET_DEVICES_FOR_ATTRIBUTES,
- AUDIO_MODULES_UPDATED, // oneway
- SET_CURRENT_IME_UID,
- REGISTER_SOUNDTRIGGER_CAPTURE_STATE_LISTENER,
- SET_DEVICES_ROLE_FOR_CAPTURE_PRESET,
- ADD_DEVICES_ROLE_FOR_CAPTURE_PRESET,
- REMOVE_DEVICES_ROLE_FOR_CAPTURE_PRESET,
- CLEAR_DEVICES_ROLE_FOR_CAPTURE_PRESET,
- GET_DEVICES_FOR_ROLE_AND_CAPTURE_PRESET,
-};
-
-#define MAX_ITEMS_PER_LIST 1024
-
-class BpAudioPolicyService : public BpInterface<IAudioPolicyService>
-{
-public:
- explicit BpAudioPolicyService(const sp<IBinder>& impl)
- : BpInterface<IAudioPolicyService>(impl)
- {
- }
-
- virtual status_t setDeviceConnectionState(
- audio_devices_t device,
- audio_policy_dev_state_t state,
- const char *device_address,
- const char *device_name,
- audio_format_t encodedFormat)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(static_cast <uint32_t>(device));
- data.writeInt32(static_cast <uint32_t>(state));
- data.writeCString(device_address);
- data.writeCString(device_name);
- data.writeInt32(static_cast <uint32_t>(encodedFormat));
- remote()->transact(SET_DEVICE_CONNECTION_STATE, data, &reply);
- return static_cast <status_t> (reply.readInt32());
- }
-
- virtual audio_policy_dev_state_t getDeviceConnectionState(
- audio_devices_t device,
- const char *device_address)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(static_cast <uint32_t>(device));
- data.writeCString(device_address);
- remote()->transact(GET_DEVICE_CONNECTION_STATE, data, &reply);
- return static_cast <audio_policy_dev_state_t>(reply.readInt32());
- }
-
- virtual status_t handleDeviceConfigChange(audio_devices_t device,
- const char *device_address,
- const char *device_name,
- audio_format_t encodedFormat)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(static_cast <uint32_t>(device));
- data.writeCString(device_address);
- data.writeCString(device_name);
- data.writeInt32(static_cast <uint32_t>(encodedFormat));
- remote()->transact(HANDLE_DEVICE_CONFIG_CHANGE, data, &reply);
- return static_cast <status_t> (reply.readInt32());
- }
-
- virtual status_t setPhoneState(audio_mode_t state, uid_t uid)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(state);
- data.writeInt32(uid);
- remote()->transact(SET_PHONE_STATE, data, &reply);
- return static_cast <status_t> (reply.readInt32());
- }
-
- virtual status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(static_cast <uint32_t>(usage));
- data.writeInt32(static_cast <uint32_t>(config));
- remote()->transact(SET_FORCE_USE, data, &reply);
- return static_cast <status_t> (reply.readInt32());
- }
-
- virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(static_cast <uint32_t>(usage));
- remote()->transact(GET_FORCE_USE, data, &reply);
- return static_cast <audio_policy_forced_cfg_t> (reply.readInt32());
- }
-
- virtual audio_io_handle_t getOutput(audio_stream_type_t stream)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(static_cast <uint32_t>(stream));
- remote()->transact(GET_OUTPUT, data, &reply);
- return static_cast <audio_io_handle_t> (reply.readInt32());
- }
-
- status_t getOutputForAttr(audio_attributes_t *attr,
- audio_io_handle_t *output,
- audio_session_t session,
- audio_stream_type_t *stream,
- pid_t pid,
- uid_t uid,
- const audio_config_t *config,
- audio_output_flags_t flags,
- audio_port_handle_t *selectedDeviceId,
- audio_port_handle_t *portId,
- std::vector<audio_io_handle_t> *secondaryOutputs) override
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- if (attr == nullptr) {
- ALOGE("%s NULL audio attributes", __func__);
- return BAD_VALUE;
- }
- if (output == nullptr) {
- ALOGE("%s NULL output - shouldn't happen", __func__);
- return BAD_VALUE;
- }
- if (selectedDeviceId == nullptr) {
- ALOGE("%s NULL selectedDeviceId - shouldn't happen", __func__);
- return BAD_VALUE;
- }
- if (portId == nullptr) {
- ALOGE("%s NULL portId - shouldn't happen", __func__);
- return BAD_VALUE;
- }
- if (secondaryOutputs == nullptr) {
- ALOGE("%s NULL secondaryOutputs - shouldn't happen", __func__);
- return BAD_VALUE;
- }
- data.write(attr, sizeof(audio_attributes_t));
- data.writeInt32(session);
- if (stream == NULL) {
- data.writeInt32(0);
- } else {
- data.writeInt32(1);
- data.writeInt32(*stream);
- }
- data.writeInt32(pid);
- data.writeInt32(uid);
- data.write(config, sizeof(audio_config_t));
- data.writeInt32(static_cast <uint32_t>(flags));
- data.writeInt32(*selectedDeviceId);
- data.writeInt32(*portId);
- status_t status = remote()->transact(GET_OUTPUT_FOR_ATTR, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = (status_t)reply.readInt32();
- if (status != NO_ERROR) {
- return status;
- }
- status = (status_t)reply.read(&attr, sizeof(audio_attributes_t));
- if (status != NO_ERROR) {
- return status;
- }
- *output = (audio_io_handle_t)reply.readInt32();
- audio_stream_type_t lStream = (audio_stream_type_t)reply.readInt32();
- if (stream != NULL) {
- *stream = lStream;
- }
- *selectedDeviceId = (audio_port_handle_t)reply.readInt32();
- *portId = (audio_port_handle_t)reply.readInt32();
- secondaryOutputs->resize(reply.readInt32());
- return reply.read(secondaryOutputs->data(),
- secondaryOutputs->size() * sizeof(audio_io_handle_t));
- }
-
- virtual status_t startOutput(audio_port_handle_t portId)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32((int32_t)portId);
- remote()->transact(START_OUTPUT, data, &reply);
- return static_cast <status_t> (reply.readInt32());
- }
-
- virtual status_t stopOutput(audio_port_handle_t portId)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32((int32_t)portId);
- remote()->transact(STOP_OUTPUT, data, &reply);
- return static_cast <status_t> (reply.readInt32());
- }
-
- virtual void releaseOutput(audio_port_handle_t portId)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32((int32_t)portId);
- remote()->transact(RELEASE_OUTPUT, data, &reply);
- }
-
- virtual status_t getInputForAttr(const audio_attributes_t *attr,
- audio_io_handle_t *input,
- audio_unique_id_t riid,
- audio_session_t session,
- pid_t pid,
- uid_t uid,
- const String16& opPackageName,
- const audio_config_base_t *config,
- audio_input_flags_t flags,
- audio_port_handle_t *selectedDeviceId,
- audio_port_handle_t *portId)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- if (attr == NULL) {
- ALOGE("getInputForAttr NULL attr - shouldn't happen");
- return BAD_VALUE;
- }
- if (input == NULL) {
- ALOGE("getInputForAttr NULL input - shouldn't happen");
- return BAD_VALUE;
- }
- if (selectedDeviceId == NULL) {
- ALOGE("getInputForAttr NULL selectedDeviceId - shouldn't happen");
- return BAD_VALUE;
- }
- if (portId == NULL) {
- ALOGE("getInputForAttr NULL portId - shouldn't happen");
- return BAD_VALUE;
- }
-
- data.write(attr, sizeof(audio_attributes_t));
- data.writeInt32(*input);
- data.writeInt32(riid);
- data.writeInt32(session);
- data.writeInt32(pid);
- data.writeInt32(uid);
- data.writeString16(opPackageName);
- data.write(config, sizeof(audio_config_base_t));
- data.writeInt32(flags);
- data.writeInt32(*selectedDeviceId);
- data.writeInt32(*portId);
- status_t status = remote()->transact(GET_INPUT_FOR_ATTR, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = reply.readInt32();
- if (status != NO_ERROR) {
- return status;
- }
- *input = (audio_io_handle_t)reply.readInt32();
- *selectedDeviceId = (audio_port_handle_t)reply.readInt32();
- *portId = (audio_port_handle_t)reply.readInt32();
- return NO_ERROR;
- }
-
- virtual status_t startInput(audio_port_handle_t portId)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(portId);
- remote()->transact(START_INPUT, data, &reply);
- status_t status = static_cast <status_t> (reply.readInt32());
- return status;
- }
-
- virtual status_t stopInput(audio_port_handle_t portId)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(portId);
- remote()->transact(STOP_INPUT, data, &reply);
- return static_cast <status_t> (reply.readInt32());
- }
-
- virtual void releaseInput(audio_port_handle_t portId)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(portId);
- remote()->transact(RELEASE_INPUT, data, &reply);
- }
-
- virtual status_t initStreamVolume(audio_stream_type_t stream,
- int indexMin,
- int indexMax)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(static_cast <uint32_t>(stream));
- data.writeInt32(indexMin);
- data.writeInt32(indexMax);
- remote()->transact(INIT_STREAM_VOLUME, data, &reply);
- return static_cast <status_t> (reply.readInt32());
- }
-
- virtual status_t setStreamVolumeIndex(audio_stream_type_t stream,
- int index,
- audio_devices_t device)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(static_cast <uint32_t>(stream));
- data.writeInt32(index);
- data.writeInt32(static_cast <uint32_t>(device));
- remote()->transact(SET_STREAM_VOLUME, data, &reply);
- return static_cast <status_t> (reply.readInt32());
- }
-
- virtual status_t getStreamVolumeIndex(audio_stream_type_t stream,
- int *index,
- audio_devices_t device)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(static_cast <uint32_t>(stream));
- data.writeInt32(static_cast <uint32_t>(device));
-
- remote()->transact(GET_STREAM_VOLUME, data, &reply);
- int lIndex = reply.readInt32();
- if (index) *index = lIndex;
- return static_cast <status_t> (reply.readInt32());
- }
-
- virtual status_t setVolumeIndexForAttributes(const audio_attributes_t &attr, int index,
- audio_devices_t device)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.write(&attr, sizeof(audio_attributes_t));
- data.writeInt32(index);
- data.writeInt32(static_cast <uint32_t>(device));
- status_t status = remote()->transact(SET_VOLUME_ATTRIBUTES, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- return static_cast <status_t> (reply.readInt32());
- }
- virtual status_t getVolumeIndexForAttributes(const audio_attributes_t &attr, int &index,
- audio_devices_t device)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.write(&attr, sizeof(audio_attributes_t));
- data.writeInt32(static_cast <uint32_t>(device));
- status_t status = remote()->transact(GET_VOLUME_ATTRIBUTES, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = static_cast <status_t> (reply.readInt32());
- if (status != NO_ERROR) {
- return status;
- }
- index = reply.readInt32();
- return NO_ERROR;
- }
- virtual status_t getMinVolumeIndexForAttributes(const audio_attributes_t &attr, int &index)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.write(&attr, sizeof(audio_attributes_t));
- status_t status = remote()->transact(GET_MIN_VOLUME_FOR_ATTRIBUTES, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = static_cast <status_t> (reply.readInt32());
- if (status != NO_ERROR) {
- return status;
- }
- index = reply.readInt32();
- return NO_ERROR;
- }
- virtual status_t getMaxVolumeIndexForAttributes(const audio_attributes_t &attr, int &index)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.write(&attr, sizeof(audio_attributes_t));
- status_t status = remote()->transact(GET_MAX_VOLUME_FOR_ATTRIBUTES, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = static_cast <status_t> (reply.readInt32());
- if (status != NO_ERROR) {
- return status;
- }
- index = reply.readInt32();
- return NO_ERROR;
- }
- virtual uint32_t getStrategyForStream(audio_stream_type_t stream)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(static_cast <uint32_t>(stream));
- remote()->transact(GET_STRATEGY_FOR_STREAM, data, &reply);
- return reply.readUint32();
- }
-
- virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(static_cast <uint32_t>(stream));
- remote()->transact(GET_DEVICES_FOR_STREAM, data, &reply);
- return (audio_devices_t) reply.readInt32();
- }
-
- virtual audio_io_handle_t getOutputForEffect(const effect_descriptor_t *desc)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.write(desc, sizeof(effect_descriptor_t));
- remote()->transact(GET_OUTPUT_FOR_EFFECT, data, &reply);
- return static_cast <audio_io_handle_t> (reply.readInt32());
- }
-
- virtual status_t registerEffect(const effect_descriptor_t *desc,
- audio_io_handle_t io,
- uint32_t strategy,
- audio_session_t session,
- int id)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.write(desc, sizeof(effect_descriptor_t));
- data.writeInt32(io);
- data.writeInt32(strategy);
- data.writeInt32(session);
- data.writeInt32(id);
- remote()->transact(REGISTER_EFFECT, data, &reply);
- return static_cast <status_t> (reply.readInt32());
- }
-
- virtual status_t unregisterEffect(int id)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(id);
- remote()->transact(UNREGISTER_EFFECT, data, &reply);
- return static_cast <status_t> (reply.readInt32());
- }
-
- virtual status_t setEffectEnabled(int id, bool enabled)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(id);
- data.writeInt32(enabled);
- remote()->transact(SET_EFFECT_ENABLED, data, &reply);
- return static_cast <status_t> (reply.readInt32());
- }
-
- status_t moveEffectsToIo(const std::vector<int>& ids, audio_io_handle_t io) override
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(ids.size());
- for (auto id : ids) {
- data.writeInt32(id);
- }
- data.writeInt32(io);
- status_t status = remote()->transact(MOVE_EFFECTS_TO_IO, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- return static_cast <status_t> (reply.readInt32());
- }
-
- virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32((int32_t) stream);
- data.writeInt32(inPastMs);
- remote()->transact(IS_STREAM_ACTIVE, data, &reply);
- return reply.readInt32();
- }
-
- virtual bool isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32((int32_t) stream);
- data.writeInt32(inPastMs);
- remote()->transact(IS_STREAM_ACTIVE_REMOTELY, data, &reply);
- return reply.readInt32();
- }
-
- virtual bool isSourceActive(audio_source_t source) const
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32((int32_t) source);
- remote()->transact(IS_SOURCE_ACTIVE, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t queryDefaultPreProcessing(audio_session_t audioSession,
- effect_descriptor_t *descriptors,
- uint32_t *count)
- {
- if (descriptors == NULL || count == NULL) {
- return BAD_VALUE;
- }
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(audioSession);
- data.writeInt32(*count);
- status_t status = remote()->transact(QUERY_DEFAULT_PRE_PROCESSING, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = static_cast <status_t> (reply.readInt32());
- uint32_t retCount = reply.readInt32();
- if (retCount != 0) {
- uint32_t numDesc = (retCount < *count) ? retCount : *count;
- reply.read(descriptors, sizeof(effect_descriptor_t) * numDesc);
- }
- *count = retCount;
- return status;
- }
-
- status_t setSupportedSystemUsages(const std::vector<audio_usage_t>& systemUsages) {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(systemUsages.size());
- for (auto systemUsage : systemUsages) {
- data.writeInt32(systemUsage);
- }
- status_t status = remote()->transact(SET_SUPPORTED_SYSTEM_USAGES, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- return static_cast <status_t> (reply.readInt32());
- }
-
- status_t setAllowedCapturePolicy(uid_t uid, audio_flags_mask_t flags) override {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(uid);
- data.writeInt32(flags);
- remote()->transact(SET_ALLOWED_CAPTURE_POLICY, data, &reply);
- return reply.readInt32();
- }
-
- virtual bool isOffloadSupported(const audio_offload_info_t& info)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.write(&info, sizeof(audio_offload_info_t));
- remote()->transact(IS_OFFLOAD_SUPPORTED, data, &reply);
- return reply.readInt32();
- }
-
- virtual bool isDirectOutputSupported(const audio_config_base_t& config,
- const audio_attributes_t& attributes) {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.write(&config, sizeof(audio_config_base_t));
- data.write(&attributes, sizeof(audio_attributes_t));
- status_t status = remote()->transact(IS_DIRECT_OUTPUT_SUPPORTED, data, &reply);
- return status == NO_ERROR ? static_cast<bool>(reply.readInt32()) : false;
- }
-
- virtual status_t listAudioPorts(audio_port_role_t role,
- audio_port_type_t type,
- unsigned int *num_ports,
- struct audio_port *ports,
- unsigned int *generation)
- {
- if (num_ports == NULL || (*num_ports != 0 && ports == NULL) ||
- generation == NULL) {
- return BAD_VALUE;
- }
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- unsigned int numPortsReq = (ports == NULL) ? 0 : *num_ports;
- data.writeInt32(role);
- data.writeInt32(type);
- data.writeInt32(numPortsReq);
- status_t status = remote()->transact(LIST_AUDIO_PORTS, data, &reply);
- if (status == NO_ERROR) {
- status = (status_t)reply.readInt32();
- *num_ports = (unsigned int)reply.readInt32();
- }
- if (status == NO_ERROR) {
- if (numPortsReq > *num_ports) {
- numPortsReq = *num_ports;
- }
- if (numPortsReq > 0) {
- reply.read(ports, numPortsReq * sizeof(struct audio_port));
- }
- *generation = reply.readInt32();
- }
- return status;
- }
-
- virtual status_t getAudioPort(struct audio_port *port)
- {
- if (port == NULL) {
- return BAD_VALUE;
- }
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.write(port, sizeof(struct audio_port));
- status_t status = remote()->transact(GET_AUDIO_PORT, data, &reply);
- if (status != NO_ERROR ||
- (status = (status_t)reply.readInt32()) != NO_ERROR) {
- return status;
- }
- reply.read(port, sizeof(struct audio_port));
- return status;
- }
-
- virtual status_t createAudioPatch(const struct audio_patch *patch,
- audio_patch_handle_t *handle)
- {
- if (patch == NULL || handle == NULL) {
- return BAD_VALUE;
- }
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.write(patch, sizeof(struct audio_patch));
- data.write(handle, sizeof(audio_patch_handle_t));
- status_t status = remote()->transact(CREATE_AUDIO_PATCH, data, &reply);
- if (status != NO_ERROR ||
- (status = (status_t)reply.readInt32()) != NO_ERROR) {
- return status;
- }
- reply.read(handle, sizeof(audio_patch_handle_t));
- return status;
- }
-
- virtual status_t releaseAudioPatch(audio_patch_handle_t handle)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.write(&handle, sizeof(audio_patch_handle_t));
- status_t status = remote()->transact(RELEASE_AUDIO_PATCH, data, &reply);
- if (status != NO_ERROR) {
- status = (status_t)reply.readInt32();
- }
- return status;
- }
-
- virtual status_t listAudioPatches(unsigned int *num_patches,
- struct audio_patch *patches,
- unsigned int *generation)
- {
- if (num_patches == NULL || (*num_patches != 0 && patches == NULL) ||
- generation == NULL) {
- return BAD_VALUE;
- }
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- unsigned int numPatchesReq = (patches == NULL) ? 0 : *num_patches;
- data.writeInt32(numPatchesReq);
- status_t status = remote()->transact(LIST_AUDIO_PATCHES, data, &reply);
- if (status == NO_ERROR) {
- status = (status_t)reply.readInt32();
- *num_patches = (unsigned int)reply.readInt32();
- }
- if (status == NO_ERROR) {
- if (numPatchesReq > *num_patches) {
- numPatchesReq = *num_patches;
- }
- if (numPatchesReq > 0) {
- reply.read(patches, numPatchesReq * sizeof(struct audio_patch));
- }
- *generation = reply.readInt32();
- }
- return status;
- }
-
- virtual status_t setAudioPortConfig(const struct audio_port_config *config)
- {
- if (config == NULL) {
- return BAD_VALUE;
- }
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.write(config, sizeof(struct audio_port_config));
- status_t status = remote()->transact(SET_AUDIO_PORT_CONFIG, data, &reply);
- if (status != NO_ERROR) {
- status = (status_t)reply.readInt32();
- }
- return status;
- }
-
- virtual void registerClient(const sp<IAudioPolicyServiceClient>& client)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeStrongBinder(IInterface::asBinder(client));
- remote()->transact(REGISTER_CLIENT, data, &reply);
- }
-
- virtual void setAudioPortCallbacksEnabled(bool enabled)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(enabled ? 1 : 0);
- remote()->transact(SET_AUDIO_PORT_CALLBACK_ENABLED, data, &reply);
- }
-
- virtual void setAudioVolumeGroupCallbacksEnabled(bool enabled)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(enabled ? 1 : 0);
- remote()->transact(SET_AUDIO_VOLUME_GROUP_CALLBACK_ENABLED, data, &reply);
- }
-
- virtual status_t acquireSoundTriggerSession(audio_session_t *session,
- audio_io_handle_t *ioHandle,
- audio_devices_t *device)
- {
- if (session == NULL || ioHandle == NULL || device == NULL) {
- return BAD_VALUE;
- }
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- status_t status = remote()->transact(ACQUIRE_SOUNDTRIGGER_SESSION, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = (status_t)reply.readInt32();
- if (status == NO_ERROR) {
- *session = (audio_session_t)reply.readInt32();
- *ioHandle = (audio_io_handle_t)reply.readInt32();
- *device = (audio_devices_t)reply.readInt32();
- }
- return status;
- }
-
- virtual status_t releaseSoundTriggerSession(audio_session_t session)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(session);
- status_t status = remote()->transact(RELEASE_SOUNDTRIGGER_SESSION, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- return (status_t)reply.readInt32();
- }
-
- virtual audio_mode_t getPhoneState()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- status_t status = remote()->transact(GET_PHONE_STATE, data, &reply);
- if (status != NO_ERROR) {
- return AUDIO_MODE_INVALID;
- }
- return (audio_mode_t)reply.readInt32();
- }
-
- virtual status_t registerPolicyMixes(const Vector<AudioMix>& mixes, bool registration)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(registration ? 1 : 0);
- size_t size = mixes.size();
- if (size > MAX_MIXES_PER_POLICY) {
- size = MAX_MIXES_PER_POLICY;
- }
- size_t sizePosition = data.dataPosition();
- data.writeInt32(size);
- size_t finalSize = size;
- for (size_t i = 0; i < size; i++) {
- size_t position = data.dataPosition();
- if (mixes[i].writeToParcel(&data) != NO_ERROR) {
- data.setDataPosition(position);
- finalSize--;
- }
- }
- if (size != finalSize) {
- size_t position = data.dataPosition();
- data.setDataPosition(sizePosition);
- data.writeInt32(finalSize);
- data.setDataPosition(position);
- }
- status_t status = remote()->transact(REGISTER_POLICY_MIXES, data, &reply);
- if (status == NO_ERROR) {
- status = (status_t)reply.readInt32();
- }
- return status;
- }
-
- virtual status_t startAudioSource(const struct audio_port_config *source,
- const audio_attributes_t *attributes,
- audio_port_handle_t *portId)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- if (source == NULL || attributes == NULL || portId == NULL) {
- return BAD_VALUE;
- }
- data.write(source, sizeof(struct audio_port_config));
- data.write(attributes, sizeof(audio_attributes_t));
- status_t status = remote()->transact(START_AUDIO_SOURCE, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = (status_t)reply.readInt32();
- if (status != NO_ERROR) {
- return status;
- }
- *portId = (audio_port_handle_t)reply.readInt32();
- return status;
- }
-
- virtual status_t stopAudioSource(audio_port_handle_t portId)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(portId);
- status_t status = remote()->transact(STOP_AUDIO_SOURCE, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = (status_t)reply.readInt32();
- return status;
- }
-
- virtual status_t setMasterMono(bool mono)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(static_cast<int32_t>(mono));
- status_t status = remote()->transact(SET_MASTER_MONO, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- return static_cast<status_t>(reply.readInt32());
- }
-
- virtual status_t getMasterMono(bool *mono)
- {
- if (mono == nullptr) {
- return BAD_VALUE;
- }
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
-
- status_t status = remote()->transact(GET_MASTER_MONO, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = static_cast<status_t>(reply.readInt32());
- if (status == NO_ERROR) {
- *mono = static_cast<bool>(reply.readInt32());
- }
- return status;
- }
-
- virtual float getStreamVolumeDB(audio_stream_type_t stream, int index, audio_devices_t device)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(static_cast <int32_t>(stream));
- data.writeInt32(static_cast <int32_t>(index));
- data.writeUint32(static_cast <uint32_t>(device));
- status_t status = remote()->transact(GET_STREAM_VOLUME_DB, data, &reply);
- if (status != NO_ERROR) {
- return NAN;
- }
- return reply.readFloat();
- }
-
- virtual status_t getSurroundFormats(unsigned int *numSurroundFormats,
- audio_format_t *surroundFormats,
- bool *surroundFormatsEnabled,
- bool reported)
- {
- if (numSurroundFormats == NULL || (*numSurroundFormats != 0 &&
- (surroundFormats == NULL || surroundFormatsEnabled == NULL))) {
- return BAD_VALUE;
- }
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- unsigned int numSurroundFormatsReq = *numSurroundFormats;
- data.writeUint32(numSurroundFormatsReq);
- data.writeBool(reported);
- status_t status = remote()->transact(GET_SURROUND_FORMATS, data, &reply);
- if (status == NO_ERROR && (status = (status_t)reply.readInt32()) == NO_ERROR) {
- *numSurroundFormats = reply.readUint32();
- }
- if (status == NO_ERROR) {
- if (numSurroundFormatsReq > *numSurroundFormats) {
- numSurroundFormatsReq = *numSurroundFormats;
- }
- if (numSurroundFormatsReq > 0) {
- status = reply.read(surroundFormats,
- numSurroundFormatsReq * sizeof(audio_format_t));
- if (status != NO_ERROR) {
- return status;
- }
- status = reply.read(surroundFormatsEnabled,
- numSurroundFormatsReq * sizeof(bool));
- }
- }
- return status;
- }
-
- virtual status_t setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(audioFormat);
- data.writeBool(enabled);
- status_t status = remote()->transact(SET_SURROUND_FORMAT_ENABLED, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- return reply.readInt32();
- }
-
- virtual status_t getHwOffloadEncodingFormatsSupportedForA2DP(
- std::vector<audio_format_t> *formats)
- {
- if (formats == NULL) {
- return BAD_VALUE;
- }
-
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- status_t status = remote()->transact(GET_OFFLOAD_FORMATS_A2DP, data, &reply);
- if (status != NO_ERROR || (status = (status_t)reply.readInt32()) != NO_ERROR) {
- return status;
- }
-
- size_t list_size = reply.readUint32();
-
- for (size_t i = 0; i < list_size; i++) {
- formats->push_back(static_cast<audio_format_t>(reply.readInt32()));
- }
- return NO_ERROR;
- }
-
-
- virtual status_t addStreamDefaultEffect(const effect_uuid_t *type,
- const String16& opPackageName,
- const effect_uuid_t *uuid,
- int32_t priority,
- audio_usage_t usage,
- audio_unique_id_t* id)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.write(type, sizeof(effect_uuid_t));
- data.writeString16(opPackageName);
- data.write(uuid, sizeof(effect_uuid_t));
- data.writeInt32(priority);
- data.writeInt32((int32_t) usage);
- status_t status = remote()->transact(ADD_STREAM_DEFAULT_EFFECT, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = static_cast <status_t> (reply.readInt32());
- *id = reply.readInt32();
- return status;
- }
-
- virtual status_t removeStreamDefaultEffect(audio_unique_id_t id)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(id);
- status_t status = remote()->transact(REMOVE_STREAM_DEFAULT_EFFECT, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- return static_cast <status_t> (reply.readInt32());
- }
-
- virtual status_t addSourceDefaultEffect(const effect_uuid_t *type,
- const String16& opPackageName,
- const effect_uuid_t *uuid,
- int32_t priority,
- audio_source_t source,
- audio_unique_id_t* id)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.write(type, sizeof(effect_uuid_t));
- data.writeString16(opPackageName);
- data.write(uuid, sizeof(effect_uuid_t));
- data.writeInt32(priority);
- data.writeInt32((int32_t) source);
- status_t status = remote()->transact(ADD_SOURCE_DEFAULT_EFFECT, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = static_cast <status_t> (reply.readInt32());
- *id = reply.readInt32();
- return status;
- }
-
- virtual status_t removeSourceDefaultEffect(audio_unique_id_t id)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(id);
- status_t status = remote()->transact(REMOVE_SOURCE_DEFAULT_EFFECT, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- return static_cast <status_t> (reply.readInt32());
- }
-
- virtual status_t setAssistantUid(uid_t uid)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(uid);
- status_t status = remote()->transact(SET_ASSISTANT_UID, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- return static_cast <status_t> (reply.readInt32());
- }
-
- virtual status_t setA11yServicesUids(const std::vector<uid_t>& uids)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(uids.size());
- for (auto uid : uids) {
- data.writeInt32(uid);
- }
- status_t status = remote()->transact(SET_A11Y_SERVICES_UIDS, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- return static_cast <status_t> (reply.readInt32());
- }
-
- virtual status_t setCurrentImeUid(uid_t uid)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(uid);
- status_t status = remote()->transact(SET_CURRENT_IME_UID, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- return static_cast <status_t> (reply.readInt32());
- }
-
- virtual bool isHapticPlaybackSupported()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- status_t status = remote()->transact(IS_HAPTIC_PLAYBACK_SUPPORTED, data, &reply);
- if (status != NO_ERROR) {
- return false;
- }
- return reply.readBool();
- }
-
- virtual status_t setUidDeviceAffinities(uid_t uid, const AudioDeviceTypeAddrVector& devices)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
-
- data.writeInt32((int32_t) uid);
- status_t status = data.writeParcelableVector(devices);
- if (status != NO_ERROR) {
- return status;
- }
-
- status = remote()->transact(SET_UID_DEVICE_AFFINITY, data, &reply);
- if (status == NO_ERROR) {
- status = (status_t)reply.readInt32();
- }
- return status;
- }
-
- virtual status_t removeUidDeviceAffinities(uid_t uid) {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
-
- data.writeInt32((int32_t) uid);
-
- status_t status =
- remote()->transact(REMOVE_UID_DEVICE_AFFINITY, data, &reply);
- if (status == NO_ERROR) {
- status = (status_t) reply.readInt32();
- }
- return status;
- }
-
- virtual status_t setUserIdDeviceAffinities(int userId, const AudioDeviceTypeAddrVector& devices)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
-
- data.writeInt32((int32_t) userId);
- status_t status = data.writeParcelableVector(devices);
- if (status != NO_ERROR) {
- return status;
- }
-
- status = remote()->transact(SET_USERID_DEVICE_AFFINITY, data, &reply);
- if (status == NO_ERROR) {
- status = (status_t)reply.readInt32();
- }
- return status;
- }
-
- virtual status_t removeUserIdDeviceAffinities(int userId) {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
-
- data.writeInt32((int32_t) userId);
-
- status_t status =
- remote()->transact(REMOVE_USERID_DEVICE_AFFINITY, data, &reply);
- if (status == NO_ERROR) {
- status = (status_t) reply.readInt32();
- }
- return status;
- }
-
- virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
-
- status_t status = remote()->transact(LIST_AUDIO_PRODUCT_STRATEGIES, data, &reply);
- if (status != NO_ERROR) {
- ALOGE("%s: permission denied", __func__);
- return status;
- }
- status = static_cast<status_t>(reply.readInt32());
- if (status != NO_ERROR) {
- return status;
- }
- uint32_t numStrategies = static_cast<uint32_t>(reply.readInt32());
- for (size_t i = 0; i < numStrategies; i++) {
- AudioProductStrategy strategy;
- status = strategy.readFromParcel(&reply);
- if (status != NO_ERROR) {
- ALOGE("%s: failed to read strategies", __FUNCTION__);
- strategies.clear();
- return status;
- }
- strategies.push_back(strategy);
- }
- return NO_ERROR;
- }
-
- virtual status_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa,
- product_strategy_t &productStrategy)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- status_t status = aa.writeToParcel(&data);
- if (status != NO_ERROR) {
- return status;
- }
- status = remote()->transact(GET_STRATEGY_FOR_ATTRIBUTES, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = static_cast<status_t>(reply.readInt32());
- if (status != NO_ERROR) {
- return status;
- }
- productStrategy = static_cast<product_strategy_t>(reply.readInt32());
- return NO_ERROR;
- }
-
- virtual status_t listAudioVolumeGroups(AudioVolumeGroupVector &groups)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
-
- status_t status = remote()->transact(LIST_AUDIO_VOLUME_GROUPS, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = static_cast<status_t>(reply.readInt32());
- if (status != NO_ERROR) {
- return status;
- }
- uint32_t numGroups = static_cast<uint32_t>(reply.readInt32());
- for (size_t i = 0; i < numGroups; i++) {
- AudioVolumeGroup group;
- status = group.readFromParcel(&reply);
- if (status != NO_ERROR) {
- ALOGE("%s: failed to read volume groups", __FUNCTION__);
- groups.clear();
- return status;
- }
- groups.push_back(group);
- }
- return NO_ERROR;
- }
-
- virtual status_t getVolumeGroupFromAudioAttributes(const AudioAttributes &aa,
- volume_group_t &volumeGroup)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- status_t status = aa.writeToParcel(&data);
- if (status != NO_ERROR) {
- return status;
- }
- status = remote()->transact(GET_VOLUME_GROUP_FOR_ATTRIBUTES, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = static_cast<status_t>(reply.readInt32());
- if (status != NO_ERROR) {
- return status;
- }
- volumeGroup = static_cast<volume_group_t>(reply.readInt32());
- return NO_ERROR;
- }
-
- virtual status_t setRttEnabled(bool enabled)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(static_cast<int32_t>(enabled));
- status_t status = remote()->transact(SET_RTT_ENABLED, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- 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 setDevicesRoleForStrategy(product_strategy_t strategy,
- device_role_t role, const AudioDeviceTypeAddrVector &devices)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeUint32(static_cast<uint32_t>(strategy));
- data.writeUint32(static_cast<uint32_t>(role));
- status_t status = data.writeParcelableVector(devices);
- if (status != NO_ERROR) {
- return BAD_VALUE;
- }
- status = remote()->transact(SET_DEVICES_ROLE_FOR_PRODUCT_STRATEGY, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- return static_cast<status_t>(reply.readInt32());
- }
-
- virtual status_t removeDevicesRoleForStrategy(product_strategy_t strategy, device_role_t role)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeUint32(static_cast<uint32_t>(strategy));
- data.writeUint32(static_cast<uint32_t>(role));
- status_t status = remote()->transact(REMOVE_DEVICES_ROLE_FOR_PRODUCT_STRATEGY,
- data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- return static_cast<status_t>(reply.readInt32());
- }
-
- virtual status_t getDevicesForRoleAndStrategy(product_strategy_t strategy,
- device_role_t role, AudioDeviceTypeAddrVector &devices)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeUint32(static_cast<uint32_t>(strategy));
- data.writeUint32(static_cast<uint32_t>(role));
- status_t status = remote()->transact(GET_DEVICES_FOR_ROLE_AND_PRODUCT_STRATEGY,
- data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = reply.readParcelableVector(&devices);
- if (status != NO_ERROR) {
- return status;
- }
- return static_cast<status_t>(reply.readInt32());
- }
-
- virtual status_t setDevicesRoleForCapturePreset(audio_source_t audioSource,
- device_role_t role, const AudioDeviceTypeAddrVector &devices) {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeUint32(static_cast<uint32_t>(audioSource));
- data.writeUint32(static_cast<uint32_t>(role));
- status_t status = data.writeParcelableVector(devices);
- if (status != NO_ERROR) {
- return status;
- }
- status = remote()->transact(SET_DEVICES_ROLE_FOR_CAPTURE_PRESET, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- return static_cast<status_t>(reply.readInt32());
- }
-
- virtual status_t addDevicesRoleForCapturePreset(audio_source_t audioSource,
- device_role_t role, const AudioDeviceTypeAddrVector &devices)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeUint32(static_cast<uint32_t>(audioSource));
- data.writeUint32(static_cast<uint32_t>(role));
- status_t status = data.writeParcelableVector(devices);
- if (status != NO_ERROR) {
- return status;
- }
- status = remote()->transact(ADD_DEVICES_ROLE_FOR_CAPTURE_PRESET, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- return static_cast<status_t>(reply.readInt32());
- }
-
- virtual status_t removeDevicesRoleForCapturePreset(
- audio_source_t audioSource, device_role_t role,
- const AudioDeviceTypeAddrVector& devices)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeUint32(static_cast<uint32_t>(audioSource));
- data.writeUint32(static_cast<uint32_t>(role));
- status_t status = data.writeParcelableVector(devices);
- if (status != NO_ERROR) {
- return status;
- }
- status = remote()->transact(REMOVE_DEVICES_ROLE_FOR_CAPTURE_PRESET,
- data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- return static_cast<status_t>(reply.readInt32());
- }
-
- virtual status_t clearDevicesRoleForCapturePreset(
- audio_source_t audioSource, device_role_t role)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeUint32(static_cast<uint32_t>(audioSource));
- data.writeUint32(static_cast<uint32_t>(role));
- status_t status = remote()->transact(CLEAR_DEVICES_ROLE_FOR_CAPTURE_PRESET,
- data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- return static_cast<status_t>(reply.readInt32());
- }
-
- virtual status_t getDevicesForRoleAndCapturePreset(audio_source_t audioSource,
- device_role_t role, AudioDeviceTypeAddrVector &devices)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeUint32(static_cast<uint32_t>(audioSource));
- data.writeUint32(static_cast<uint32_t>(role));
- status_t status = remote()->transact(GET_DEVICES_FOR_ROLE_AND_CAPTURE_PRESET,
- data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = reply.readParcelableVector(&devices);
- if (status != NO_ERROR) {
- return status;
- }
- 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;
- }
-
- virtual void onNewAudioModulesAvailable()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- remote()->transact(AUDIO_MODULES_UPDATED, data, &reply, IBinder::FLAG_ONEWAY);
- }
-
- status_t registerSoundTriggerCaptureStateListener(
- const sp<media::ICaptureStateListener>& listener,
- bool* result) override {
- Parcel data, reply;
- status_t status;
- status =
- data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- if (status != NO_ERROR) return status;
- status = data.writeStrongBinder(IInterface::asBinder(listener));
- if (status != NO_ERROR) return status;
- status =
- remote()->transact(REGISTER_SOUNDTRIGGER_CAPTURE_STATE_LISTENER,
- data,
- &reply,
- 0);
- if (status != NO_ERROR) return status;
- status = reply.readBool(result);
- if (status != NO_ERROR) return status;
- return NO_ERROR;
- }
-};
-
-IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService");
-
-// ----------------------------------------------------------------------
-
-status_t BnAudioPolicyService::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- // make sure transactions reserved to AudioFlinger do not come from other processes
- switch (code) {
- case START_OUTPUT:
- case STOP_OUTPUT:
- case RELEASE_OUTPUT:
- case GET_INPUT_FOR_ATTR:
- case START_INPUT:
- case STOP_INPUT:
- case RELEASE_INPUT:
- case GET_OUTPUT_FOR_EFFECT:
- case REGISTER_EFFECT:
- case UNREGISTER_EFFECT:
- case SET_EFFECT_ENABLED:
- case GET_OUTPUT_FOR_ATTR:
- case MOVE_EFFECTS_TO_IO:
- ALOGW("%s: transaction %d received from PID %d",
- __func__, code, IPCThreadState::self()->getCallingPid());
- // return status only for non void methods
- switch (code) {
- case RELEASE_OUTPUT:
- case RELEASE_INPUT:
- break;
- default:
- reply->writeInt32(static_cast<int32_t> (INVALID_OPERATION));
- break;
- }
- return OK;
- default:
- break;
- }
-
- // make sure the following transactions come from system components
- switch (code) {
- case SET_DEVICE_CONNECTION_STATE:
- case HANDLE_DEVICE_CONFIG_CHANGE:
- case SET_PHONE_STATE:
-//FIXME: Allow SET_FORCE_USE calls from system apps until a better use case routing API is available
-// case SET_FORCE_USE:
- case INIT_STREAM_VOLUME:
- case SET_STREAM_VOLUME:
- case REGISTER_POLICY_MIXES:
- case SET_MASTER_MONO:
- case GET_SURROUND_FORMATS:
- case SET_SURROUND_FORMAT_ENABLED:
- case SET_ASSISTANT_UID:
- case SET_A11Y_SERVICES_UIDS:
- case SET_UID_DEVICE_AFFINITY:
- case REMOVE_UID_DEVICE_AFFINITY:
- case SET_USERID_DEVICE_AFFINITY:
- case REMOVE_USERID_DEVICE_AFFINITY:
- case GET_OFFLOAD_FORMATS_A2DP:
- case LIST_AUDIO_VOLUME_GROUPS:
- case GET_VOLUME_GROUP_FOR_ATTRIBUTES:
- case ACQUIRE_SOUNDTRIGGER_SESSION:
- case RELEASE_SOUNDTRIGGER_SESSION:
- case SET_RTT_ENABLED:
- case IS_CALL_SCREEN_MODE_SUPPORTED:
- case SET_DEVICES_ROLE_FOR_PRODUCT_STRATEGY:
- case SET_SUPPORTED_SYSTEM_USAGES:
- case REMOVE_DEVICES_ROLE_FOR_PRODUCT_STRATEGY:
- case GET_DEVICES_FOR_ROLE_AND_PRODUCT_STRATEGY:
- case GET_DEVICES_FOR_ATTRIBUTES:
- case SET_ALLOWED_CAPTURE_POLICY:
- case AUDIO_MODULES_UPDATED:
- case SET_CURRENT_IME_UID:
- case REGISTER_SOUNDTRIGGER_CAPTURE_STATE_LISTENER:
- case SET_DEVICES_ROLE_FOR_CAPTURE_PRESET:
- case ADD_DEVICES_ROLE_FOR_CAPTURE_PRESET:
- case REMOVE_DEVICES_ROLE_FOR_CAPTURE_PRESET:
- case CLEAR_DEVICES_ROLE_FOR_CAPTURE_PRESET:
- case GET_DEVICES_FOR_ROLE_AND_CAPTURE_PRESET: {
- if (!isServiceUid(IPCThreadState::self()->getCallingUid())) {
- ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
- __func__, code, IPCThreadState::self()->getCallingPid(),
- IPCThreadState::self()->getCallingUid());
- reply->writeInt32(static_cast<int32_t> (INVALID_OPERATION));
- return OK;
- }
- } break;
- default:
- break;
- }
-
- std::string tag("IAudioPolicyService command " + std::to_string(code));
- TimeCheck check(tag.c_str());
-
- switch (code) {
- case SET_DEVICE_CONNECTION_STATE: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_devices_t device =
- static_cast <audio_devices_t>(data.readInt32());
- audio_policy_dev_state_t state =
- static_cast <audio_policy_dev_state_t>(data.readInt32());
- const char *device_address = data.readCString();
- const char *device_name = data.readCString();
- audio_format_t codecFormat = static_cast <audio_format_t>(data.readInt32());
- if (device_address == nullptr || device_name == nullptr) {
- ALOGE("Bad Binder transaction: SET_DEVICE_CONNECTION_STATE for device %u", device);
- reply->writeInt32(static_cast<int32_t> (BAD_VALUE));
- } else {
- reply->writeInt32(static_cast<uint32_t> (setDeviceConnectionState(device,
- state,
- device_address,
- device_name,
- codecFormat)));
- }
- return NO_ERROR;
- } break;
-
- case GET_DEVICE_CONNECTION_STATE: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_devices_t device =
- static_cast<audio_devices_t> (data.readInt32());
- const char *device_address = data.readCString();
- if (device_address == nullptr) {
- ALOGE("Bad Binder transaction: GET_DEVICE_CONNECTION_STATE for device %u", device);
- reply->writeInt32(static_cast<int32_t> (AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE));
- } else {
- reply->writeInt32(static_cast<uint32_t> (getDeviceConnectionState(device,
- device_address)));
- }
- return NO_ERROR;
- } break;
-
- case HANDLE_DEVICE_CONFIG_CHANGE: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_devices_t device =
- static_cast <audio_devices_t>(data.readInt32());
- const char *device_address = data.readCString();
- const char *device_name = data.readCString();
- audio_format_t codecFormat =
- static_cast <audio_format_t>(data.readInt32());
- if (device_address == nullptr || device_name == nullptr) {
- ALOGE("Bad Binder transaction: HANDLE_DEVICE_CONFIG_CHANGE for device %u", device);
- reply->writeInt32(static_cast<int32_t> (BAD_VALUE));
- } else {
- reply->writeInt32(static_cast<uint32_t> (handleDeviceConfigChange(device,
- device_address,
- device_name,
- codecFormat)));
- }
- return NO_ERROR;
- } break;
-
- case SET_PHONE_STATE: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- reply->writeInt32(static_cast <uint32_t>(setPhoneState(
- (audio_mode_t) data.readInt32(),
- (uid_t) data.readInt32())));
- return NO_ERROR;
- } break;
-
- case SET_FORCE_USE: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_policy_force_use_t usage = static_cast <audio_policy_force_use_t>(
- data.readInt32());
- audio_policy_forced_cfg_t config =
- static_cast <audio_policy_forced_cfg_t>(data.readInt32());
- reply->writeInt32(static_cast <uint32_t>(setForceUse(usage, config)));
- return NO_ERROR;
- } break;
-
- case GET_FORCE_USE: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_policy_force_use_t usage = static_cast <audio_policy_force_use_t>(
- data.readInt32());
- reply->writeInt32(static_cast <uint32_t>(getForceUse(usage)));
- return NO_ERROR;
- } break;
-
- case GET_OUTPUT: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_stream_type_t stream =
- static_cast <audio_stream_type_t>(data.readInt32());
- audio_io_handle_t output = getOutput(stream);
- reply->writeInt32(static_cast <int>(output));
- return NO_ERROR;
- } break;
-
- case GET_OUTPUT_FOR_ATTR: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
- status_t status = data.read(&attr, sizeof(audio_attributes_t));
- if (status != NO_ERROR) {
- return status;
- }
- audio_session_t session = (audio_session_t)data.readInt32();
- audio_stream_type_t stream = AUDIO_STREAM_DEFAULT;
- bool hasStream = data.readInt32() != 0;
- if (hasStream) {
- stream = (audio_stream_type_t)data.readInt32();
- }
- pid_t pid = (pid_t)data.readInt32();
- uid_t uid = (uid_t)data.readInt32();
- audio_config_t config;
- memset(&config, 0, sizeof(audio_config_t));
- data.read(&config, sizeof(audio_config_t));
- audio_output_flags_t flags =
- static_cast <audio_output_flags_t>(data.readInt32());
- audio_port_handle_t selectedDeviceId = data.readInt32();
- audio_port_handle_t portId = (audio_port_handle_t)data.readInt32();
- audio_io_handle_t output = 0;
- std::vector<audio_io_handle_t> secondaryOutputs;
-
- status = AudioSanitizer::sanitizeAudioAttributes(&attr, "68953950");
- if (status == NO_ERROR) {
- status = getOutputForAttr(&attr,
- &output, session, &stream, pid, uid,
- &config,
- flags, &selectedDeviceId, &portId, &secondaryOutputs);
- }
- reply->writeInt32(status);
- status = reply->write(&attr, sizeof(audio_attributes_t));
- if (status != NO_ERROR) {
- return status;
- }
- reply->writeInt32(output);
- reply->writeInt32(stream);
- reply->writeInt32(selectedDeviceId);
- reply->writeInt32(portId);
- reply->writeInt32(secondaryOutputs.size());
- return reply->write(secondaryOutputs.data(),
- secondaryOutputs.size() * sizeof(audio_io_handle_t));
- } break;
-
- case START_OUTPUT: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- const audio_port_handle_t portId = static_cast <audio_port_handle_t>(data.readInt32());
- reply->writeInt32(static_cast <uint32_t>(startOutput(portId)));
- return NO_ERROR;
- } break;
-
- case STOP_OUTPUT: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- const audio_port_handle_t portId = static_cast <audio_port_handle_t>(data.readInt32());
- reply->writeInt32(static_cast <uint32_t>(stopOutput(portId)));
- return NO_ERROR;
- } break;
-
- case RELEASE_OUTPUT: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- const audio_port_handle_t portId = static_cast <audio_port_handle_t>(data.readInt32());
- releaseOutput(portId);
- return NO_ERROR;
- } break;
-
- case GET_INPUT_FOR_ATTR: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_attributes_t attr = {};
- status_t status = data.read(&attr, sizeof(audio_attributes_t));
- if (status != NO_ERROR) {
- return status;
- }
-
- audio_io_handle_t input = (audio_io_handle_t)data.readInt32();
- audio_unique_id_t riid = (audio_unique_id_t)data.readInt32();
- audio_session_t session = (audio_session_t)data.readInt32();
- pid_t pid = (pid_t)data.readInt32();
- uid_t uid = (uid_t)data.readInt32();
- const String16 opPackageName = data.readString16();
- audio_config_base_t config;
- memset(&config, 0, sizeof(audio_config_base_t));
- data.read(&config, sizeof(audio_config_base_t));
- audio_input_flags_t flags = (audio_input_flags_t) data.readInt32();
- audio_port_handle_t selectedDeviceId = (audio_port_handle_t) data.readInt32();
- audio_port_handle_t portId = (audio_port_handle_t)data.readInt32();
-
- status = AudioSanitizer::sanitizeAudioAttributes(&attr, "68953950");
- if (status == NO_ERROR) {
- status = getInputForAttr(&attr, &input, riid, session, pid, uid,
- opPackageName, &config,
- flags, &selectedDeviceId, &portId);
- }
- reply->writeInt32(status);
- if (status == NO_ERROR) {
- reply->writeInt32(input);
- reply->writeInt32(selectedDeviceId);
- reply->writeInt32(portId);
- }
- return NO_ERROR;
- } break;
-
- case START_INPUT: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_port_handle_t portId = static_cast <audio_port_handle_t>(data.readInt32());
- status_t status = startInput(portId);
- reply->writeInt32(static_cast <uint32_t>(status));
- return NO_ERROR;
- } break;
-
- case STOP_INPUT: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_port_handle_t portId = static_cast <audio_port_handle_t>(data.readInt32());
- reply->writeInt32(static_cast <uint32_t>(stopInput(portId)));
- return NO_ERROR;
- } break;
-
- case RELEASE_INPUT: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_port_handle_t portId = static_cast <audio_port_handle_t>(data.readInt32());
- releaseInput(portId);
- return NO_ERROR;
- } break;
-
- case INIT_STREAM_VOLUME: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_stream_type_t stream =
- static_cast <audio_stream_type_t>(data.readInt32());
- int indexMin = data.readInt32();
- int indexMax = data.readInt32();
- reply->writeInt32(static_cast <uint32_t>(initStreamVolume(stream, indexMin,indexMax)));
- return NO_ERROR;
- } break;
-
- case SET_STREAM_VOLUME: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_stream_type_t stream =
- static_cast <audio_stream_type_t>(data.readInt32());
- int index = data.readInt32();
- audio_devices_t device = static_cast <audio_devices_t>(data.readInt32());
- reply->writeInt32(static_cast <uint32_t>(setStreamVolumeIndex(stream,
- index,
- device)));
- return NO_ERROR;
- } break;
-
- case GET_STREAM_VOLUME: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_stream_type_t stream =
- static_cast <audio_stream_type_t>(data.readInt32());
- audio_devices_t device = static_cast <audio_devices_t>(data.readInt32());
- int index = 0;
- status_t status = getStreamVolumeIndex(stream, &index, device);
- reply->writeInt32(index);
- reply->writeInt32(static_cast <uint32_t>(status));
- return NO_ERROR;
- } break;
-
- case GET_STRATEGY_FOR_STREAM: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_stream_type_t stream =
- static_cast <audio_stream_type_t>(data.readInt32());
- reply->writeUint32(getStrategyForStream(stream));
- return NO_ERROR;
- } break;
-
- case SET_VOLUME_ATTRIBUTES: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_attributes_t attributes = {};
- status_t status = data.read(&attributes, sizeof(audio_attributes_t));
- if (status != NO_ERROR) {
- return status;
- }
-
- int index = data.readInt32();
- audio_devices_t device = static_cast <audio_devices_t>(data.readInt32());
-
- status = AudioSanitizer::sanitizeAudioAttributes(&attributes, "169572641");
- if (status == NO_ERROR) {
- status = setVolumeIndexForAttributes(attributes, index, device);
- }
- reply->writeInt32(static_cast <int32_t>(status));
- return NO_ERROR;
- } break;
-
- case GET_VOLUME_ATTRIBUTES: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_attributes_t attributes = {};
- status_t status = data.read(&attributes, sizeof(audio_attributes_t));
- if (status != NO_ERROR) {
- return status;
- }
- audio_devices_t device = static_cast <audio_devices_t>(data.readInt32());
-
- int index = 0;
- status = AudioSanitizer::sanitizeAudioAttributes(&attributes, "169572641");
- if (status == NO_ERROR) {
- status = getVolumeIndexForAttributes(attributes, index, device);
- }
- reply->writeInt32(static_cast <int32_t>(status));
- if (status == NO_ERROR) {
- reply->writeInt32(index);
- }
- return NO_ERROR;
- } break;
-
- case GET_MIN_VOLUME_FOR_ATTRIBUTES: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_attributes_t attributes = {};
- status_t status = data.read(&attributes, sizeof(audio_attributes_t));
- if (status != NO_ERROR) {
- return status;
- }
-
- int index = 0;
- status = AudioSanitizer::sanitizeAudioAttributes(&attributes, "169572641");
- if (status == NO_ERROR) {
- status = getMinVolumeIndexForAttributes(attributes, index);
- }
- reply->writeInt32(static_cast <int32_t>(status));
- if (status == NO_ERROR) {
- reply->writeInt32(index);
- }
- return NO_ERROR;
- } break;
-
- case GET_MAX_VOLUME_FOR_ATTRIBUTES: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_attributes_t attributes = {};
- status_t status = data.read(&attributes, sizeof(audio_attributes_t));
- if (status != NO_ERROR) {
- return status;
- }
-
- int index = 0;
- status = AudioSanitizer::sanitizeAudioAttributes(&attributes, "169572641");
- if (status == NO_ERROR) {
- status = getMaxVolumeIndexForAttributes(attributes, index);
- }
- reply->writeInt32(static_cast <int32_t>(status));
- if (status == NO_ERROR) {
- reply->writeInt32(index);
- }
- return NO_ERROR;
- } break;
-
- case GET_DEVICES_FOR_STREAM: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_stream_type_t stream =
- static_cast <audio_stream_type_t>(data.readInt32());
- reply->writeInt32(static_cast <int>(getDevicesForStream(stream)));
- return NO_ERROR;
- } break;
-
- case GET_OUTPUT_FOR_EFFECT: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- effect_descriptor_t desc = {};
- status_t status = data.read(&desc, sizeof(desc));
- if (status != NO_ERROR) {
- android_errorWriteLog(0x534e4554, "73126106");
- return status;
- }
- audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
- status = AudioSanitizer::sanitizeEffectDescriptor(&desc, "73126106");
- if (status == NO_ERROR) {
- output = getOutputForEffect(&desc);
- }
- reply->writeInt32(static_cast <int32_t>(output));
- return NO_ERROR;
- } break;
-
- case REGISTER_EFFECT: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- effect_descriptor_t desc = {};
- status_t status = data.read(&desc, sizeof(desc));
- if (status != NO_ERROR) {
- android_errorWriteLog(0x534e4554, "73126106");
- return status;
- }
- audio_io_handle_t io = data.readInt32();
- uint32_t strategy = data.readInt32();
- audio_session_t session = (audio_session_t) data.readInt32();
- int id = data.readInt32();
- status = AudioSanitizer::sanitizeEffectDescriptor(&desc, "73126106");
- if (status == NO_ERROR) {
- status = registerEffect(&desc, io, strategy, session, id);
- }
- reply->writeInt32(static_cast <int32_t>(status));
- return NO_ERROR;
- } break;
-
- case UNREGISTER_EFFECT: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- int id = data.readInt32();
- reply->writeInt32(static_cast <int32_t>(unregisterEffect(id)));
- return NO_ERROR;
- } break;
-
- case SET_EFFECT_ENABLED: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- int id = data.readInt32();
- bool enabled = static_cast <bool>(data.readInt32());
- reply->writeInt32(static_cast <int32_t>(setEffectEnabled(id, enabled)));
- return NO_ERROR;
- } break;
-
- case MOVE_EFFECTS_TO_IO: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- std::vector<int> ids;
- int32_t size;
- status_t status = data.readInt32(&size);
- if (status != NO_ERROR) {
- return status;
- }
- if (size > MAX_ITEMS_PER_LIST) {
- return BAD_VALUE;
- }
- for (int32_t i = 0; i < size; i++) {
- int id;
- status = data.readInt32(&id);
- if (status != NO_ERROR) {
- return status;
- }
- ids.push_back(id);
- }
-
- audio_io_handle_t io = data.readInt32();
- reply->writeInt32(static_cast <int32_t>(moveEffectsToIo(ids, io)));
- return NO_ERROR;
- } break;
-
- case IS_STREAM_ACTIVE: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_stream_type_t stream = (audio_stream_type_t) data.readInt32();
- uint32_t inPastMs = (uint32_t)data.readInt32();
- reply->writeInt32( isStreamActive(stream, inPastMs) );
- return NO_ERROR;
- } break;
-
- case IS_STREAM_ACTIVE_REMOTELY: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_stream_type_t stream = (audio_stream_type_t) data.readInt32();
- uint32_t inPastMs = (uint32_t)data.readInt32();
- reply->writeInt32( isStreamActiveRemotely(stream, inPastMs) );
- return NO_ERROR;
- } break;
-
- case IS_SOURCE_ACTIVE: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_source_t source = (audio_source_t) data.readInt32();
- reply->writeInt32( isSourceActive(source));
- return NO_ERROR;
- }
-
- case QUERY_DEFAULT_PRE_PROCESSING: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_session_t audioSession = (audio_session_t) data.readInt32();
- uint32_t count = data.readInt32();
- if (count > AudioEffect::kMaxPreProcessing) {
- count = AudioEffect::kMaxPreProcessing;
- }
- uint32_t retCount = count;
- effect_descriptor_t *descriptors = new effect_descriptor_t[count]{};
- status_t status = queryDefaultPreProcessing(audioSession, descriptors, &retCount);
- reply->writeInt32(status);
- if (status != NO_ERROR && status != NO_MEMORY) {
- retCount = 0;
- }
- reply->writeInt32(retCount);
- if (retCount != 0) {
- if (retCount < count) {
- count = retCount;
- }
- reply->write(descriptors, sizeof(effect_descriptor_t) * count);
- }
- delete[] descriptors;
- return status;
- }
-
- case IS_OFFLOAD_SUPPORTED: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_offload_info_t info = {};
- data.read(&info, sizeof(audio_offload_info_t));
- bool isSupported = isOffloadSupported(info);
- reply->writeInt32(isSupported);
- return NO_ERROR;
- }
-
- case IS_DIRECT_OUTPUT_SUPPORTED: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_config_base_t config = {};
- audio_attributes_t attributes = {};
- status_t status = data.read(&config, sizeof(audio_config_base_t));
- if (status != NO_ERROR) return status;
- status = data.read(&attributes, sizeof(audio_attributes_t));
- if (status != NO_ERROR) return status;
- status = AudioSanitizer::sanitizeAudioAttributes(&attributes, "169572641");
- if (status == NO_ERROR) {
- status = isDirectOutputSupported(config, attributes);
- }
- reply->writeInt32(static_cast <int32_t>(status));
- return NO_ERROR;
- }
-
- case LIST_AUDIO_PORTS: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_port_role_t role = (audio_port_role_t)data.readInt32();
- audio_port_type_t type = (audio_port_type_t)data.readInt32();
- unsigned int numPortsReq = data.readInt32();
- if (numPortsReq > MAX_ITEMS_PER_LIST) {
- numPortsReq = MAX_ITEMS_PER_LIST;
- }
- unsigned int numPorts = numPortsReq;
- struct audio_port *ports =
- (struct audio_port *)calloc(numPortsReq, sizeof(struct audio_port));
- if (ports == NULL) {
- reply->writeInt32(NO_MEMORY);
- reply->writeInt32(0);
- return NO_ERROR;
- }
- unsigned int generation;
- status_t status = listAudioPorts(role, type, &numPorts, ports, &generation);
- reply->writeInt32(status);
- reply->writeInt32(numPorts);
-
- if (status == NO_ERROR) {
- if (numPortsReq > numPorts) {
- numPortsReq = numPorts;
- }
- reply->write(ports, numPortsReq * sizeof(struct audio_port));
- reply->writeInt32(generation);
- }
- free(ports);
- return NO_ERROR;
- }
-
- case GET_AUDIO_PORT: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- struct audio_port port = {};
- status_t status = data.read(&port, sizeof(struct audio_port));
- if (status != NO_ERROR) {
- ALOGE("b/23912202");
- return status;
- }
- status = AudioSanitizer::sanitizeAudioPort(&port);
- if (status == NO_ERROR) {
- status = getAudioPort(&port);
- }
- reply->writeInt32(status);
- if (status == NO_ERROR) {
- reply->write(&port, sizeof(struct audio_port));
- }
- return NO_ERROR;
- }
-
- case CREATE_AUDIO_PATCH: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- struct audio_patch patch = {};
- status_t status = data.read(&patch, sizeof(struct audio_patch));
- if (status != NO_ERROR) {
- return status;
- }
- audio_patch_handle_t handle = AUDIO_PATCH_HANDLE_NONE;
- status = data.read(&handle, sizeof(audio_patch_handle_t));
- if (status != NO_ERROR) {
- ALOGE("b/23912202");
- return status;
- }
- status = AudioSanitizer::sanitizeAudioPatch(&patch);
- if (status == NO_ERROR) {
- status = createAudioPatch(&patch, &handle);
- }
- reply->writeInt32(status);
- if (status == NO_ERROR) {
- reply->write(&handle, sizeof(audio_patch_handle_t));
- }
- return NO_ERROR;
- }
-
- case RELEASE_AUDIO_PATCH: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_patch_handle_t handle = AUDIO_PATCH_HANDLE_NONE;
- data.read(&handle, sizeof(audio_patch_handle_t));
- status_t status = releaseAudioPatch(handle);
- reply->writeInt32(status);
- return NO_ERROR;
- }
-
- case LIST_AUDIO_PATCHES: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- unsigned int numPatchesReq = data.readInt32();
- if (numPatchesReq > MAX_ITEMS_PER_LIST) {
- numPatchesReq = MAX_ITEMS_PER_LIST;
- }
- unsigned int numPatches = numPatchesReq;
- struct audio_patch *patches =
- (struct audio_patch *)calloc(numPatchesReq,
- sizeof(struct audio_patch));
- if (patches == NULL) {
- reply->writeInt32(NO_MEMORY);
- reply->writeInt32(0);
- return NO_ERROR;
- }
- unsigned int generation;
- status_t status = listAudioPatches(&numPatches, patches, &generation);
- reply->writeInt32(status);
- reply->writeInt32(numPatches);
- if (status == NO_ERROR) {
- if (numPatchesReq > numPatches) {
- numPatchesReq = numPatches;
- }
- reply->write(patches, numPatchesReq * sizeof(struct audio_patch));
- reply->writeInt32(generation);
- }
- free(patches);
- return NO_ERROR;
- }
-
- case SET_AUDIO_PORT_CONFIG: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- struct audio_port_config config = {};
- status_t status = data.read(&config, sizeof(struct audio_port_config));
- if (status != NO_ERROR) {
- return status;
- }
- (void)AudioSanitizer::sanitizeAudioPortConfig(&config);
- status = setAudioPortConfig(&config);
- reply->writeInt32(status);
- return NO_ERROR;
- }
-
- case REGISTER_CLIENT: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- sp<IAudioPolicyServiceClient> client = interface_cast<IAudioPolicyServiceClient>(
- data.readStrongBinder());
- registerClient(client);
- return NO_ERROR;
- } break;
-
- case SET_AUDIO_PORT_CALLBACK_ENABLED: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- setAudioPortCallbacksEnabled(data.readInt32() == 1);
- return NO_ERROR;
- } break;
-
- case SET_AUDIO_VOLUME_GROUP_CALLBACK_ENABLED: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- setAudioVolumeGroupCallbacksEnabled(data.readInt32() == 1);
- return NO_ERROR;
- } break;
-
- case ACQUIRE_SOUNDTRIGGER_SESSION: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_session_t session = AUDIO_SESSION_NONE;
- audio_io_handle_t ioHandle = AUDIO_IO_HANDLE_NONE;
- audio_devices_t device = AUDIO_DEVICE_NONE;
- status_t status = acquireSoundTriggerSession(&session, &ioHandle, &device);
- reply->writeInt32(status);
- if (status == NO_ERROR) {
- reply->writeInt32(session);
- reply->writeInt32(ioHandle);
- reply->writeInt32(device);
- }
- return NO_ERROR;
- } break;
-
- case RELEASE_SOUNDTRIGGER_SESSION: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_session_t session = (audio_session_t)data.readInt32();
- status_t status = releaseSoundTriggerSession(session);
- reply->writeInt32(status);
- return NO_ERROR;
- } break;
-
- case GET_PHONE_STATE: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- reply->writeInt32((int32_t)getPhoneState());
- return NO_ERROR;
- } break;
-
- case REGISTER_POLICY_MIXES: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- bool registration = data.readInt32() == 1;
- Vector<AudioMix> mixes;
- size_t size = (size_t)data.readInt32();
- if (size > MAX_MIXES_PER_POLICY) {
- size = MAX_MIXES_PER_POLICY;
- }
- for (size_t i = 0; i < size; i++) {
- AudioMix mix;
- if (mix.readFromParcel((Parcel*)&data) == NO_ERROR) {
- mixes.add(mix);
- }
- }
- status_t status = registerPolicyMixes(mixes, registration);
- reply->writeInt32(status);
- return NO_ERROR;
- } break;
-
- case START_AUDIO_SOURCE: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- struct audio_port_config source = {};
- status_t status = data.read(&source, sizeof(struct audio_port_config));
- if (status != NO_ERROR) {
- return status;
- }
- audio_attributes_t attributes = {};
- status = data.read(&attributes, sizeof(audio_attributes_t));
- if (status != NO_ERROR) {
- return status;
- }
- status = AudioSanitizer::sanitizeAudioPortConfig(&source);
- if (status == NO_ERROR) {
- // OK to not always sanitize attributes as startAudioSource() is not called if
- // the port config is invalid.
- status = AudioSanitizer::sanitizeAudioAttributes(&attributes, "68953950");
- }
- audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
- if (status == NO_ERROR) {
- status = startAudioSource(&source, &attributes, &portId);
- }
- reply->writeInt32(status);
- reply->writeInt32(portId);
- return NO_ERROR;
- } break;
-
- case STOP_AUDIO_SOURCE: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_port_handle_t portId = (audio_port_handle_t) data.readInt32();
- status_t status = stopAudioSource(portId);
- reply->writeInt32(status);
- return NO_ERROR;
- } break;
-
- case SET_MASTER_MONO: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- bool mono = static_cast<bool>(data.readInt32());
- status_t status = setMasterMono(mono);
- reply->writeInt32(status);
- return NO_ERROR;
- } break;
-
- case GET_MASTER_MONO: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- bool mono;
- status_t status = getMasterMono(&mono);
- reply->writeInt32(status);
- if (status == NO_ERROR) {
- reply->writeInt32(static_cast<int32_t>(mono));
- }
- return NO_ERROR;
- } break;
-
- case GET_STREAM_VOLUME_DB: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_stream_type_t stream =
- static_cast <audio_stream_type_t>(data.readInt32());
- int index = static_cast <int>(data.readInt32());
- audio_devices_t device =
- static_cast <audio_devices_t>(data.readUint32());
- reply->writeFloat(getStreamVolumeDB(stream, index, device));
- return NO_ERROR;
- }
-
- case GET_SURROUND_FORMATS: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- unsigned int numSurroundFormatsReq = data.readUint32();
- if (numSurroundFormatsReq > MAX_ITEMS_PER_LIST) {
- numSurroundFormatsReq = MAX_ITEMS_PER_LIST;
- }
- bool reported = data.readBool();
- unsigned int numSurroundFormats = numSurroundFormatsReq;
- audio_format_t *surroundFormats = (audio_format_t *)calloc(
- numSurroundFormats, sizeof(audio_format_t));
- bool *surroundFormatsEnabled = (bool *)calloc(numSurroundFormats, sizeof(bool));
- if (numSurroundFormatsReq > 0 &&
- (surroundFormats == NULL || surroundFormatsEnabled == NULL)) {
- free(surroundFormats);
- free(surroundFormatsEnabled);
- reply->writeInt32(NO_MEMORY);
- return NO_ERROR;
- }
- status_t status = getSurroundFormats(
- &numSurroundFormats, surroundFormats, surroundFormatsEnabled, reported);
- reply->writeInt32(status);
-
- if (status == NO_ERROR) {
- reply->writeUint32(numSurroundFormats);
- if (numSurroundFormatsReq > numSurroundFormats) {
- numSurroundFormatsReq = numSurroundFormats;
- }
- reply->write(surroundFormats, numSurroundFormatsReq * sizeof(audio_format_t));
- reply->write(surroundFormatsEnabled, numSurroundFormatsReq * sizeof(bool));
- }
- free(surroundFormats);
- free(surroundFormatsEnabled);
- return NO_ERROR;
- }
-
- case SET_SURROUND_FORMAT_ENABLED: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_format_t audioFormat = (audio_format_t) data.readInt32();
- bool enabled = data.readBool();
- status_t status = setSurroundFormatEnabled(audioFormat, enabled);
- reply->writeInt32(status);
- return NO_ERROR;
- }
-
- case GET_OFFLOAD_FORMATS_A2DP: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- std::vector<audio_format_t> encodingFormats;
- status_t status = getHwOffloadEncodingFormatsSupportedForA2DP(&encodingFormats);
- reply->writeInt32(status);
- if (status != NO_ERROR) {
- return NO_ERROR;
- }
- reply->writeUint32(static_cast<uint32_t>(encodingFormats.size()));
- for (size_t i = 0; i < encodingFormats.size(); i++)
- reply->writeInt32(static_cast<int32_t>(encodingFormats[i]));
- return NO_ERROR;
- }
-
-
- case ADD_STREAM_DEFAULT_EFFECT: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- effect_uuid_t type;
- status_t status = data.read(&type, sizeof(effect_uuid_t));
- if (status != NO_ERROR) {
- return status;
- }
- String16 opPackageName;
- status = data.readString16(&opPackageName);
- if (status != NO_ERROR) {
- return status;
- }
- effect_uuid_t uuid;
- status = data.read(&uuid, sizeof(effect_uuid_t));
- if (status != NO_ERROR) {
- return status;
- }
- int32_t priority = data.readInt32();
- audio_usage_t usage = (audio_usage_t) data.readInt32();
- audio_unique_id_t id = 0;
- reply->writeInt32(static_cast <int32_t>(addStreamDefaultEffect(&type,
- opPackageName,
- &uuid,
- priority,
- usage,
- &id)));
- reply->writeInt32(id);
- return NO_ERROR;
- }
-
- case REMOVE_STREAM_DEFAULT_EFFECT: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_unique_id_t id = static_cast<audio_unique_id_t>(data.readInt32());
- reply->writeInt32(static_cast <int32_t>(removeStreamDefaultEffect(id)));
- return NO_ERROR;
- }
-
- case ADD_SOURCE_DEFAULT_EFFECT: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- effect_uuid_t type;
- status_t status = data.read(&type, sizeof(effect_uuid_t));
- if (status != NO_ERROR) {
- return status;
- }
- String16 opPackageName;
- status = data.readString16(&opPackageName);
- if (status != NO_ERROR) {
- return status;
- }
- effect_uuid_t uuid;
- status = data.read(&uuid, sizeof(effect_uuid_t));
- if (status != NO_ERROR) {
- return status;
- }
- int32_t priority = data.readInt32();
- audio_source_t source = (audio_source_t) data.readInt32();
- audio_unique_id_t id = 0;
- reply->writeInt32(static_cast <int32_t>(addSourceDefaultEffect(&type,
- opPackageName,
- &uuid,
- priority,
- source,
- &id)));
- reply->writeInt32(id);
- return NO_ERROR;
- }
-
- case REMOVE_SOURCE_DEFAULT_EFFECT: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_unique_id_t id = static_cast<audio_unique_id_t>(data.readInt32());
- reply->writeInt32(static_cast <int32_t>(removeSourceDefaultEffect(id)));
- return NO_ERROR;
- }
-
- case SET_ASSISTANT_UID: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- int32_t uid;
- status_t status = data.readInt32(&uid);
- if (status != NO_ERROR) {
- return status;
- }
- status = setAssistantUid(uid);
- reply->writeInt32(static_cast <int32_t>(status));
- return NO_ERROR;
- }
-
- case SET_A11Y_SERVICES_UIDS: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- std::vector<uid_t> uids;
- int32_t size;
- status_t status = data.readInt32(&size);
- if (status != NO_ERROR) {
- return status;
- }
- if (size > MAX_ITEMS_PER_LIST) {
- size = MAX_ITEMS_PER_LIST;
- }
- for (int32_t i = 0; i < size; i++) {
- int32_t uid;
- status = data.readInt32(&uid);
- if (status != NO_ERROR) {
- return status;
- }
- uids.push_back(uid);
- }
- status = setA11yServicesUids(uids);
- reply->writeInt32(static_cast <int32_t>(status));
- return NO_ERROR;
- }
-
- case IS_HAPTIC_PLAYBACK_SUPPORTED: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- bool isSupported = isHapticPlaybackSupported();
- reply->writeBool(isSupported);
- return NO_ERROR;
- }
- case SET_UID_DEVICE_AFFINITY: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- const uid_t uid = (uid_t) data.readInt32();
- AudioDeviceTypeAddrVector devices;
- status_t status = data.readParcelableVector(&devices);
- if (status != NO_ERROR) {
- return status;
- }
- status = setUidDeviceAffinities(uid, devices);
- reply->writeInt32(status);
- return NO_ERROR;
- }
-
- case REMOVE_UID_DEVICE_AFFINITY: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- const uid_t uid = (uid_t) data.readInt32();
- status_t status = removeUidDeviceAffinities(uid);
- reply->writeInt32(status);
- return NO_ERROR;
- }
-
- case SET_USERID_DEVICE_AFFINITY: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- const int userId = (int) data.readInt32();
- AudioDeviceTypeAddrVector devices;
- status_t status = data.readParcelableVector(&devices);
- if (status != NO_ERROR) {
- return status;
- }
- status = setUserIdDeviceAffinities(userId, devices);
- reply->writeInt32(status);
- return NO_ERROR;
- }
-
- case REMOVE_USERID_DEVICE_AFFINITY: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- const int userId = (int) data.readInt32();
- status_t status = removeUserIdDeviceAffinities(userId);
- reply->writeInt32(status);
- return NO_ERROR;
- }
-
- case LIST_AUDIO_PRODUCT_STRATEGIES: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- AudioProductStrategyVector strategies;
- status_t status = listAudioProductStrategies(strategies);
- reply->writeInt32(status);
- if (status != NO_ERROR) {
- return NO_ERROR;
- }
- size_t size = strategies.size();
- size_t sizePosition = reply->dataPosition();
- reply->writeInt32(size);
- size_t finalSize = size;
- for (size_t i = 0; i < size; i++) {
- size_t position = reply->dataPosition();
- if (strategies[i].writeToParcel(reply) != NO_ERROR) {
- reply->setDataPosition(position);
- finalSize--;
- }
- }
- if (size != finalSize) {
- size_t position = reply->dataPosition();
- reply->setDataPosition(sizePosition);
- reply->writeInt32(finalSize);
- reply->setDataPosition(position);
- }
- return NO_ERROR;
- }
-
- case GET_STRATEGY_FOR_ATTRIBUTES: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- AudioAttributes attributes;
- status_t status = attributes.readFromParcel(&data);
- if (status != NO_ERROR) {
- return status;
- }
- product_strategy_t strategy;
- status = getProductStrategyFromAudioAttributes(attributes, strategy);
- reply->writeInt32(status);
- if (status != NO_ERROR) {
- return NO_ERROR;
- }
- reply->writeUint32(static_cast<int>(strategy));
- return NO_ERROR;
- }
-
- case LIST_AUDIO_VOLUME_GROUPS: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- AudioVolumeGroupVector groups;
- status_t status = listAudioVolumeGroups(groups);
- reply->writeInt32(status);
- if (status != NO_ERROR) {
- return NO_ERROR;
- }
- size_t size = groups.size();
- size_t sizePosition = reply->dataPosition();
- reply->writeInt32(size);
- size_t finalSize = size;
- for (size_t i = 0; i < size; i++) {
- size_t position = reply->dataPosition();
- if (groups[i].writeToParcel(reply) != NO_ERROR) {
- reply->setDataPosition(position);
- finalSize--;
- }
- }
- if (size != finalSize) {
- size_t position = reply->dataPosition();
- reply->setDataPosition(sizePosition);
- reply->writeInt32(finalSize);
- reply->setDataPosition(position);
- }
- return NO_ERROR;
- }
-
- case GET_VOLUME_GROUP_FOR_ATTRIBUTES: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- AudioAttributes attributes;
- status_t status = attributes.readFromParcel(&data);
- if (status != NO_ERROR) {
- return status;
- }
-
- volume_group_t group;
- status = getVolumeGroupFromAudioAttributes(attributes, group);
- if (status != NO_ERROR) {
- return NO_ERROR;
- }
-
- reply->writeInt32(status);
- reply->writeUint32(static_cast<int>(group));
- return NO_ERROR;
- }
-
- case SET_SUPPORTED_SYSTEM_USAGES: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- std::vector<audio_usage_t> systemUsages;
-
- int32_t size;
- status_t status = data.readInt32(&size);
- if (status != NO_ERROR) {
- return status;
- }
- if (size > MAX_ITEMS_PER_LIST) {
- size = MAX_ITEMS_PER_LIST;
- }
-
- for (int32_t i = 0; i < size; i++) {
- int32_t systemUsageInt;
- status = data.readInt32(&systemUsageInt);
- if (status != NO_ERROR) {
- return status;
- }
-
- audio_usage_t systemUsage = static_cast<audio_usage_t>(systemUsageInt);
- systemUsages.push_back(systemUsage);
- }
- status = setSupportedSystemUsages(systemUsages);
- reply->writeInt32(static_cast <int32_t>(status));
- return NO_ERROR;
- }
-
- case SET_ALLOWED_CAPTURE_POLICY: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- uid_t uid = data.readInt32();
- audio_flags_mask_t flags = static_cast<audio_flags_mask_t>(data.readInt32());
- status_t status = setAllowedCapturePolicy(uid, flags);
- reply->writeInt32(status);
- return NO_ERROR;
- }
-
- case SET_RTT_ENABLED: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- bool enabled = static_cast<bool>(data.readInt32());
- status_t status = setRttEnabled(enabled);
- reply->writeInt32(status);
- 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_DEVICES_ROLE_FOR_PRODUCT_STRATEGY: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- product_strategy_t strategy = (product_strategy_t) data.readUint32();
- device_role_t role = (device_role_t) data.readUint32();
- AudioDeviceTypeAddrVector devices;
- status_t status = data.readParcelableVector(&devices);
- if (status != NO_ERROR) {
- return status;
- }
- status = setDevicesRoleForStrategy(strategy, role, devices);
- reply->writeInt32(status);
- return NO_ERROR;
- }
-
- case REMOVE_DEVICES_ROLE_FOR_PRODUCT_STRATEGY: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- product_strategy_t strategy = (product_strategy_t) data.readUint32();
- device_role_t role = (device_role_t) data.readUint32();
- status_t status = removeDevicesRoleForStrategy(strategy, role);
- reply->writeInt32(status);
- return NO_ERROR;
- }
-
- case GET_DEVICES_FOR_ROLE_AND_PRODUCT_STRATEGY: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- product_strategy_t strategy = (product_strategy_t) data.readUint32();
- device_role_t role = (device_role_t) data.readUint32();
- AudioDeviceTypeAddrVector devices;
- status_t status = getDevicesForRoleAndStrategy(strategy, role, devices);
- status_t marshall_status = reply->writeParcelableVector(devices);
- if (marshall_status != NO_ERROR) {
- return marshall_status;
- }
- reply->writeInt32(status);
- 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;
- }
-
- case AUDIO_MODULES_UPDATED: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- onNewAudioModulesAvailable();
- return NO_ERROR;
- } break;
-
- case SET_CURRENT_IME_UID: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- int32_t uid;
- status_t status = data.readInt32(&uid);
- if (status != NO_ERROR) {
- return status;
- }
- status = setCurrentImeUid(uid);
- reply->writeInt32(static_cast <int32_t>(status));
- return NO_ERROR;
- }
-
- case REGISTER_SOUNDTRIGGER_CAPTURE_STATE_LISTENER: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- sp<IBinder> binder = data.readStrongBinder();
- if (binder == nullptr) {
- return BAD_VALUE;
- }
- sp<ICaptureStateListener>
- listener = interface_cast<ICaptureStateListener>(
- binder);
- if (listener == nullptr) {
- return BAD_VALUE;
- }
- bool ret;
- status_t status =
- registerSoundTriggerCaptureStateListener(listener, &ret);
- LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "Server returned unexpected status code: %d",
- status);
- status = reply->writeBool(ret);
- if (status != NO_ERROR) {
- return status;
- }
- return NO_ERROR;
- } break;
-
- case SET_DEVICES_ROLE_FOR_CAPTURE_PRESET: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_source_t audioSource = (audio_source_t) data.readUint32();
- device_role_t role = (device_role_t) data.readUint32();
- AudioDeviceTypeAddrVector devices;
- status_t status = data.readParcelableVector(&devices);
- if (status != NO_ERROR) {
- return status;
- }
- status = setDevicesRoleForCapturePreset(audioSource, role, devices);
- reply->writeInt32(status);
- return NO_ERROR;
- }
-
- case ADD_DEVICES_ROLE_FOR_CAPTURE_PRESET: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_source_t audioSource = (audio_source_t) data.readUint32();
- device_role_t role = (device_role_t) data.readUint32();
- AudioDeviceTypeAddrVector devices;
- status_t status = data.readParcelableVector(&devices);
- if (status != NO_ERROR) {
- return status;
- }
- status = addDevicesRoleForCapturePreset(audioSource, role, devices);
- reply->writeInt32(status);
- return NO_ERROR;
- }
-
- case REMOVE_DEVICES_ROLE_FOR_CAPTURE_PRESET: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_source_t audioSource = (audio_source_t) data.readUint32();
- device_role_t role = (device_role_t) data.readUint32();
- AudioDeviceTypeAddrVector devices;
- status_t status = data.readParcelableVector(&devices);
- if (status != NO_ERROR) {
- return status;
- }
- status = removeDevicesRoleForCapturePreset(audioSource, role, devices);
- reply->writeInt32(status);
- return NO_ERROR;
- }
-
- case CLEAR_DEVICES_ROLE_FOR_CAPTURE_PRESET: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_source_t audioSource = (audio_source_t) data.readUint32();
- device_role_t role = (device_role_t) data.readUint32();
- status_t status = clearDevicesRoleForCapturePreset(audioSource, role);
- reply->writeInt32(status);
- return NO_ERROR;
- }
-
- case GET_DEVICES_FOR_ROLE_AND_CAPTURE_PRESET: {
- CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_source_t audioSource = (audio_source_t) data.readUint32();
- device_role_t role = (device_role_t) data.readUint32();
- AudioDeviceTypeAddrVector devices;
- status_t status = getDevicesForRoleAndCapturePreset(audioSource, role, devices);
- status_t marshall_status = reply->writeParcelableVector(devices);
- if (marshall_status != NO_ERROR) {
- return marshall_status;
- }
- reply->writeInt32(status);
- return NO_ERROR;
- }
-
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-// ----------------------------------------------------------------------------
-
-} // namespace android
diff --git a/media/libaudioclient/IAudioPolicyServiceClient.cpp b/media/libaudioclient/IAudioPolicyServiceClient.cpp
deleted file mode 100644
index 0f9580c..0000000
--- a/media/libaudioclient/IAudioPolicyServiceClient.cpp
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 "IAudioPolicyServiceClient"
-#include <utils/Log.h>
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-
-#include <media/IAudioPolicyServiceClient.h>
-#include <media/AudioSystem.h>
-
-namespace android {
-
-enum {
- PORT_LIST_UPDATE = IBinder::FIRST_CALL_TRANSACTION,
- PATCH_LIST_UPDATE,
- MIX_STATE_UPDATE,
- RECORDING_CONFIGURATION_UPDATE,
- VOLUME_GROUP_CHANGED,
-};
-
-// ----------------------------------------------------------------------
-inline void readAudioConfigBaseFromParcel(const Parcel& data, audio_config_base_t *config) {
- config->sample_rate = data.readUint32();
- config->channel_mask = (audio_channel_mask_t) data.readInt32();
- config->format = (audio_format_t) data.readInt32();
-}
-
-inline void writeAudioConfigBaseToParcel(Parcel& data, const audio_config_base_t *config)
-{
- data.writeUint32(config->sample_rate);
- data.writeInt32((int32_t) config->channel_mask);
- data.writeInt32((int32_t) config->format);
-}
-
-inline void readRecordClientInfoFromParcel(const Parcel& data, record_client_info_t *clientInfo) {
- clientInfo->riid = (audio_unique_id_t) data.readInt32();
- clientInfo->uid = (uid_t) data.readUint32();
- clientInfo->session = (audio_session_t) data.readInt32();
- clientInfo->source = (audio_source_t) data.readInt32();
- data.read(&clientInfo->port_id, sizeof(audio_port_handle_t));
- clientInfo->silenced = data.readBool();
-}
-
-inline void writeRecordClientInfoToParcel(Parcel& data, const record_client_info_t *clientInfo) {
- data.writeInt32((int32_t) clientInfo->riid);
- data.writeUint32((uint32_t) clientInfo->uid);
- data.writeInt32((int32_t) clientInfo->session);
- data.writeInt32((int32_t) clientInfo->source);
- data.write(&clientInfo->port_id, sizeof(audio_port_handle_t));
- data.writeBool(clientInfo->silenced);
-}
-
-inline void readEffectVectorFromParcel(const Parcel& data,
- std::vector<effect_descriptor_t> *effects) {
- int32_t numEffects = data.readInt32();
- for (int32_t i = 0; i < numEffects; i++) {
- effect_descriptor_t effect;
- if (data.read(&effect, sizeof(effect_descriptor_t)) != NO_ERROR) {
- break;
- }
- (*effects).push_back(effect);
- }
-}
-
-inline void writeEffectVectorToParcel(Parcel& data, std::vector<effect_descriptor_t> effects) {
- data.writeUint32((uint32_t) effects.size());
- for (const auto& effect : effects) {
- if (data.write(&effect, sizeof(effect_descriptor_t)) != NO_ERROR) {
- break;
- }
- }
-}
-
-// ----------------------------------------------------------------------
-class BpAudioPolicyServiceClient : public BpInterface<IAudioPolicyServiceClient>
-{
-public:
- explicit BpAudioPolicyServiceClient(const sp<IBinder>& impl)
- : BpInterface<IAudioPolicyServiceClient>(impl)
- {
- }
-
- void onAudioPortListUpdate()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyServiceClient::getInterfaceDescriptor());
- remote()->transact(PORT_LIST_UPDATE, data, &reply, IBinder::FLAG_ONEWAY);
- }
-
- void onAudioPatchListUpdate()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyServiceClient::getInterfaceDescriptor());
- remote()->transact(PATCH_LIST_UPDATE, data, &reply, IBinder::FLAG_ONEWAY);
- }
-
- void onAudioVolumeGroupChanged(volume_group_t group, int flags)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyServiceClient::getInterfaceDescriptor());
- data.writeUint32(group);
- data.writeInt32(flags);
- remote()->transact(VOLUME_GROUP_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
- }
-
- void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyServiceClient::getInterfaceDescriptor());
- data.writeString8(regId);
- data.writeInt32(state);
- remote()->transact(MIX_STATE_UPDATE, data, &reply, IBinder::FLAG_ONEWAY);
- }
-
- void onRecordingConfigurationUpdate(int event,
- const record_client_info_t *clientInfo,
- const audio_config_base_t *clientConfig,
- std::vector<effect_descriptor_t> clientEffects,
- const audio_config_base_t *deviceConfig,
- std::vector<effect_descriptor_t> effects,
- audio_patch_handle_t patchHandle,
- audio_source_t source) {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioPolicyServiceClient::getInterfaceDescriptor());
- data.writeInt32(event);
- writeRecordClientInfoToParcel(data, clientInfo);
- writeAudioConfigBaseToParcel(data, clientConfig);
- writeEffectVectorToParcel(data, clientEffects);
- writeAudioConfigBaseToParcel(data, deviceConfig);
- writeEffectVectorToParcel(data, effects);
- data.writeInt32(patchHandle);
- data.writeInt32((int32_t) source);
- remote()->transact(RECORDING_CONFIGURATION_UPDATE, data, &reply, IBinder::FLAG_ONEWAY);
- }
-};
-
-IMPLEMENT_META_INTERFACE(AudioPolicyServiceClient, "android.media.IAudioPolicyServiceClient");
-
-// ----------------------------------------------------------------------
-
-status_t BnAudioPolicyServiceClient::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch (code) {
- case PORT_LIST_UPDATE: {
- CHECK_INTERFACE(IAudioPolicyServiceClient, data, reply);
- onAudioPortListUpdate();
- return NO_ERROR;
- } break;
- case PATCH_LIST_UPDATE: {
- CHECK_INTERFACE(IAudioPolicyServiceClient, data, reply);
- onAudioPatchListUpdate();
- return NO_ERROR;
- } break;
- case VOLUME_GROUP_CHANGED: {
- CHECK_INTERFACE(IAudioPolicyServiceClient, data, reply);
- volume_group_t group = static_cast<volume_group_t>(data.readUint32());
- int flags = data.readInt32();
- onAudioVolumeGroupChanged(group, flags);
- return NO_ERROR;
- } break;
- case MIX_STATE_UPDATE: {
- CHECK_INTERFACE(IAudioPolicyServiceClient, data, reply);
- String8 regId = data.readString8();
- int32_t state = data.readInt32();
- onDynamicPolicyMixStateUpdate(regId, state);
- return NO_ERROR;
- } break;
- case RECORDING_CONFIGURATION_UPDATE: {
- CHECK_INTERFACE(IAudioPolicyServiceClient, data, reply);
- int event = (int) data.readInt32();
- record_client_info_t clientInfo;
- audio_config_base_t clientConfig;
- audio_config_base_t deviceConfig;
- readRecordClientInfoFromParcel(data, &clientInfo);
- readAudioConfigBaseFromParcel(data, &clientConfig);
- std::vector<effect_descriptor_t> clientEffects;
- readEffectVectorFromParcel(data, &clientEffects);
- readAudioConfigBaseFromParcel(data, &deviceConfig);
- std::vector<effect_descriptor_t> effects;
- readEffectVectorFromParcel(data, &effects);
- audio_patch_handle_t patchHandle = (audio_patch_handle_t) data.readInt32();
- audio_source_t source = (audio_source_t) data.readInt32();
- onRecordingConfigurationUpdate(event, &clientInfo, &clientConfig, clientEffects,
- &deviceConfig, effects, patchHandle, source);
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-// ----------------------------------------------------------------------------
-
-} // namespace android
diff --git a/media/libaudioclient/IAudioTrack.cpp b/media/libaudioclient/IAudioTrack.cpp
deleted file mode 100644
index 6fcf300..0000000
--- a/media/libaudioclient/IAudioTrack.cpp
+++ /dev/null
@@ -1,462 +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 "IAudioTrack"
-//#define LOG_NDEBUG 0
-#include <utils/Log.h>
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-
-#include <media/IAudioTrack.h>
-
-namespace android {
-
-using media::VolumeShaper;
-
-enum {
- GET_CBLK = IBinder::FIRST_CALL_TRANSACTION,
- START,
- STOP,
- FLUSH,
- RESERVED, // was MUTE
- PAUSE,
- ATTACH_AUX_EFFECT,
- SET_PARAMETERS,
- SELECT_PRESENTATION,
- GET_TIMESTAMP,
- SIGNAL,
- APPLY_VOLUME_SHAPER,
- GET_VOLUME_SHAPER_STATE,
- SET_DUAL_MONO_MODE,
- GET_DUAL_MONO_MODE,
- SET_AUDIO_DESCRIPTION_MIX_LEVEL,
- GET_AUDIO_DESCRIPTION_MIX_LEVEL,
- SET_PLAYBACK_RATE_PARAMETERS,
- GET_PLAYBACK_RATE_PARAMETERS,
-};
-
-class BpAudioTrack : public BpInterface<IAudioTrack>
-{
-public:
- explicit BpAudioTrack(const sp<IBinder>& impl)
- : BpInterface<IAudioTrack>(impl)
- {
- }
-
- virtual sp<IMemory> getCblk() const
- {
- Parcel data, reply;
- sp<IMemory> cblk;
- data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
- status_t status = remote()->transact(GET_CBLK, data, &reply);
- if (status == NO_ERROR) {
- cblk = interface_cast<IMemory>(reply.readStrongBinder());
- if (cblk != 0 && cblk->unsecurePointer() == NULL) {
- cblk.clear();
- }
- }
- return cblk;
- }
-
- virtual status_t start()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
- status_t status = remote()->transact(START, data, &reply);
- if (status == NO_ERROR) {
- status = reply.readInt32();
- } else {
- ALOGW("start() error: %s", strerror(-status));
- }
- return status;
- }
-
- virtual void stop()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
- remote()->transact(STOP, data, &reply);
- }
-
- virtual void flush()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
- remote()->transact(FLUSH, data, &reply);
- }
-
- virtual void pause()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
- remote()->transact(PAUSE, data, &reply);
- }
-
- virtual status_t attachAuxEffect(int effectId)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
- data.writeInt32(effectId);
- status_t status = remote()->transact(ATTACH_AUX_EFFECT, data, &reply);
- if (status == NO_ERROR) {
- status = reply.readInt32();
- } else {
- ALOGW("attachAuxEffect() error: %s", strerror(-status));
- }
- return status;
- }
-
- virtual status_t setParameters(const String8& keyValuePairs) {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
- data.writeString8(keyValuePairs);
- status_t status = remote()->transact(SET_PARAMETERS, data, &reply);
- if (status == NO_ERROR) {
- status = reply.readInt32();
- }
- return status;
- }
-
- /* Selects the presentation (if available) */
- virtual status_t selectPresentation(int presentationId, int programId) {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
- data.writeInt32(presentationId);
- data.writeInt32(programId);
- status_t status = remote()->transact(SELECT_PRESENTATION, data, &reply);
- if (status == NO_ERROR) {
- status = reply.readInt32();
- }
- return status;
- }
-
- virtual status_t getTimestamp(AudioTimestamp& timestamp) {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
- status_t status = remote()->transact(GET_TIMESTAMP, data, &reply);
- if (status == NO_ERROR) {
- status = reply.readInt32();
- if (status == NO_ERROR) {
- timestamp.mPosition = reply.readInt32();
- timestamp.mTime.tv_sec = reply.readInt32();
- timestamp.mTime.tv_nsec = reply.readInt32();
- }
- }
- return status;
- }
-
- virtual void signal() {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
- remote()->transact(SIGNAL, data, &reply);
- }
-
- virtual VolumeShaper::Status applyVolumeShaper(
- const sp<VolumeShaper::Configuration>& configuration,
- const sp<VolumeShaper::Operation>& operation) {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
-
- status_t status = configuration.get() == nullptr
- ? data.writeInt32(0)
- : data.writeInt32(1)
- ?: configuration->writeToParcel(&data);
- if (status != NO_ERROR) {
- return VolumeShaper::Status(status);
- }
-
- status = operation.get() == nullptr
- ? status = data.writeInt32(0)
- : data.writeInt32(1)
- ?: operation->writeToParcel(&data);
- if (status != NO_ERROR) {
- return VolumeShaper::Status(status);
- }
-
- int32_t remoteVolumeShaperStatus;
- status = remote()->transact(APPLY_VOLUME_SHAPER, data, &reply)
- ?: reply.readInt32(&remoteVolumeShaperStatus);
-
- return VolumeShaper::Status(status ?: remoteVolumeShaperStatus);
- }
-
- virtual sp<VolumeShaper::State> getVolumeShaperState(int id) {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
-
- data.writeInt32(id);
- status_t status = remote()->transact(GET_VOLUME_SHAPER_STATE, data, &reply);
- if (status != NO_ERROR) {
- return nullptr;
- }
- sp<VolumeShaper::State> state = new VolumeShaper::State;
- status = state->readFromParcel(&reply);
- if (status != NO_ERROR) {
- return nullptr;
- }
- return state;
- }
-
- status_t getDualMonoMode(audio_dual_mono_mode_t* mode) override {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
- status_t status = remote()->transact(GET_DUAL_MONO_MODE, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = (status_t)reply.readInt32();
- if (status != NO_ERROR) {
- return status;
- }
- *mode = (audio_dual_mono_mode_t)reply.readInt32();
- return NO_ERROR;
- }
-
- status_t setDualMonoMode(audio_dual_mono_mode_t mode) override {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
- data.writeInt32((int32_t)mode);
- status_t status = remote()->transact(SET_DUAL_MONO_MODE, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- return reply.readInt32();
- }
-
- status_t getAudioDescriptionMixLevel(float* leveldB) override {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
- status_t status = remote()->transact(GET_AUDIO_DESCRIPTION_MIX_LEVEL, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = (status_t)reply.readInt32();
- if (status != NO_ERROR) {
- return status;
- }
- *leveldB = reply.readFloat();
- return NO_ERROR;
- }
-
- status_t setAudioDescriptionMixLevel(float leveldB) override {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
- data.writeFloat(leveldB);
- status_t status = remote()->transact(SET_AUDIO_DESCRIPTION_MIX_LEVEL, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- return reply.readInt32();
- }
-
- status_t getPlaybackRateParameters(audio_playback_rate_t* playbackRate) override {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
- status_t status = remote()->transact(GET_PLAYBACK_RATE_PARAMETERS, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- status = (status_t)reply.readInt32();
- if (status != NO_ERROR) {
- return status;
- }
- playbackRate->mSpeed = reply.readFloat();
- playbackRate->mPitch = reply.readFloat();
- playbackRate->mStretchMode =
- static_cast<audio_timestretch_stretch_mode_t>(reply.readInt32());
- playbackRate->mFallbackMode =
- static_cast<audio_timestretch_fallback_mode_t>(reply.readInt32());
- return NO_ERROR;
- }
-
- status_t setPlaybackRateParameters(const audio_playback_rate_t& playbackRate) override {
- Parcel data, reply;
- data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
- data.writeFloat(playbackRate.mSpeed);
- data.writeFloat(playbackRate.mPitch);
- data.writeInt32(playbackRate.mStretchMode);
- data.writeInt32(playbackRate.mFallbackMode);
- status_t status = remote()->transact(SET_PLAYBACK_RATE_PARAMETERS, data, &reply);
- if (status != NO_ERROR) {
- return status;
- }
- return reply.readInt32();
- }
-};
-
-IMPLEMENT_META_INTERFACE(AudioTrack, "android.media.IAudioTrack");
-
-// ----------------------------------------------------------------------
-
-status_t BnAudioTrack::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch (code) {
- case GET_CBLK: {
- CHECK_INTERFACE(IAudioTrack, data, reply);
- reply->writeStrongBinder(IInterface::asBinder(getCblk()));
- return NO_ERROR;
- } break;
- case START: {
- CHECK_INTERFACE(IAudioTrack, data, reply);
- reply->writeInt32(start());
- return NO_ERROR;
- } break;
- case STOP: {
- CHECK_INTERFACE(IAudioTrack, data, reply);
- stop();
- return NO_ERROR;
- } break;
- case FLUSH: {
- CHECK_INTERFACE(IAudioTrack, data, reply);
- flush();
- return NO_ERROR;
- } break;
- case PAUSE: {
- CHECK_INTERFACE(IAudioTrack, data, reply);
- pause();
- return NO_ERROR;
- }
- case ATTACH_AUX_EFFECT: {
- CHECK_INTERFACE(IAudioTrack, data, reply);
- reply->writeInt32(attachAuxEffect(data.readInt32()));
- return NO_ERROR;
- } break;
- case SET_PARAMETERS: {
- CHECK_INTERFACE(IAudioTrack, data, reply);
- String8 keyValuePairs(data.readString8());
- reply->writeInt32(setParameters(keyValuePairs));
- return NO_ERROR;
- } break;
- case SELECT_PRESENTATION: {
- CHECK_INTERFACE(IAudioTrack, data, reply);
- reply->writeInt32(selectPresentation(data.readInt32(), data.readInt32()));
- return NO_ERROR;
- } break;
- case GET_TIMESTAMP: {
- CHECK_INTERFACE(IAudioTrack, data, reply);
- AudioTimestamp timestamp;
- status_t status = getTimestamp(timestamp);
- reply->writeInt32(status);
- if (status == NO_ERROR) {
- reply->writeInt32(timestamp.mPosition);
- reply->writeInt32(timestamp.mTime.tv_sec);
- reply->writeInt32(timestamp.mTime.tv_nsec);
- }
- return NO_ERROR;
- } break;
- case SIGNAL: {
- CHECK_INTERFACE(IAudioTrack, data, reply);
- signal();
- return NO_ERROR;
- } break;
- case APPLY_VOLUME_SHAPER: {
- CHECK_INTERFACE(IAudioTrack, data, reply);
- sp<VolumeShaper::Configuration> configuration;
- sp<VolumeShaper::Operation> operation;
-
- int32_t present;
- status_t status = data.readInt32(&present);
- if (status == NO_ERROR && present != 0) {
- configuration = new VolumeShaper::Configuration();
- status = configuration->readFromParcel(&data);
- }
- status = status ?: data.readInt32(&present);
- if (status == NO_ERROR && present != 0) {
- operation = new VolumeShaper::Operation();
- status = operation->readFromParcel(&data);
- }
- if (status == NO_ERROR) {
- status = (status_t)applyVolumeShaper(configuration, operation);
- }
- reply->writeInt32(status);
- return NO_ERROR;
- } break;
- case GET_VOLUME_SHAPER_STATE: {
- CHECK_INTERFACE(IAudioTrack, data, reply);
- int id;
- status_t status = data.readInt32(&id);
- if (status == NO_ERROR) {
- sp<VolumeShaper::State> state = getVolumeShaperState(id);
- if (state.get() != nullptr) {
- status = state->writeToParcel(reply);
- }
- }
- return NO_ERROR;
- } break;
- case SET_DUAL_MONO_MODE: {
- CHECK_INTERFACE(IAudioTrack, data, reply);
- reply->writeInt32( setDualMonoMode((audio_dual_mono_mode_t)data.readInt32()) );
- return NO_ERROR;
- } break;
- case GET_DUAL_MONO_MODE: {
- CHECK_INTERFACE(IAudioTrack, data, reply);
- audio_dual_mono_mode_t mode;
- const status_t status = getDualMonoMode(&mode);
- reply->writeInt32((int32_t)status);
- if (status == NO_ERROR) {
- reply->writeInt32(mode);
- }
- return NO_ERROR;
- } break;
- case SET_AUDIO_DESCRIPTION_MIX_LEVEL: {
- CHECK_INTERFACE(IAudioTrack, data, reply);
- reply->writeInt32( setAudioDescriptionMixLevel(data.readFloat()) );
- return NO_ERROR;
- } break;
- case GET_AUDIO_DESCRIPTION_MIX_LEVEL: {
- CHECK_INTERFACE(IAudioTrack, data, reply);
- float f;
- const status_t status = getAudioDescriptionMixLevel(&f);
- reply->writeInt32((int32_t)status);
- if (status == NO_ERROR) {
- reply->writeFloat(f);
- }
- return NO_ERROR;
- } break;
- case SET_PLAYBACK_RATE_PARAMETERS: {
- CHECK_INTERFACE(IAudioTrack, data, reply);
- audio_playback_rate_t playbackRate = {
- data.readFloat(),
- data.readFloat(),
- static_cast<audio_timestretch_stretch_mode_t>(data.readInt32()),
- static_cast<audio_timestretch_fallback_mode_t>(data.readInt32())};
- reply->writeInt32( setPlaybackRateParameters(playbackRate) );
- return NO_ERROR;
- } break;
- case GET_PLAYBACK_RATE_PARAMETERS: {
- CHECK_INTERFACE(IAudioTrack, data, reply);
- audio_playback_rate_t playbackRate;
- const status_t status = getPlaybackRateParameters(&playbackRate);
- reply->writeInt32((int32_t)status);
- if (status == NO_ERROR) {
- reply->writeFloat(playbackRate.mSpeed);
- reply->writeFloat(playbackRate.mPitch);
- reply->writeInt32(playbackRate.mStretchMode);
- reply->writeInt32(playbackRate.mFallbackMode);
- }
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-} // namespace android
diff --git a/media/libaudioclient/IEffect.cpp b/media/libaudioclient/IEffect.cpp
deleted file mode 100644
index 5d47dff..0000000
--- a/media/libaudioclient/IEffect.cpp
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT 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 "IEffect"
-#include <utils/Log.h>
-#include <stdint.h>
-#include <sys/types.h>
-#include <binder/Parcel.h>
-#include <media/IEffect.h>
-
-namespace android {
-
-// Maximum command/reply size expected
-#define EFFECT_PARAM_SIZE_MAX 65536
-
-enum {
- ENABLE = IBinder::FIRST_CALL_TRANSACTION,
- DISABLE,
- COMMAND,
- DISCONNECT,
- GET_CBLK
-};
-
-class BpEffect: public BpInterface<IEffect>
-{
-public:
- explicit BpEffect(const sp<IBinder>& impl)
- : BpInterface<IEffect>(impl)
- {
- }
-
- status_t enable()
- {
- ALOGV("enable");
- Parcel data, reply;
- data.writeInterfaceToken(IEffect::getInterfaceDescriptor());
- remote()->transact(ENABLE, data, &reply);
- return reply.readInt32();
- }
-
- status_t disable()
- {
- ALOGV("disable");
- Parcel data, reply;
- data.writeInterfaceToken(IEffect::getInterfaceDescriptor());
- remote()->transact(DISABLE, data, &reply);
- return reply.readInt32();
- }
-
- status_t command(uint32_t cmdCode,
- uint32_t cmdSize,
- void *pCmdData,
- uint32_t *pReplySize,
- void *pReplyData)
- {
- ALOGV("command");
- Parcel data, reply;
- data.writeInterfaceToken(IEffect::getInterfaceDescriptor());
- data.writeInt32(cmdCode);
- int size = cmdSize;
- if (pCmdData == NULL) {
- size = 0;
- }
- data.writeInt32(size);
- if (size) {
- data.write(pCmdData, size);
- }
- if (pReplySize == NULL) {
- size = 0;
- } else {
- size = *pReplySize;
- }
- data.writeInt32(size);
-
- status_t status = remote()->transact(COMMAND, data, &reply);
- if (status == NO_ERROR) {
- status = reply.readInt32();
- }
- if (status != NO_ERROR) {
- if (pReplySize != NULL)
- *pReplySize = 0;
- return status;
- }
-
- size = reply.readInt32();
- if (size != 0 && pReplyData != NULL && pReplySize != NULL) {
- reply.read(pReplyData, size);
- *pReplySize = size;
- }
- return status;
- }
-
- void disconnect()
- {
- ALOGV("disconnect");
- Parcel data, reply;
- data.writeInterfaceToken(IEffect::getInterfaceDescriptor());
- remote()->transact(DISCONNECT, data, &reply);
- return;
- }
-
- virtual sp<IMemory> getCblk() const
- {
- Parcel data, reply;
- sp<IMemory> cblk;
- data.writeInterfaceToken(IEffect::getInterfaceDescriptor());
- status_t status = remote()->transact(GET_CBLK, data, &reply);
- if (status == NO_ERROR) {
- cblk = interface_cast<IMemory>(reply.readStrongBinder());
- if (cblk != 0 && cblk->unsecurePointer() == NULL) {
- cblk.clear();
- }
- }
- return cblk;
- }
- };
-
-IMPLEMENT_META_INTERFACE(Effect, "android.media.IEffect");
-
-// ----------------------------------------------------------------------
-
-status_t BnEffect::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch (code) {
- case ENABLE: {
- ALOGV("ENABLE");
- CHECK_INTERFACE(IEffect, data, reply);
- reply->writeInt32(enable());
- return NO_ERROR;
- } break;
-
- case DISABLE: {
- ALOGV("DISABLE");
- CHECK_INTERFACE(IEffect, data, reply);
- reply->writeInt32(disable());
- return NO_ERROR;
- } break;
-
- case COMMAND: {
- ALOGV("COMMAND");
- CHECK_INTERFACE(IEffect, data, reply);
- uint32_t cmdCode = data.readInt32();
- uint32_t cmdSize = data.readInt32();
- char *cmd = NULL;
- if (cmdSize) {
- if (cmdSize > EFFECT_PARAM_SIZE_MAX) {
- reply->writeInt32(NO_MEMORY);
- return NO_ERROR;
- }
- cmd = (char *)calloc(cmdSize, 1);
- if (cmd == NULL) {
- reply->writeInt32(NO_MEMORY);
- return NO_ERROR;
- }
- data.read(cmd, cmdSize);
- }
- uint32_t replySize = data.readInt32();
- uint32_t replySz = replySize;
- char *resp = NULL;
- if (replySize) {
- if (replySize > EFFECT_PARAM_SIZE_MAX) {
- free(cmd);
- reply->writeInt32(NO_MEMORY);
- return NO_ERROR;
- }
- resp = (char *)calloc(replySize, 1);
- if (resp == NULL) {
- free(cmd);
- reply->writeInt32(NO_MEMORY);
- return NO_ERROR;
- }
- }
- status_t status = command(cmdCode, cmdSize, cmd, &replySz, resp);
- reply->writeInt32(status);
- if (status == NO_ERROR) {
- if (replySz < replySize) {
- replySize = replySz;
- }
- reply->writeInt32(replySize);
- if (replySize) {
- reply->write(resp, replySize);
- }
- }
- if (cmd) {
- free(cmd);
- }
- if (resp) {
- free(resp);
- }
- return NO_ERROR;
- } break;
-
- case DISCONNECT: {
- ALOGV("DISCONNECT");
- CHECK_INTERFACE(IEffect, data, reply);
- disconnect();
- return NO_ERROR;
- } break;
-
- case GET_CBLK: {
- CHECK_INTERFACE(IEffect, data, reply);
- reply->writeStrongBinder(IInterface::asBinder(getCblk()));
- return NO_ERROR;
- } break;
-
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-// ----------------------------------------------------------------------------
-
-} // namespace android
diff --git a/media/libaudioclient/IEffectClient.cpp b/media/libaudioclient/IEffectClient.cpp
deleted file mode 100644
index 3f2c67d..0000000
--- a/media/libaudioclient/IEffectClient.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT 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 "IEffectClient"
-#include <utils/Log.h>
-#include <stdint.h>
-#include <sys/types.h>
-#include <media/IEffectClient.h>
-
-namespace android {
-
-enum {
- CONTROL_STATUS_CHANGED = IBinder::FIRST_CALL_TRANSACTION,
- ENABLE_STATUS_CHANGED,
- COMMAND_EXECUTED
-};
-
-class BpEffectClient: public BpInterface<IEffectClient>
-{
-public:
- explicit BpEffectClient(const sp<IBinder>& impl)
- : BpInterface<IEffectClient>(impl)
- {
- }
-
- void controlStatusChanged(bool controlGranted)
- {
- ALOGV("controlStatusChanged");
- Parcel data, reply;
- data.writeInterfaceToken(IEffectClient::getInterfaceDescriptor());
- data.writeInt32((uint32_t)controlGranted);
- remote()->transact(CONTROL_STATUS_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
- }
-
- void enableStatusChanged(bool enabled)
- {
- ALOGV("enableStatusChanged");
- Parcel data, reply;
- data.writeInterfaceToken(IEffectClient::getInterfaceDescriptor());
- data.writeInt32((uint32_t)enabled);
- remote()->transact(ENABLE_STATUS_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
- }
-
- void commandExecuted(uint32_t cmdCode,
- uint32_t cmdSize,
- void *pCmdData,
- uint32_t replySize,
- void *pReplyData)
- {
- ALOGV("commandExecuted");
- Parcel data, reply;
- data.writeInterfaceToken(IEffectClient::getInterfaceDescriptor());
- data.writeInt32(cmdCode);
- int size = cmdSize;
- if (pCmdData == NULL) {
- size = 0;
- }
- data.writeInt32(size);
- if (size) {
- data.write(pCmdData, size);
- }
- size = replySize;
- if (pReplyData == NULL) {
- size = 0;
- }
- data.writeInt32(size);
- if (size) {
- data.write(pReplyData, size);
- }
- remote()->transact(COMMAND_EXECUTED, data, &reply, IBinder::FLAG_ONEWAY);
- }
-
-};
-
-IMPLEMENT_META_INTERFACE(EffectClient, "android.media.IEffectClient");
-
-// ----------------------------------------------------------------------
-
-status_t BnEffectClient::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch (code) {
- case CONTROL_STATUS_CHANGED: {
- ALOGV("CONTROL_STATUS_CHANGED");
- CHECK_INTERFACE(IEffectClient, data, reply);
- bool hasControl = (bool)data.readInt32();
- controlStatusChanged(hasControl);
- return NO_ERROR;
- } break;
- case ENABLE_STATUS_CHANGED: {
- ALOGV("ENABLE_STATUS_CHANGED");
- CHECK_INTERFACE(IEffectClient, data, reply);
- bool enabled = (bool)data.readInt32();
- enableStatusChanged(enabled);
- return NO_ERROR;
- } break;
- case COMMAND_EXECUTED: {
- ALOGV("COMMAND_EXECUTED");
- CHECK_INTERFACE(IEffectClient, data, reply);
- uint32_t cmdCode = data.readInt32();
- uint32_t cmdSize = data.readInt32();
- char *cmd = NULL;
- if (cmdSize) {
- cmd = (char *)malloc(cmdSize);
- data.read(cmd, cmdSize);
- }
- uint32_t replySize = data.readInt32();
- char *resp = NULL;
- if (replySize) {
- resp = (char *)malloc(replySize);
- data.read(resp, replySize);
- }
- commandExecuted(cmdCode, cmdSize, cmd, replySize, resp);
- if (cmd) {
- free(cmd);
- }
- if (resp) {
- free(resp);
- }
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-// ----------------------------------------------------------------------------
-
-} // namespace android
diff --git a/media/libaudioclient/PlayerBase.cpp b/media/libaudioclient/PlayerBase.cpp
index b0c68e5..446a58c 100644
--- a/media/libaudioclient/PlayerBase.cpp
+++ b/media/libaudioclient/PlayerBase.cpp
@@ -15,20 +15,23 @@
*/
#include <binder/IServiceManager.h>
+#include <media/AidlConversionUtil.h>
#include <media/PlayerBase.h>
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
namespace android {
-
-using media::VolumeShaper;
+using aidl_utils::binderStatusFromStatusT;
+using media::VolumeShaperConfiguration;
+using media::VolumeShaperOperation;
//--------------------------------------------------------------------------------------------------
PlayerBase::PlayerBase() : BnPlayer(),
mPanMultiplierL(1.0f), mPanMultiplierR(1.0f),
mVolumeMultiplierL(1.0f), mVolumeMultiplierR(1.0f),
- mPIId(PLAYER_PIID_INVALID), mLastReportedEvent(PLAYER_STATE_UNKNOWN)
+ mPIId(PLAYER_PIID_INVALID), mLastReportedEvent(PLAYER_STATE_UNKNOWN),
+ mLastReportedDeviceId(AUDIO_PORT_HANDLE_NONE)
{
ALOGD("PlayerBase::PlayerBase()");
// use checkService() to avoid blocking if audio service is not up yet
@@ -46,11 +49,12 @@
baseDestroy();
}
-void PlayerBase::init(player_type_t playerType, audio_usage_t usage) {
+void PlayerBase::init(player_type_t playerType, audio_usage_t usage, audio_session_t sessionId) {
if (mAudioManager == 0) {
ALOGE("AudioPlayer realize: no audio service, player will not be registered");
} else {
- mPIId = mAudioManager->trackPlayer(playerType, usage, AUDIO_CONTENT_TYPE_UNKNOWN, this);
+ mPIId = mAudioManager->trackPlayer(playerType, usage, AUDIO_CONTENT_TYPE_UNKNOWN, this,
+ sessionId);
}
}
@@ -62,14 +66,26 @@
}
//------------------------------------------------------------------------------
-void PlayerBase::servicePlayerEvent(player_state_t event) {
+void PlayerBase::servicePlayerEvent(player_state_t event, audio_port_handle_t deviceId) {
if (mAudioManager != 0) {
- // only report state change
- Mutex::Autolock _l(mPlayerStateLock);
- if (event != mLastReportedEvent
- && mPIId != PLAYER_PIID_INVALID) {
- mLastReportedEvent = event;
- mAudioManager->playerEvent(mPIId, event);
+ bool changed = false;
+ {
+ Mutex::Autolock _l(mDeviceIdLock);
+ changed = mLastReportedDeviceId != deviceId;
+ mLastReportedDeviceId = deviceId;
+ }
+
+ {
+ Mutex::Autolock _l(mPlayerStateLock);
+ // PLAYER_UPDATE_DEVICE_ID is not saved as an actual state, instead it is used to update
+ // device ID only.
+ if ((event != PLAYER_UPDATE_DEVICE_ID) && (event != mLastReportedEvent)) {
+ mLastReportedEvent = event;
+ changed = true;
+ }
+ }
+ if (changed && (mPIId != PLAYER_PIID_INVALID)) {
+ mAudioManager->playerEvent(mPIId, event, deviceId);
}
}
}
@@ -82,14 +98,18 @@
}
//FIXME temporary method while some player state is outside of this class
-void PlayerBase::reportEvent(player_state_t event) {
- servicePlayerEvent(event);
+void PlayerBase::reportEvent(player_state_t event, audio_port_handle_t deviceId) {
+ servicePlayerEvent(event, deviceId);
}
-status_t PlayerBase::startWithStatus() {
+void PlayerBase::baseUpdateDeviceId(audio_port_handle_t deviceId) {
+ servicePlayerEvent(PLAYER_UPDATE_DEVICE_ID, deviceId);
+}
+
+status_t PlayerBase::startWithStatus(audio_port_handle_t deviceId) {
status_t status = playerStart();
if (status == NO_ERROR) {
- servicePlayerEvent(PLAYER_STATE_STARTED);
+ servicePlayerEvent(PLAYER_STATE_STARTED, deviceId);
} else {
ALOGW("PlayerBase::start() error %d", status);
}
@@ -99,18 +119,18 @@
status_t PlayerBase::pauseWithStatus() {
status_t status = playerPause();
if (status == NO_ERROR) {
- servicePlayerEvent(PLAYER_STATE_PAUSED);
+ servicePlayerEvent(PLAYER_STATE_PAUSED, AUDIO_PORT_HANDLE_NONE);
} else {
ALOGW("PlayerBase::pause() error %d", status);
}
return status;
}
-
status_t PlayerBase::stopWithStatus() {
status_t status = playerStop();
+
if (status == NO_ERROR) {
- servicePlayerEvent(PLAYER_STATE_STOPPED);
+ servicePlayerEvent(PLAYER_STATE_STOPPED, AUDIO_PORT_HANDLE_NONE);
} else {
ALOGW("PlayerBase::stop() error %d", status);
}
@@ -121,7 +141,12 @@
// Implementation of IPlayer
binder::Status PlayerBase::start() {
ALOGD("PlayerBase::start() from IPlayer");
- (void)startWithStatus();
+ audio_port_handle_t deviceId;
+ {
+ Mutex::Autolock _l(mDeviceIdLock);
+ deviceId = mLastReportedDeviceId;
+ }
+ (void)startWithStatus(deviceId);
return binder::Status::ok();
}
@@ -149,7 +174,7 @@
if (status != NO_ERROR) {
ALOGW("PlayerBase::setVolume() error %d", status);
}
- return binder::Status::fromStatusT(status);
+ return binderStatusFromStatusT(status);
}
binder::Status PlayerBase::setPan(float pan) {
@@ -169,7 +194,7 @@
if (status != NO_ERROR) {
ALOGW("PlayerBase::setPan() error %d", status);
}
- return binder::Status::fromStatusT(status);
+ return binderStatusFromStatusT(status);
}
binder::Status PlayerBase::setStartDelayMs(int32_t delayMs __unused) {
@@ -178,8 +203,8 @@
}
binder::Status PlayerBase::applyVolumeShaper(
- const VolumeShaper::Configuration& configuration __unused,
- const VolumeShaper::Operation& operation __unused) {
+ const VolumeShaperConfiguration& configuration __unused,
+ const VolumeShaperOperation& operation __unused) {
ALOGW("applyVolumeShaper() is not supported");
return binder::Status::ok();
}
diff --git a/media/libaudioclient/PolicyAidlConversion.cpp b/media/libaudioclient/PolicyAidlConversion.cpp
new file mode 100644
index 0000000..25fdb49
--- /dev/null
+++ b/media/libaudioclient/PolicyAidlConversion.cpp
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PolicyAidlConversion"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "media/PolicyAidlConversion.h"
+
+#include "media/AidlConversion.h"
+
+namespace android {
+
+using base::unexpected;
+
+ConversionResult<volume_group_t>
+aidl2legacy_int32_t_volume_group_t(int32_t aidl) {
+ return convertReinterpret<volume_group_t>(aidl);
+}
+
+ConversionResult<int32_t>
+legacy2aidl_volume_group_t_int32_t(volume_group_t legacy) {
+ return convertReinterpret<int32_t>(legacy);
+}
+
+ConversionResult<uint32_t>
+aidl2legacy_AudioMixType_uint32_t(media::AudioMixType aidl) {
+ switch (aidl) {
+ case media::AudioMixType::PLAYERS:
+ return MIX_TYPE_PLAYERS;
+ case media::AudioMixType::RECORDERS:
+ return MIX_TYPE_RECORDERS;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioMixType>
+legacy2aidl_uint32_t_AudioMixType(uint32_t legacy) {
+ switch (legacy) {
+ case MIX_TYPE_PLAYERS:
+ return media::AudioMixType::PLAYERS;
+ case MIX_TYPE_RECORDERS:
+ return media::AudioMixType::RECORDERS;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<uint32_t>
+aidl2legacy_AudioMixCallbackFlag_uint32_t(media::AudioMixCallbackFlag aidl) {
+ switch (aidl) {
+ case media::AudioMixCallbackFlag::NOTIFY_ACTIVITY:
+ return AudioMix::kCbFlagNotifyActivity;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioMixCallbackFlag>
+legacy2aidl_uint32_t_AudioMixCallbackFlag(uint32_t legacy) {
+ switch (legacy) {
+ case AudioMix::kCbFlagNotifyActivity:
+ return media::AudioMixCallbackFlag::NOTIFY_ACTIVITY;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<uint32_t>
+aidl2legacy_AudioMixCallbackFlag_uint32_t_mask(int32_t aidl) {
+ return convertBitmask<uint32_t, int32_t, uint32_t, media::AudioMixCallbackFlag>(
+ aidl,
+ aidl2legacy_AudioMixCallbackFlag_uint32_t,
+ indexToEnum_index<media::AudioMixCallbackFlag>,
+ enumToMask_bitmask<uint32_t, uint32_t>);
+}
+
+ConversionResult<int32_t>
+legacy2aidl_uint32_t_AudioMixCallbackFlag_mask(uint32_t legacy) {
+ return convertBitmask<int32_t, uint32_t, media::AudioMixCallbackFlag, uint32_t>(
+ legacy,
+ legacy2aidl_uint32_t_AudioMixCallbackFlag,
+ indexToEnum_bitmask<uint32_t>,
+ enumToMask_index<int32_t, media::AudioMixCallbackFlag>);
+}
+
+ConversionResult<uint32_t>
+aidl2legacy_AudioMixRouteFlag_uint32_t(media::AudioMixRouteFlag aidl) {
+ switch (aidl) {
+ case media::AudioMixRouteFlag::RENDER:
+ return MIX_ROUTE_FLAG_RENDER;
+ case media::AudioMixRouteFlag::LOOP_BACK:
+ return MIX_ROUTE_FLAG_LOOP_BACK;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioMixRouteFlag>
+legacy2aidl_uint32_t_AudioMixRouteFlag(uint32_t legacy) {
+ switch (legacy) {
+ case MIX_ROUTE_FLAG_RENDER:
+ return media::AudioMixRouteFlag::RENDER;
+ case MIX_ROUTE_FLAG_LOOP_BACK:
+ return media::AudioMixRouteFlag::LOOP_BACK;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<uint32_t>
+aidl2legacy_AudioMixRouteFlag_uint32_t_mask(int32_t aidl) {
+ return convertBitmask<uint32_t, int32_t, uint32_t, media::AudioMixRouteFlag>(
+ aidl,
+ aidl2legacy_AudioMixRouteFlag_uint32_t,
+ indexToEnum_index<media::AudioMixRouteFlag>,
+ enumToMask_bitmask<uint32_t, uint32_t>);
+}
+
+ConversionResult<int32_t>
+legacy2aidl_uint32_t_AudioMixRouteFlag_mask(uint32_t legacy) {
+ return convertBitmask<int32_t, uint32_t, media::AudioMixRouteFlag, uint32_t>(
+ legacy,
+ legacy2aidl_uint32_t_AudioMixRouteFlag,
+ indexToEnum_bitmask<uint32_t>,
+ enumToMask_index<int32_t, media::AudioMixRouteFlag>);
+}
+
+// This type is unnamed in the original definition, thus we name it here.
+using AudioMixMatchCriterionValue = decltype(AudioMixMatchCriterion::mValue);
+
+ConversionResult<AudioMixMatchCriterionValue>
+aidl2legacy_AudioMixMatchCriterionValue(
+ const media::AudioMixMatchCriterionValue& aidl,
+ uint32_t* rule) {
+ AudioMixMatchCriterionValue legacy;
+ *rule = 0;
+ switch (aidl.getTag()) {
+ case media::AudioMixMatchCriterionValue::usage:
+ legacy.mUsage = VALUE_OR_RETURN(
+ aidl2legacy_AudioUsage_audio_usage_t(UNION_GET(aidl, usage).value()));
+ *rule |= RULE_MATCH_ATTRIBUTE_USAGE;
+ return legacy;
+
+ case media::AudioMixMatchCriterionValue::source:
+ legacy.mSource = VALUE_OR_RETURN(
+ aidl2legacy_AudioSourceType_audio_source_t(UNION_GET(aidl, source).value()));
+ *rule |= RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET;
+ return legacy;
+
+ case media::AudioMixMatchCriterionValue::uid:
+ legacy.mUid = VALUE_OR_RETURN(
+ aidl2legacy_int32_t_uid_t(UNION_GET(aidl, uid).value()));
+ *rule |= RULE_MATCH_UID;
+ return legacy;
+
+ case media::AudioMixMatchCriterionValue::userId:
+ legacy.mUserId = VALUE_OR_RETURN(
+ convertIntegral<int>(UNION_GET(aidl, userId).value()));
+ *rule |= RULE_MATCH_USERID;
+ return legacy;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioMixMatchCriterionValue>
+legacy2aidl_AudioMixMatchCriterionValue(
+ const AudioMixMatchCriterionValue& legacy,
+ uint32_t rule) {
+ media::AudioMixMatchCriterionValue aidl;
+ switch (rule) {
+ case RULE_MATCH_ATTRIBUTE_USAGE:
+ UNION_SET(aidl, usage,
+ VALUE_OR_RETURN(legacy2aidl_audio_usage_t_AudioUsage(legacy.mUsage)));
+ break;
+
+ case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
+ UNION_SET(aidl, source,
+ VALUE_OR_RETURN(legacy2aidl_audio_source_t_AudioSourceType(legacy.mSource)));
+ break;
+
+ case RULE_MATCH_UID:
+ UNION_SET(aidl, uid, VALUE_OR_RETURN(legacy2aidl_uid_t_int32_t(legacy.mUid)));
+ break;
+
+ case RULE_MATCH_USERID:
+ UNION_SET(aidl, userId, VALUE_OR_RETURN(convertReinterpret<uint32_t>(legacy.mUserId)));
+ break;
+
+ default:
+ return unexpected(BAD_VALUE);
+ }
+ return aidl;
+}
+
+
+ConversionResult<AudioMixMatchCriterion>
+aidl2legacy_AudioMixMatchCriterion(const media::AudioMixMatchCriterion& aidl) {
+ AudioMixMatchCriterion legacy;
+ legacy.mValue = VALUE_OR_RETURN(
+ aidl2legacy_AudioMixMatchCriterionValue(aidl.value, &legacy.mRule));
+ if (aidl.invert) {
+ legacy.mRule |= RULE_EXCLUSION_MASK;
+ }
+ return legacy;
+}
+
+ConversionResult<media::AudioMixMatchCriterion>
+legacy2aidl_AudioMixMatchCriterion(const AudioMixMatchCriterion& legacy) {
+ media::AudioMixMatchCriterion aidl;
+ uint32_t rule = legacy.mRule;
+ if (rule & RULE_EXCLUSION_MASK) {
+ aidl.invert = true;
+ rule &= ~RULE_EXCLUSION_MASK;
+ }
+ aidl.value = VALUE_OR_RETURN(legacy2aidl_AudioMixMatchCriterionValue(legacy.mValue, rule));
+ return aidl;
+}
+
+ConversionResult<AudioMix>
+aidl2legacy_AudioMix(const media::AudioMix& aidl) {
+ AudioMix legacy;
+ RETURN_IF_ERROR(convertRange(aidl.criteria.begin(), aidl.criteria.end(),
+ std::back_inserter(legacy.mCriteria),
+ aidl2legacy_AudioMixMatchCriterion));
+ legacy.mMixType = VALUE_OR_RETURN(aidl2legacy_AudioMixType_uint32_t(aidl.mixType));
+ legacy.mFormat = VALUE_OR_RETURN(aidl2legacy_AudioConfig_audio_config_t(aidl.format));
+ legacy.mRouteFlags = VALUE_OR_RETURN(
+ aidl2legacy_AudioMixRouteFlag_uint32_t_mask(aidl.routeFlags));
+ legacy.mDeviceType = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_devices_t(aidl.device.type));
+ legacy.mDeviceAddress = VALUE_OR_RETURN(aidl2legacy_string_view_String8(aidl.device.address));
+ legacy.mCbFlags = VALUE_OR_RETURN(aidl2legacy_AudioMixCallbackFlag_uint32_t_mask(aidl.cbFlags));
+ legacy.mAllowPrivilegedMediaPlaybackCapture = aidl.allowPrivilegedMediaPlaybackCapture;
+ legacy.mVoiceCommunicationCaptureAllowed = aidl.voiceCommunicationCaptureAllowed;
+ return legacy;
+}
+
+ConversionResult<media::AudioMix>
+legacy2aidl_AudioMix(const AudioMix& legacy) {
+ media::AudioMix aidl;
+ aidl.criteria = VALUE_OR_RETURN(
+ convertContainer<std::vector<media::AudioMixMatchCriterion>>(
+ legacy.mCriteria,
+ legacy2aidl_AudioMixMatchCriterion));
+ aidl.mixType = VALUE_OR_RETURN(legacy2aidl_uint32_t_AudioMixType(legacy.mMixType));
+ aidl.format = VALUE_OR_RETURN(legacy2aidl_audio_config_t_AudioConfig(legacy.mFormat));
+ aidl.routeFlags = VALUE_OR_RETURN(
+ legacy2aidl_uint32_t_AudioMixRouteFlag_mask(legacy.mRouteFlags));
+ aidl.device.type = VALUE_OR_RETURN(legacy2aidl_audio_devices_t_int32_t(legacy.mDeviceType));
+ aidl.device.address = VALUE_OR_RETURN(legacy2aidl_String8_string(legacy.mDeviceAddress));
+ aidl.cbFlags = VALUE_OR_RETURN(legacy2aidl_uint32_t_AudioMixCallbackFlag_mask(legacy.mCbFlags));
+ aidl.allowPrivilegedMediaPlaybackCapture = legacy.mAllowPrivilegedMediaPlaybackCapture;
+ aidl.voiceCommunicationCaptureAllowed = legacy.mVoiceCommunicationCaptureAllowed;
+ return aidl;
+}
+
+ConversionResult<audio_policy_dev_state_t>
+aidl2legacy_AudioPolicyDeviceState_audio_policy_dev_state_t(media::AudioPolicyDeviceState aidl) {
+ switch (aidl) {
+ case media::AudioPolicyDeviceState::UNAVAILABLE:
+ return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE;
+ case media::AudioPolicyDeviceState::AVAILABLE:
+ return AUDIO_POLICY_DEVICE_STATE_AVAILABLE;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioPolicyDeviceState>
+legacy2aidl_audio_policy_dev_state_t_AudioPolicyDeviceState(audio_policy_dev_state_t legacy) {
+ switch (legacy) {
+ case AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE:
+ return media::AudioPolicyDeviceState::UNAVAILABLE;
+ case AUDIO_POLICY_DEVICE_STATE_AVAILABLE:
+ return media::AudioPolicyDeviceState::AVAILABLE;
+ case AUDIO_POLICY_DEVICE_STATE_CNT:
+ break;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_policy_force_use_t>
+aidl2legacy_AudioPolicyForceUse_audio_policy_force_use_t(media::AudioPolicyForceUse aidl) {
+ switch (aidl) {
+ case media::AudioPolicyForceUse::COMMUNICATION:
+ return AUDIO_POLICY_FORCE_FOR_COMMUNICATION;
+ case media::AudioPolicyForceUse::MEDIA:
+ return AUDIO_POLICY_FORCE_FOR_MEDIA;
+ case media::AudioPolicyForceUse::RECORD:
+ return AUDIO_POLICY_FORCE_FOR_RECORD;
+ case media::AudioPolicyForceUse::DOCK:
+ return AUDIO_POLICY_FORCE_FOR_DOCK;
+ case media::AudioPolicyForceUse::SYSTEM:
+ return AUDIO_POLICY_FORCE_FOR_SYSTEM;
+ case media::AudioPolicyForceUse::HDMI_SYSTEM_AUDIO:
+ return AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO;
+ case media::AudioPolicyForceUse::ENCODED_SURROUND:
+ return AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND;
+ case media::AudioPolicyForceUse::VIBRATE_RINGING:
+ return AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioPolicyForceUse>
+legacy2aidl_audio_policy_force_use_t_AudioPolicyForceUse(audio_policy_force_use_t legacy) {
+ switch (legacy) {
+ case AUDIO_POLICY_FORCE_FOR_COMMUNICATION:
+ return media::AudioPolicyForceUse::COMMUNICATION;
+ case AUDIO_POLICY_FORCE_FOR_MEDIA:
+ return media::AudioPolicyForceUse::MEDIA;
+ case AUDIO_POLICY_FORCE_FOR_RECORD:
+ return media::AudioPolicyForceUse::RECORD;
+ case AUDIO_POLICY_FORCE_FOR_DOCK:
+ return media::AudioPolicyForceUse::DOCK;
+ case AUDIO_POLICY_FORCE_FOR_SYSTEM:
+ return media::AudioPolicyForceUse::SYSTEM;
+ case AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO:
+ return media::AudioPolicyForceUse::HDMI_SYSTEM_AUDIO;
+ case AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND:
+ return media::AudioPolicyForceUse::ENCODED_SURROUND;
+ case AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING:
+ return media::AudioPolicyForceUse::VIBRATE_RINGING;
+ case AUDIO_POLICY_FORCE_USE_CNT:
+ break;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_policy_forced_cfg_t>
+aidl2legacy_AudioPolicyForcedConfig_audio_policy_forced_cfg_t(media::AudioPolicyForcedConfig aidl) {
+ switch (aidl) {
+ case media::AudioPolicyForcedConfig::NONE:
+ return AUDIO_POLICY_FORCE_NONE;
+ case media::AudioPolicyForcedConfig::SPEAKER:
+ return AUDIO_POLICY_FORCE_SPEAKER;
+ case media::AudioPolicyForcedConfig::HEADPHONES:
+ return AUDIO_POLICY_FORCE_HEADPHONES;
+ case media::AudioPolicyForcedConfig::BT_SCO:
+ return AUDIO_POLICY_FORCE_BT_SCO;
+ case media::AudioPolicyForcedConfig::BT_A2DP:
+ return AUDIO_POLICY_FORCE_BT_A2DP;
+ case media::AudioPolicyForcedConfig::WIRED_ACCESSORY:
+ return AUDIO_POLICY_FORCE_WIRED_ACCESSORY;
+ case media::AudioPolicyForcedConfig::BT_CAR_DOCK:
+ return AUDIO_POLICY_FORCE_BT_CAR_DOCK;
+ case media::AudioPolicyForcedConfig::BT_DESK_DOCK:
+ return AUDIO_POLICY_FORCE_BT_DESK_DOCK;
+ case media::AudioPolicyForcedConfig::ANALOG_DOCK:
+ return AUDIO_POLICY_FORCE_ANALOG_DOCK;
+ case media::AudioPolicyForcedConfig::DIGITAL_DOCK:
+ return AUDIO_POLICY_FORCE_DIGITAL_DOCK;
+ case media::AudioPolicyForcedConfig::NO_BT_A2DP:
+ return AUDIO_POLICY_FORCE_NO_BT_A2DP;
+ case media::AudioPolicyForcedConfig::SYSTEM_ENFORCED:
+ return AUDIO_POLICY_FORCE_SYSTEM_ENFORCED;
+ case media::AudioPolicyForcedConfig::HDMI_SYSTEM_AUDIO_ENFORCED:
+ return AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED;
+ case media::AudioPolicyForcedConfig::ENCODED_SURROUND_NEVER:
+ return AUDIO_POLICY_FORCE_ENCODED_SURROUND_NEVER;
+ case media::AudioPolicyForcedConfig::ENCODED_SURROUND_ALWAYS:
+ return AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS;
+ case media::AudioPolicyForcedConfig::ENCODED_SURROUND_MANUAL:
+ return AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioPolicyForcedConfig>
+legacy2aidl_audio_policy_forced_cfg_t_AudioPolicyForcedConfig(audio_policy_forced_cfg_t legacy) {
+ switch (legacy) {
+ case AUDIO_POLICY_FORCE_NONE:
+ return media::AudioPolicyForcedConfig::NONE;
+ case AUDIO_POLICY_FORCE_SPEAKER:
+ return media::AudioPolicyForcedConfig::SPEAKER;
+ case AUDIO_POLICY_FORCE_HEADPHONES:
+ return media::AudioPolicyForcedConfig::HEADPHONES;
+ case AUDIO_POLICY_FORCE_BT_SCO:
+ return media::AudioPolicyForcedConfig::BT_SCO;
+ case AUDIO_POLICY_FORCE_BT_A2DP:
+ return media::AudioPolicyForcedConfig::BT_A2DP;
+ case AUDIO_POLICY_FORCE_WIRED_ACCESSORY:
+ return media::AudioPolicyForcedConfig::WIRED_ACCESSORY;
+ case AUDIO_POLICY_FORCE_BT_CAR_DOCK:
+ return media::AudioPolicyForcedConfig::BT_CAR_DOCK;
+ case AUDIO_POLICY_FORCE_BT_DESK_DOCK:
+ return media::AudioPolicyForcedConfig::BT_DESK_DOCK;
+ case AUDIO_POLICY_FORCE_ANALOG_DOCK:
+ return media::AudioPolicyForcedConfig::ANALOG_DOCK;
+ case AUDIO_POLICY_FORCE_DIGITAL_DOCK:
+ return media::AudioPolicyForcedConfig::DIGITAL_DOCK;
+ case AUDIO_POLICY_FORCE_NO_BT_A2DP:
+ return media::AudioPolicyForcedConfig::NO_BT_A2DP;
+ case AUDIO_POLICY_FORCE_SYSTEM_ENFORCED:
+ return media::AudioPolicyForcedConfig::SYSTEM_ENFORCED;
+ case AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED:
+ return media::AudioPolicyForcedConfig::HDMI_SYSTEM_AUDIO_ENFORCED;
+ case AUDIO_POLICY_FORCE_ENCODED_SURROUND_NEVER:
+ return media::AudioPolicyForcedConfig::ENCODED_SURROUND_NEVER;
+ case AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS:
+ return media::AudioPolicyForcedConfig::ENCODED_SURROUND_ALWAYS;
+ case AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL:
+ return media::AudioPolicyForcedConfig::ENCODED_SURROUND_MANUAL;
+ case AUDIO_POLICY_FORCE_CFG_CNT:
+ break;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<device_role_t>
+aidl2legacy_DeviceRole_device_role_t(media::DeviceRole aidl) {
+ switch (aidl) {
+ case media::DeviceRole::NONE:
+ return DEVICE_ROLE_NONE;
+ case media::DeviceRole::PREFERRED:
+ return DEVICE_ROLE_PREFERRED;
+ case media::DeviceRole::DISABLED:
+ return DEVICE_ROLE_DISABLED;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::DeviceRole>
+legacy2aidl_device_role_t_DeviceRole(device_role_t legacy) {
+ switch (legacy) {
+ case DEVICE_ROLE_NONE:
+ return media::DeviceRole::NONE;
+ case DEVICE_ROLE_PREFERRED:
+ return media::DeviceRole::PREFERRED;
+ case DEVICE_ROLE_DISABLED:
+ return media::DeviceRole::DISABLED;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_offload_mode_t>
+aidl2legacy_AudioOffloadMode_audio_offload_mode_t(media::AudioOffloadMode aidl) {
+ switch (aidl) {
+ case media::AudioOffloadMode::NOT_SUPPORTED:
+ return AUDIO_OFFLOAD_NOT_SUPPORTED;
+ case media::AudioOffloadMode::SUPPORTED:
+ return AUDIO_OFFLOAD_SUPPORTED;
+ case media::AudioOffloadMode::GAPLESS_SUPPORTED:
+ return AUDIO_OFFLOAD_GAPLESS_SUPPORTED;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioOffloadMode>
+legacy2aidl_audio_offload_mode_t_AudioOffloadMode(audio_offload_mode_t legacy) {
+ switch (legacy) {
+ case AUDIO_OFFLOAD_NOT_SUPPORTED:
+ return media::AudioOffloadMode::NOT_SUPPORTED;
+ case AUDIO_OFFLOAD_SUPPORTED:
+ return media::AudioOffloadMode::SUPPORTED;
+ case AUDIO_OFFLOAD_GAPLESS_SUPPORTED:
+ return media::AudioOffloadMode::GAPLESS_SUPPORTED;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+} // namespace android
diff --git a/media/libaudioclient/ToneGenerator.cpp b/media/libaudioclient/ToneGenerator.cpp
index ee78a2d..451c4b1 100644
--- a/media/libaudioclient/ToneGenerator.cpp
+++ b/media/libaudioclient/ToneGenerator.cpp
@@ -17,6 +17,8 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "ToneGenerator"
+#include <utility>
+
#include <math.h>
#include <utils/Log.h>
#include <cutils/properties.h>
@@ -25,6 +27,7 @@
namespace android {
+using media::permission::Identity;
// Descriptors for all available tones (See ToneGenerator::ToneDescriptor class declaration for details)
const ToneGenerator::ToneDescriptor ToneGenerator::sToneDescriptors[] = {
@@ -740,6 +743,11 @@
{ .duration = 0 , .waveFreq = { 0 }, 0, 0}},
.repeatCnt = ToneGenerator::TONEGEN_INF,
.repeatSegment = 0 }, // TONE_JAPAN_RADIO_ACK
+ { .segments = { { .duration = 1000, .waveFreq = { 400, 0 }, 0, 0 },
+ { .duration = 2000, .waveFreq = { 0 }, 0, 0 },
+ { .duration = 0 , .waveFreq = { 0 }, 0, 0}},
+ .repeatCnt = ToneGenerator::TONEGEN_INF,
+ .repeatSegment = 0 }, // TONE_JAPAN_RINGTONE
{ .segments = { { .duration = 375, .waveFreq = { 400, 0 }, 0, 0 },
{ .duration = 375, .waveFreq = { 0 }, 0, 0 },
{ .duration = 0 , .waveFreq = { 0 }, 0, 0}},
@@ -881,7 +889,7 @@
TONE_SUP_RADIO_NOTAVAIL, // TONE_SUP_RADIO_NOTAVAIL
TONE_SUP_ERROR, // TONE_SUP_ERROR
TONE_SUP_CALL_WAITING, // TONE_SUP_CALL_WAITING
- TONE_SUP_RINGTONE // TONE_SUP_RINGTONE
+ TONE_JAPAN_RINGTONE // TONE_SUP_RINGTONE
},
{ // GB
TONE_ANSI_DIAL, // TONE_SUP_DIAL
@@ -979,7 +987,9 @@
// none
//
////////////////////////////////////////////////////////////////////////////////
-ToneGenerator::ToneGenerator(audio_stream_type_t streamType, float volume, bool threadCanCallJava) {
+ToneGenerator::ToneGenerator(audio_stream_type_t streamType, float volume, bool threadCanCallJava,
+ std::string opPackageName)
+ : mOpPackageName(std::move(opPackageName)) {
ALOGV("ToneGenerator constructor: streamType=%d, volume=%f", streamType, volume);
@@ -1250,7 +1260,10 @@
////////////////////////////////////////////////////////////////////////////////
bool ToneGenerator::initAudioTrack() {
// Open audio track in mono, PCM 16bit, default sampling rate.
- mpAudioTrack = new AudioTrack();
+ // TODO b/182392769: use identity util
+ Identity identity = Identity();
+ identity.packageName = mOpPackageName;
+ mpAudioTrack = new AudioTrack(identity);
ALOGV("AudioTrack(%p) created", mpAudioTrack.get());
audio_attributes_t attr;
@@ -1276,8 +1289,7 @@
AUDIO_SESSION_ALLOCATE,
AudioTrack::TRANSFER_CALLBACK,
nullptr,
- AUDIO_UID_INVALID,
- -1,
+ identity,
&attr);
// Set caller name so it can be logged in destructor.
// MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_TONEGENERATOR
diff --git a/media/libaudioclient/TrackPlayerBase.cpp b/media/libaudioclient/TrackPlayerBase.cpp
index 0a914fc..188f321 100644
--- a/media/libaudioclient/TrackPlayerBase.cpp
+++ b/media/libaudioclient/TrackPlayerBase.cpp
@@ -17,7 +17,7 @@
#include <media/TrackPlayerBase.h>
namespace android {
-
+using aidl_utils::binderStatusFromStatusT;
using media::VolumeShaper;
//--------------------------------------------------------------------------------------------------
@@ -33,9 +33,15 @@
doDestroy();
}
-void TrackPlayerBase::init(AudioTrack* pat, player_type_t playerType, audio_usage_t usage) {
- PlayerBase::init(playerType, usage);
+void TrackPlayerBase::init(AudioTrack* pat, player_type_t playerType, audio_usage_t usage,
+ audio_session_t sessionId) {
+ PlayerBase::init(playerType, usage, sessionId);
mAudioTrack = pat;
+ if (mAudioTrack != 0) {
+ mSelfAudioDeviceCallback = new SelfAudioDeviceCallback(*this);
+ mAudioTrack->addAudioDeviceCallback(mSelfAudioDeviceCallback);
+ mAudioTrack->setPlayerIId(mPIId); // set in PlayerBase::init().
+ }
}
void TrackPlayerBase::destroy() {
@@ -43,9 +49,23 @@
baseDestroy();
}
+TrackPlayerBase::SelfAudioDeviceCallback::SelfAudioDeviceCallback(PlayerBase& self) :
+ AudioSystem::AudioDeviceCallback(), mSelf(self) {
+}
+
+TrackPlayerBase::SelfAudioDeviceCallback::~SelfAudioDeviceCallback() {
+}
+
+void TrackPlayerBase::SelfAudioDeviceCallback::onAudioDeviceUpdate(audio_io_handle_t __unused,
+ audio_port_handle_t deviceId) {
+ mSelf.baseUpdateDeviceId(deviceId);
+}
+
void TrackPlayerBase::doDestroy() {
if (mAudioTrack != 0) {
mAudioTrack->stop();
+ mAudioTrack->removeAudioDeviceCallback(mSelfAudioDeviceCallback);
+ mSelfAudioDeviceCallback.clear();
// Note that there may still be another reference in post-unlock phase of SetPlayState
mAudioTrack.clear();
}
@@ -106,11 +126,17 @@
binder::Status TrackPlayerBase::applyVolumeShaper(
- const VolumeShaper::Configuration& configuration,
- const VolumeShaper::Operation& operation) {
+ const media::VolumeShaperConfiguration& configuration,
+ const media::VolumeShaperOperation& operation) {
- sp<VolumeShaper::Configuration> spConfiguration = new VolumeShaper::Configuration(configuration);
- sp<VolumeShaper::Operation> spOperation = new VolumeShaper::Operation(operation);
+ sp<VolumeShaper::Configuration> spConfiguration = new VolumeShaper::Configuration();
+ sp<VolumeShaper::Operation> spOperation = new VolumeShaper::Operation();
+
+ status_t s = spConfiguration->readFromParcelable(configuration)
+ ?: spOperation->readFromParcelable(operation);
+ if (s != OK) {
+ return binderStatusFromStatusT(s);
+ }
if (mAudioTrack != 0) {
ALOGD("TrackPlayerBase::applyVolumeShaper() from IPlayer");
@@ -118,7 +144,7 @@
if (status < 0) { // a non-negative value is the volume shaper id.
ALOGE("TrackPlayerBase::applyVolumeShaper() failed with status %d", status);
}
- return binder::Status::fromStatusT(status);
+ return binderStatusFromStatusT(status);
} else {
ALOGD("TrackPlayerBase::applyVolumeShaper()"
" no AudioTrack for volume control from IPlayer");
diff --git a/media/libaudioclient/aidl/android/media/AudioAttributesEx.aidl b/media/libaudioclient/aidl/android/media/AudioAttributesEx.aidl
new file mode 100644
index 0000000..04a02c7
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioAttributesEx.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioAttributesInternal;
+import android.media.AudioStreamType;
+
+/**
+ * This is the equivalent of the android::AudioAttributes C++ type.
+ * {@hide}
+ */
+parcelable AudioAttributesEx {
+ AudioAttributesInternal attributes;
+ AudioStreamType streamType;
+ /** Interpreted as volume_group_t. */
+ int groupId;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioAttributesInternal.aidl b/media/libaudioclient/aidl/android/media/AudioAttributesInternal.aidl
new file mode 100644
index 0000000..699df0a
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioAttributesInternal.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioContentType;
+import android.media.AudioSourceType;
+import android.media.AudioUsage;
+
+/**
+ * The "Internal" suffix of this type name is to disambiguate it from the
+ * android.media.AudioAttributes SDK type.
+ * {@hide}
+ */
+parcelable AudioAttributesInternal {
+ AudioContentType contentType;
+ AudioUsage usage;
+ AudioSourceType source;
+ // Bitmask, indexed by AudioFlag.
+ int flags;
+ @utf8InCpp String tags; /* UTF8 */
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioClient.aidl b/media/libaudioclient/aidl/android/media/AudioClient.aidl
new file mode 100644
index 0000000..aa4d8f5
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioClient.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.permission.Identity;
+
+/**
+ * {@hide}
+ */
+parcelable AudioClient {
+ /** Interpreted as pid_t. */
+ int clientTid;
+ Identity identity;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioConfig.aidl b/media/libaudioclient/aidl/android/media/AudioConfig.aidl
new file mode 100644
index 0000000..8dc97d3
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioConfig.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioOffloadInfo;
+import android.media.audio.common.AudioFormat;
+
+/**
+ * {@hide}
+ */
+parcelable AudioConfig {
+ int sampleRate;
+ /**
+ * Interpreted as audio_channel_mask_t.
+ * TODO(ytai): Create a designated type.
+ */
+ int channelMask;
+ AudioFormat format;
+ AudioOffloadInfo offloadInfo;
+ long frameCount;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioConfigBase.aidl b/media/libaudioclient/aidl/android/media/AudioConfigBase.aidl
new file mode 100644
index 0000000..8353c0d
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioConfigBase.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.audio.common.AudioFormat;
+
+/**
+ * {@hide}
+ */
+parcelable AudioConfigBase {
+ int sampleRate;
+ /** Interpreted as audio_channel_mask_t. */
+ int channelMask;
+ AudioFormat format;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioContentType.aidl b/media/libaudioclient/aidl/android/media/AudioContentType.aidl
new file mode 100644
index 0000000..f734fba
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioContentType.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+@Backing(type="int")
+enum AudioContentType {
+ UNKNOWN = 0,
+ SPEECH = 1,
+ MUSIC = 2,
+ MOVIE = 3,
+ SONIFICATION = 4,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioDevice.aidl b/media/libaudioclient/aidl/android/media/AudioDevice.aidl
new file mode 100644
index 0000000..b200697
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioDevice.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * {@hide}
+ */
+parcelable AudioDevice {
+ /** Interpreted as audio_devices_t. */
+ int type;
+ @utf8InCpp String address;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioDualMonoMode.aidl b/media/libaudioclient/aidl/android/media/AudioDualMonoMode.aidl
new file mode 100644
index 0000000..f6220c2
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioDualMonoMode.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+// TODO(b/175167149): Reconcile AudioDualMonoMode with framework-media-sources
+
+@Backing(type="int")
+enum AudioDualMonoMode {
+ OFF = 0,
+ LR = 1,
+ LL = 2,
+ RR = 3,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioEncapsulationMetadataType.aidl b/media/libaudioclient/aidl/android/media/AudioEncapsulationMetadataType.aidl
new file mode 100644
index 0000000..b03adfe
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioEncapsulationMetadataType.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioEncapsulationMetadataType {
+ NONE = 0,
+ FRAMEWORK_TUNER = 1,
+ DVB_AD_DESCRIPTOR = 2,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioEncapsulationMode.aidl b/media/libaudioclient/aidl/android/media/AudioEncapsulationMode.aidl
new file mode 100644
index 0000000..9e04e82
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioEncapsulationMode.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioEncapsulationMode {
+ NONE = 0,
+ ELEMENTARY_STREAM = 1,
+ HANDLE = 2,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioEncapsulationType.aidl b/media/libaudioclient/aidl/android/media/AudioEncapsulationType.aidl
new file mode 100644
index 0000000..b08a604
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioEncapsulationType.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * Audio encapsulation type is used to describe if the audio data should be sent with a particular
+ * encapsulation type or not.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioEncapsulationType {
+ NONE = 0,
+ IEC61937 = 1,
+}
\ No newline at end of file
diff --git a/media/libaudioclient/aidl/android/media/AudioFlag.aidl b/media/libaudioclient/aidl/android/media/AudioFlag.aidl
new file mode 100644
index 0000000..58b493b
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioFlag.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioFlag {
+ AUDIBILITY_ENFORCED = 0,
+ SECURE = 1,
+ SCO = 2,
+ BEACON = 3,
+ HW_AV_SYNC = 4,
+ HW_HOTWORD = 5,
+ BYPASS_INTERRUPTION_POLICY = 6,
+ BYPASS_MUTE = 7,
+ LOW_LATENCY = 8,
+ DEEP_BUFFER = 9,
+ NO_MEDIA_PROJECTION = 10,
+ MUTE_HAPTIC = 11,
+ NO_SYSTEM_CAPTURE = 12,
+ CAPTURE_PRIVATE = 13,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioGain.aidl b/media/libaudioclient/aidl/android/media/AudioGain.aidl
new file mode 100644
index 0000000..048b295
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioGain.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * {@hide}
+ */
+parcelable AudioGain {
+ int index;
+ boolean useInChannelMask;
+ boolean useForVolume;
+ /** Bitmask, indexed by AudioGainMode. */
+ int mode;
+ /** Interpreted as audio_channel_mask_t. */
+ int channelMask;
+ int minValue;
+ int maxValue;
+ int defaultValue;
+ int stepValue;
+ int minRampMs;
+ int maxRampMs;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioGainConfig.aidl b/media/libaudioclient/aidl/android/media/AudioGainConfig.aidl
new file mode 100644
index 0000000..b93c2dc
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioGainConfig.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * {@hide}
+ */
+parcelable AudioGainConfig {
+ /** Index of the corresponding audio_gain in the audio_port gains[] table. */
+ int index;
+
+ /** Mode requested for this command. Bitfield indexed by AudioGainMode. */
+ int mode;
+
+ /**
+ * Channels which gain value follows. N/A in joint mode.
+ * Interpreted as audio_channel_mask_t.
+ */
+ int channelMask;
+
+ /**
+ * Gain values in millibels.
+ * For each channel ordered from LSb to MSb in channel mask. The number of values is 1 in joint
+ * mode, otherwise equals the number of bits implied by channelMask.
+ */
+ int[] values;
+
+ /** Ramp duration in ms. */
+ int rampDurationMs;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioGainMode.aidl b/media/libaudioclient/aidl/android/media/AudioGainMode.aidl
new file mode 100644
index 0000000..e1b9f0b
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioGainMode.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioGainMode {
+ JOINT = 0,
+ CHANNELS = 1,
+ RAMP = 2,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioInputFlags.aidl b/media/libaudioclient/aidl/android/media/AudioInputFlags.aidl
new file mode 100644
index 0000000..bfc0eb0
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioInputFlags.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioInputFlags {
+ FAST = 0,
+ HW_HOTWORD = 1,
+ RAW = 2,
+ SYNC = 3,
+ MMAP_NOIRQ = 4,
+ VOIP_TX = 5,
+ HW_AV_SYNC = 6,
+ DIRECT = 7,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioIoConfigEvent.aidl b/media/libaudioclient/aidl/android/media/AudioIoConfigEvent.aidl
new file mode 100644
index 0000000..d5f23a1
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioIoConfigEvent.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioIoConfigEvent {
+ OUTPUT_REGISTERED = 0,
+ OUTPUT_OPENED = 1,
+ OUTPUT_CLOSED = 2,
+ OUTPUT_CONFIG_CHANGED = 3,
+ INPUT_REGISTERED = 4,
+ INPUT_OPENED = 5,
+ INPUT_CLOSED = 6,
+ INPUT_CONFIG_CHANGED = 7,
+ CLIENT_STARTED = 8,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioIoDescriptor.aidl b/media/libaudioclient/aidl/android/media/AudioIoDescriptor.aidl
new file mode 100644
index 0000000..876ef9b
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioIoDescriptor.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioPatch;
+import android.media.audio.common.AudioFormat;
+
+/**
+ * {@hide}
+ */
+parcelable AudioIoDescriptor {
+ /** Interpreted as audio_io_handle_t. */
+ int ioHandle;
+ AudioPatch patch;
+ int samplingRate;
+ AudioFormat format;
+ /** Interpreted as audio_channel_mask_t. */
+ int channelMask;
+ long frameCount;
+ long frameCountHAL;
+ /** Only valid for output. */
+ int latency;
+ /**
+ * Interpreted as audio_port_handle_t.
+ * valid for event AUDIO_CLIENT_STARTED.
+ */
+ int portId;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioIoFlags.aidl b/media/libaudioclient/aidl/android/media/AudioIoFlags.aidl
new file mode 100644
index 0000000..f9b25bf
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioIoFlags.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * {@hide}
+ */
+union AudioIoFlags {
+ /** Bitmask indexed by AudioInputFlags. */
+ int input;
+ /** Bitmask indexed by AudioOutputFlags. */
+ int output;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioMix.aidl b/media/libaudioclient/aidl/android/media/AudioMix.aidl
new file mode 100644
index 0000000..7473372
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioMix.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioConfig;
+import android.media.AudioDevice;
+import android.media.AudioMixCallbackFlag;
+import android.media.AudioMixMatchCriterion;
+import android.media.AudioMixRouteFlag;
+import android.media.AudioMixType;
+
+/**
+ * {@hide}
+ */
+parcelable AudioMix {
+ AudioMixMatchCriterion[] criteria;
+ AudioMixType mixType;
+ AudioConfig format;
+ /** Bitmask, indexed by AudioMixRouteFlag. */
+ int routeFlags;
+ AudioDevice device;
+ /** Flags indicating which callbacks to use. Bitmask, indexed by AudioMixCallbackFlag. */
+ int cbFlags;
+ /** Ignore the AUDIO_FLAG_NO_MEDIA_PROJECTION */
+ boolean allowPrivilegedMediaPlaybackCapture;
+ /** Indicates if the caller can capture voice communication output */
+ boolean voiceCommunicationCaptureAllowed;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioMixCallbackFlag.aidl b/media/libaudioclient/aidl/android/media/AudioMixCallbackFlag.aidl
new file mode 100644
index 0000000..6cb3d38
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioMixCallbackFlag.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioMixCallbackFlag {
+ NOTIFY_ACTIVITY = 0,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioMixLatencyClass.aidl b/media/libaudioclient/aidl/android/media/AudioMixLatencyClass.aidl
new file mode 100644
index 0000000..d70b364
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioMixLatencyClass.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioMixLatencyClass {
+ LOW = 0,
+ NORMAL = 1,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioMixMatchCriterion.aidl b/media/libaudioclient/aidl/android/media/AudioMixMatchCriterion.aidl
new file mode 100644
index 0000000..2c63dc5
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioMixMatchCriterion.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioMixMatchCriterionValue;
+
+/**
+ * {@hide}
+ */
+parcelable AudioMixMatchCriterion {
+ /** When true, rule becomes "exclude" instead of "include". */
+ boolean invert;
+ AudioMixMatchCriterionValue value;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioMixMatchCriterionValue.aidl b/media/libaudioclient/aidl/android/media/AudioMixMatchCriterionValue.aidl
new file mode 100644
index 0000000..e26a9e1
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioMixMatchCriterionValue.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioSourceType;
+import android.media.AudioUsage;
+
+/**
+ * {@hide}
+ */
+union AudioMixMatchCriterionValue {
+ AudioUsage usage = AudioUsage.UNKNOWN;
+ AudioSourceType source;
+ /** Interpreted as uid_t. */
+ int uid;
+ int userId;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioMixRouteFlag.aidl b/media/libaudioclient/aidl/android/media/AudioMixRouteFlag.aidl
new file mode 100644
index 0000000..f0cce2d
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioMixRouteFlag.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioMixRouteFlag {
+ RENDER = 0,
+ LOOP_BACK = 1,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioMixType.aidl b/media/libaudioclient/aidl/android/media/AudioMixType.aidl
new file mode 100644
index 0000000..b6806dc
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioMixType.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioMixType {
+ PLAYERS = 0,
+ RECORDERS = 1,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioMode.aidl b/media/libaudioclient/aidl/android/media/AudioMode.aidl
new file mode 100644
index 0000000..7067dd3
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioMode.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioMode {
+ INVALID = -2,
+ CURRENT = -1,
+ NORMAL = 0,
+ RINGTONE = 1,
+ IN_CALL = 2,
+ IN_COMMUNICATION = 3,
+ CALL_SCREEN = 4,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioOffloadInfo.aidl b/media/libaudioclient/aidl/android/media/AudioOffloadInfo.aidl
new file mode 100644
index 0000000..c86b3f0
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioOffloadInfo.aidl
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioConfigBase;
+import android.media.AudioEncapsulationMode;
+import android.media.AudioStreamType;
+import android.media.AudioUsage;
+import android.media.audio.common.AudioFormat;
+
+/**
+ * {@hide}
+ */
+parcelable AudioOffloadInfo {
+ /** Version of the info structure. Interpreted as a uint16_t version constant. */
+ int version;
+ /** Audio configuration. */
+ AudioConfigBase config;
+ /** Stream type. */
+ AudioStreamType streamType;
+ /** Bit rate in bits per second. */
+ int bitRate;
+ /** Duration in microseconds, -1 if unknown. */
+ long durationUs;
+ /** true if stream is tied to a video stream. */
+ boolean hasVideo;
+ /** true if streaming, false if local playback. */
+ boolean isStreaming;
+ int bitWidth;
+ /** Offload fragment size. */
+ int offloadBufferSize;
+ AudioUsage usage;
+ AudioEncapsulationMode encapsulationMode;
+ /** Content id from tuner HAL (0 if none). */
+ int contentId;
+ /** Sync id from tuner HAL (0 if none). */
+ int syncId;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioOffloadMode.aidl b/media/libaudioclient/aidl/android/media/AudioOffloadMode.aidl
new file mode 100644
index 0000000..45a44f2
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioOffloadMode.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioOffloadMode {
+ NOT_SUPPORTED = 0,
+ SUPPORTED = 1,
+ GAPLESS_SUPPORTED = 2
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioOutputFlags.aidl b/media/libaudioclient/aidl/android/media/AudioOutputFlags.aidl
new file mode 100644
index 0000000..cebd8f0
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioOutputFlags.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioOutputFlags {
+ DIRECT = 0,
+ PRIMARY = 1,
+ FAST = 2,
+ DEEP_BUFFER = 3,
+ COMPRESS_OFFLOAD = 4,
+ NON_BLOCKING = 5,
+ HW_AV_SYNC = 6,
+ TTS = 7,
+ RAW = 8,
+ SYNC = 9,
+ IEC958_NONAUDIO = 10,
+ DIRECT_PCM = 11,
+ MMAP_NOIRQ = 12,
+ VOIP_RX = 13,
+ INCALL_MUSIC = 14,
+ GAPLESS_OFFLOAD = 15,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPatch.aidl b/media/libaudioclient/aidl/android/media/AudioPatch.aidl
new file mode 100644
index 0000000..8519faf
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPatch.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioPortConfig;
+
+/**
+ * {@hide}
+ */
+parcelable AudioPatch {
+ /**
+ * Patch unique ID.
+ * Interpreted as audio_patch_handle_t.
+ */
+ int id;
+ AudioPortConfig[] sources;
+ AudioPortConfig[] sinks;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPlaybackRate.aidl b/media/libaudioclient/aidl/android/media/AudioPlaybackRate.aidl
new file mode 100644
index 0000000..e29d398
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPlaybackRate.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * The AudioPlaybackRate.
+ *
+ * See https://developer.android.com/reference/android/media/PlaybackParams.
+ * TODO(b/175166815): Reconcile with framework-media-sources PlaybackParams.aidl.
+ * As this is used for native wire serialization, no need to define
+ * audio_timestretch_stretch_mode_t and audio_timestretch_fallback_mode_t enums
+ * until we attempt to unify with PlaybackParams.
+ *
+ * {@hide}
+ */
+parcelable AudioPlaybackRate {
+ /** Speed of audio playback, >= 0.f, 1.f nominal (system limits are further restrictive) */
+ float speed;
+ /** Pitch of audio, >= 0.f, 1.f nominal (system limits are further restrictive) */
+ float pitch;
+ /** Interpreted as audio_timestretch_stretch_mode_t */
+ int stretchMode;
+ /** Interpreted as audio_timestretch_fallback_mode_t */
+ int fallbackMode;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPolicyDeviceState.aidl b/media/libaudioclient/aidl/android/media/AudioPolicyDeviceState.aidl
new file mode 100644
index 0000000..a1072d2
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPolicyDeviceState.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioPolicyDeviceState {
+ UNAVAILABLE = 0,
+ AVAILABLE = 1,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPolicyForceUse.aidl b/media/libaudioclient/aidl/android/media/AudioPolicyForceUse.aidl
new file mode 100644
index 0000000..9bb0605
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPolicyForceUse.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioPolicyForceUse {
+ COMMUNICATION = 0,
+ MEDIA = 1,
+ RECORD = 2,
+ DOCK = 3,
+ SYSTEM = 4,
+ HDMI_SYSTEM_AUDIO = 5,
+ ENCODED_SURROUND = 6,
+ VIBRATE_RINGING = 7,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPolicyForcedConfig.aidl b/media/libaudioclient/aidl/android/media/AudioPolicyForcedConfig.aidl
new file mode 100644
index 0000000..2255d4c
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPolicyForcedConfig.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioPolicyForcedConfig {
+ NONE = 0,
+ SPEAKER = 1,
+ HEADPHONES = 2,
+ BT_SCO = 3,
+ BT_A2DP = 4,
+ WIRED_ACCESSORY = 5,
+ BT_CAR_DOCK = 6,
+ BT_DESK_DOCK = 7,
+ ANALOG_DOCK = 8,
+ DIGITAL_DOCK = 9,
+ NO_BT_A2DP = 10, /* A2DP sink is not preferred to speaker or wired HS */
+ SYSTEM_ENFORCED = 11,
+ HDMI_SYSTEM_AUDIO_ENFORCED = 12,
+ ENCODED_SURROUND_NEVER = 13,
+ ENCODED_SURROUND_ALWAYS = 14,
+ ENCODED_SURROUND_MANUAL = 15,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPort.aidl b/media/libaudioclient/aidl/android/media/AudioPort.aidl
new file mode 100644
index 0000000..bf0e5b7
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPort.aidl
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioGain;
+import android.media.AudioPortConfig;
+import android.media.AudioPortExt;
+import android.media.AudioPortRole;
+import android.media.AudioPortType;
+import android.media.AudioProfile;
+import android.media.ExtraAudioDescriptor;
+
+/**
+ * {@hide}
+ */
+parcelable AudioPort {
+ /** Port unique ID. Interpreted as audio_port_handle_t. */
+ int id;
+ /** Sink or source. */
+ AudioPortRole role;
+ /** Device, mix ... */
+ AudioPortType type;
+ @utf8InCpp String name;
+ /** AudioProfiles supported by this port (format, Rates, Channels). */
+ AudioProfile[] profiles;
+ /**
+ * ExtraAudioDescriptors supported by this port. The format is not unrecognized to the
+ * platform. The audio capability is described by a hardware descriptor.
+ */
+ ExtraAudioDescriptor[] extraAudioDescriptors;
+ /** Gain controllers. */
+ AudioGain[] gains;
+ /** Current audio port configuration. */
+ AudioPortConfig activeConfig;
+ AudioPortExt ext;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPortConfig.aidl b/media/libaudioclient/aidl/android/media/AudioPortConfig.aidl
new file mode 100644
index 0000000..2dd30a4
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPortConfig.aidl
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioGainConfig;
+import android.media.AudioIoFlags;
+import android.media.AudioPortConfigExt;
+import android.media.AudioPortConfigType;
+import android.media.AudioPortRole;
+import android.media.AudioPortType;
+import android.media.audio.common.AudioFormat;
+
+/**
+ * {@hide}
+ */
+parcelable AudioPortConfig {
+ /**
+ * Port unique ID.
+ * Interpreted as audio_port_handle_t.
+ */
+ int id;
+ /** Sink or source. */
+ AudioPortRole role;
+ /** Device, mix ... */
+ AudioPortType type;
+ /** Bitmask, indexed by AudioPortConfigType. */
+ int configMask;
+ /** Sampling rate in Hz. */
+ int sampleRate;
+ /**
+ * Channel mask, if applicable.
+ * Interpreted as audio_channel_mask_t.
+ * TODO: bitmask?
+ */
+ int channelMask;
+ /**
+ * Format, if applicable.
+ */
+ AudioFormat format;
+ /** Gain to apply, if applicable. */
+ AudioGainConfig gain;
+ /** Framework only: HW_AV_SYNC, DIRECT, ... */
+ AudioIoFlags flags;
+ AudioPortConfigExt ext;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPortConfigDeviceExt.aidl b/media/libaudioclient/aidl/android/media/AudioPortConfigDeviceExt.aidl
new file mode 100644
index 0000000..a99aa9b
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPortConfigDeviceExt.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * {@hide}
+ */
+parcelable AudioPortConfigDeviceExt {
+ /**
+ * Module the device is attached to.
+ * Interpreted as audio_module_handle_t.
+ */
+ int hwModule;
+ /**
+ * Device type (e.g AUDIO_DEVICE_OUT_SPEAKER).
+ * Interpreted as audio_devices_t.
+ * TODO: Convert to a standalone AIDL representation.
+ */
+ int type;
+ /** Device address. "" if N/A. */
+ @utf8InCpp String address;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPortConfigExt.aidl b/media/libaudioclient/aidl/android/media/AudioPortConfigExt.aidl
new file mode 100644
index 0000000..5d635b6
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPortConfigExt.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioPortConfigDeviceExt;
+import android.media.AudioPortConfigMixExt;
+import android.media.AudioPortConfigSessionExt;
+
+/**
+ * {@hide}
+ */
+union AudioPortConfigExt {
+ /**
+ * This represents an empty union. Value is ignored.
+ * TODO(ytai): replace with the canonical representation for an empty union, as soon as it is
+ * established.
+ */
+ boolean unspecified;
+ /** Device specific info. */
+ AudioPortConfigDeviceExt device;
+ /** Mix specific info. */
+ AudioPortConfigMixExt mix;
+ /** Session specific info. */
+ AudioPortConfigSessionExt session;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPortConfigMixExt.aidl b/media/libaudioclient/aidl/android/media/AudioPortConfigMixExt.aidl
new file mode 100644
index 0000000..d3226f2
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPortConfigMixExt.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioPortConfigMixExtUseCase;
+
+/**
+ * {@hide}
+ */
+parcelable AudioPortConfigMixExt {
+ /**
+ * Module the stream is attached to.
+ * Interpreted as audio_module_handle_t.
+ */
+ int hwModule;
+ /**
+ * I/O handle of the input/output stream.
+ * Interpreted as audio_io_handle_t.
+ */
+ int handle;
+ AudioPortConfigMixExtUseCase usecase;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPortConfigMixExtUseCase.aidl b/media/libaudioclient/aidl/android/media/AudioPortConfigMixExtUseCase.aidl
new file mode 100644
index 0000000..c61f044
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPortConfigMixExtUseCase.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioSourceType;
+import android.media.AudioStreamType;
+
+/**
+ * {@hide}
+ */
+union AudioPortConfigMixExtUseCase {
+ /**
+ * This to be set if the containing config has the AudioPortRole::NONE role.
+ * This represents an empty value (value is ignored).
+ * TODO(ytai): replace with the canonical representation for an empty union, as soon as it is
+ * established.
+ */
+ boolean unspecified;
+ /** This to be set if the containing config has the AudioPortRole::SOURCE role. */
+ AudioStreamType stream;
+ /** This to be set if the containing config has the AudioPortRole::SINK role. */
+ AudioSourceType source;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPortConfigSessionExt.aidl b/media/libaudioclient/aidl/android/media/AudioPortConfigSessionExt.aidl
new file mode 100644
index 0000000..a2cbf62
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPortConfigSessionExt.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * {@hide}
+ */
+parcelable AudioPortConfigSessionExt {
+ int session;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPortConfigType.aidl b/media/libaudioclient/aidl/android/media/AudioPortConfigType.aidl
new file mode 100644
index 0000000..6e22b8d
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPortConfigType.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioPortConfigType {
+ SAMPLE_RATE = 0,
+ CHANNEL_MASK = 1,
+ FORMAT = 2,
+ GAIN = 3,
+ FLAGS = 4,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPortDeviceExt.aidl b/media/libaudioclient/aidl/android/media/AudioPortDeviceExt.aidl
new file mode 100644
index 0000000..b758f23
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPortDeviceExt.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioDevice;
+
+/**
+ * {@hide}
+ */
+parcelable AudioPortDeviceExt {
+ /** Module the device is attached to. Interpreted as audio_module_handle_t. */
+ int hwModule;
+ AudioDevice device;
+ /** Bitmask, indexed by AudioEncapsulationMode. */
+ int encapsulationModes;
+ /** Bitmask, indexed by AudioEncapsulationMetadataType. */
+ int encapsulationMetadataTypes;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPortExt.aidl b/media/libaudioclient/aidl/android/media/AudioPortExt.aidl
new file mode 100644
index 0000000..453784b
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPortExt.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioPortDeviceExt;
+import android.media.AudioPortMixExt;
+import android.media.AudioPortSessionExt;
+
+/**
+ * {@hide}
+ */
+union AudioPortExt {
+ /**
+ * This represents an empty union. Value is ignored.
+ * TODO(ytai): replace with the canonical representation for an empty union, as soon as it is
+ * established.
+ */
+ boolean unspecified;
+ /** Device specific info. */
+ AudioPortDeviceExt device;
+ /** Mix specific info. */
+ AudioPortMixExt mix;
+ /** Session specific info. */
+ AudioPortSessionExt session;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPortMixExt.aidl b/media/libaudioclient/aidl/android/media/AudioPortMixExt.aidl
new file mode 100644
index 0000000..62cdb8e
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPortMixExt.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioMixLatencyClass;
+
+/**
+ * {@hide}
+ */
+parcelable AudioPortMixExt {
+ /** Module the stream is attached to. Interpreted as audio_module_handle_t. */
+ int hwModule;
+ /** I/O handle of the input/output stream. Interpreted as audio_io_handle_t. */
+ int handle;
+ /** Latency class */
+ AudioMixLatencyClass latencyClass;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPortRole.aidl b/media/libaudioclient/aidl/android/media/AudioPortRole.aidl
new file mode 100644
index 0000000..ea2ef3a
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPortRole.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioPortRole {
+ NONE = 0,
+ SOURCE = 1,
+ SINK = 2,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPortSessionExt.aidl b/media/libaudioclient/aidl/android/media/AudioPortSessionExt.aidl
new file mode 100644
index 0000000..dbca168
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPortSessionExt.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * {@hide}
+ */
+parcelable AudioPortSessionExt {
+ /** Audio session. Interpreted as audio_session_t. */
+ int session;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPortType.aidl b/media/libaudioclient/aidl/android/media/AudioPortType.aidl
new file mode 100644
index 0000000..9e6af49
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPortType.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioPortType {
+ NONE = 0,
+ DEVICE = 1,
+ MIX = 2,
+ SESSION = 3,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioProductStrategy.aidl b/media/libaudioclient/aidl/android/media/AudioProductStrategy.aidl
new file mode 100644
index 0000000..56c4a44
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioProductStrategy.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioAttributesEx;
+
+/**
+ * {@hide}
+ */
+parcelable AudioProductStrategy {
+ /** Interpreted as product_strategy_t. */
+ int id;
+ @utf8InCpp String name;
+ AudioAttributesEx[] audioAttributes;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioProfile.aidl b/media/libaudioclient/aidl/android/media/AudioProfile.aidl
new file mode 100644
index 0000000..afb288f
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioProfile.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioEncapsulationType;
+import android.media.audio.common.AudioFormat;
+
+/**
+ * {@hide}
+ */
+parcelable AudioProfile {
+ @utf8InCpp String name;
+ /** The format for an audio profile should only be set when initialized. */
+ AudioFormat format;
+ /** Interpreted as audio_channel_mask_t. */
+ int[] channelMasks;
+ int[] samplingRates;
+ boolean isDynamicFormat;
+ boolean isDynamicChannels;
+ boolean isDynamicRate;
+ AudioEncapsulationType encapsulationType;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioSourceType.aidl b/media/libaudioclient/aidl/android/media/AudioSourceType.aidl
new file mode 100644
index 0000000..8673b92
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioSourceType.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioSourceType {
+ INVALID = -1,
+ DEFAULT = 0,
+ MIC = 1,
+ VOICE_UPLINK = 2,
+ VOICE_DOWNLINK = 3,
+ VOICE_CALL = 4,
+ CAMCORDER = 5,
+ VOICE_RECOGNITION = 6,
+ VOICE_COMMUNICATION = 7,
+ REMOTE_SUBMIX = 8,
+ UNPROCESSED = 9,
+ VOICE_PERFORMANCE = 10,
+ ECHO_REFERENCE = 1997,
+ FM_TUNER = 1998,
+ /**
+ * A low-priority, preemptible audio source for for background software
+ * hotword detection. Same tuning as VOICE_RECOGNITION.
+ * Used only internally by the framework.
+ */
+ HOTWORD = 1999,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioStandard.aidl b/media/libaudioclient/aidl/android/media/AudioStandard.aidl
new file mode 100644
index 0000000..e131d0d
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioStandard.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * The audio standard that describe audio playback/capture capabilites.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioStandard {
+ NONE = 0,
+ EDID = 1,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioStreamType.aidl b/media/libaudioclient/aidl/android/media/AudioStreamType.aidl
new file mode 100644
index 0000000..d777882
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioStreamType.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioStreamType {
+ DEFAULT = -1,
+ VOICE_CALL = 0,
+ SYSTEM = 1,
+ RING = 2,
+ MUSIC = 3,
+ ALARM = 4,
+ NOTIFICATION = 5,
+ BLUETOOTH_SCO = 6,
+ ENFORCED_AUDIBLE = 7,
+ DTMF = 8,
+ TTS = 9,
+ ACCESSIBILITY = 10,
+ ASSISTANT = 11,
+ /** For dynamic policy output mixes. Only used by the audio policy */
+ REROUTING = 12,
+ /** For audio flinger tracks volume. Only used by the audioflinger */
+ PATCH = 13,
+ /** stream for corresponding to AUDIO_USAGE_CALL_ASSISTANT */
+ CALL_ASSISTANT = 14,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioTimestampInternal.aidl b/media/libaudioclient/aidl/android/media/AudioTimestampInternal.aidl
new file mode 100644
index 0000000..8bbfb57
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioTimestampInternal.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * The "Internal" timestamp is intended to disambiguate from the android.media.AudioTimestamp type.
+ *
+ * {@hide}
+ */
+parcelable AudioTimestampInternal {
+ /** A frame position in AudioTrack::getPosition() units. */
+ int position;
+ /** corresponding CLOCK_MONOTONIC when frame is expected to present. */
+ long sec;
+ int nsec;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioUniqueIdUse.aidl b/media/libaudioclient/aidl/android/media/AudioUniqueIdUse.aidl
new file mode 100644
index 0000000..fdb6d2d
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioUniqueIdUse.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioUniqueIdUse {
+ UNSPECIFIED = 0,
+ SESSION = 1, // audio_session_t
+ // for allocated sessions, not special AUDIO_SESSION_*
+ MODULE = 2, // audio_module_handle_t
+ EFFECT = 3, // audio_effect_handle_t
+ PATCH = 4, // audio_patch_handle_t
+ OUTPUT = 5, // audio_io_handle_t
+ INPUT = 6, // audio_io_handle_t
+ CLIENT = 7, // client-side players and recorders
+ // FIXME should move to a separate namespace;
+ // these IDs are allocated by AudioFlinger on client request,
+ // but are never used by AudioFlinger
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioUsage.aidl b/media/libaudioclient/aidl/android/media/AudioUsage.aidl
new file mode 100644
index 0000000..66c5c30
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioUsage.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioUsage {
+ UNKNOWN = 0,
+ MEDIA = 1,
+ VOICE_COMMUNICATION = 2,
+ VOICE_COMMUNICATION_SIGNALLING = 3,
+ ALARM = 4,
+ NOTIFICATION = 5,
+ NOTIFICATION_TELEPHONY_RINGTONE = 6,
+ NOTIFICATION_COMMUNICATION_REQUEST = 7,
+ NOTIFICATION_COMMUNICATION_INSTANT = 8,
+ NOTIFICATION_COMMUNICATION_DELAYED = 9,
+ NOTIFICATION_EVENT = 10,
+ ASSISTANCE_ACCESSIBILITY = 11,
+ ASSISTANCE_NAVIGATION_GUIDANCE = 12,
+ ASSISTANCE_SONIFICATION = 13,
+ GAME = 14,
+ VIRTUAL_SOURCE = 15,
+ ASSISTANT = 16,
+ CALL_ASSISTANT = 17,
+ EMERGENCY = 1000,
+ SAFETY = 1001,
+ VEHICLE_STATUS = 1002,
+ ANNOUNCEMENT = 1003,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioUuid.aidl b/media/libaudioclient/aidl/android/media/AudioUuid.aidl
new file mode 100644
index 0000000..bba9039
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioUuid.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/**
+ * {@hide}
+ */
+parcelable AudioUuid {
+ int timeLow;
+ int timeMid;
+ int timeHiAndVersion;
+ int clockSeq;
+ byte[] node; // Length = 6
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioVibratorInfo.aidl b/media/libaudioclient/aidl/android/media/AudioVibratorInfo.aidl
new file mode 100644
index 0000000..f88fc3c
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioVibratorInfo.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * {@hide}
+ * A class for vibrator information. The information will be used in HapticGenerator effect.
+ */
+parcelable AudioVibratorInfo {
+ int id;
+ float resonantFrequency;
+ float qFactor;
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioVolumeGroup.aidl b/media/libaudioclient/aidl/android/media/AudioVolumeGroup.aidl
new file mode 100644
index 0000000..3a29a08
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioVolumeGroup.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioAttributesInternal;
+import android.media.AudioStreamType;
+
+/**
+ * {@hide}
+ */
+parcelable AudioVolumeGroup {
+ /** Interpreted as volume_group_t. */
+ int groupId;
+ @utf8InCpp String name;
+ AudioAttributesInternal[] audioAttributes;
+ AudioStreamType[] streams;
+}
diff --git a/media/libaudioclient/aidl/android/media/CreateEffectRequest.aidl b/media/libaudioclient/aidl/android/media/CreateEffectRequest.aidl
new file mode 100644
index 0000000..5737fcd
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/CreateEffectRequest.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioDevice;
+import android.media.EffectDescriptor;
+import android.media.IEffectClient;
+import android.media.permission.Identity;
+
+/**
+ * Input arguments of the createEffect() method.
+ *
+ * {@hide}
+ */
+parcelable CreateEffectRequest {
+ EffectDescriptor desc;
+ @nullable IEffectClient client;
+ int priority;
+ /** Interpreted as audio_io_handle_t. */
+ int output;
+ /** Interpreted as audio_session_t. */
+ int sessionId;
+ AudioDevice device;
+ Identity identity;
+ boolean probe;
+}
diff --git a/media/libaudioclient/aidl/android/media/CreateEffectResponse.aidl b/media/libaudioclient/aidl/android/media/CreateEffectResponse.aidl
new file mode 100644
index 0000000..0aa640a
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/CreateEffectResponse.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.EffectDescriptor;
+import android.media.IEffect;
+
+/**
+ * Output arguments of the createEffect() method.
+ *
+ * {@hide}
+ */
+parcelable CreateEffectResponse {
+ int id;
+ boolean enabled;
+ @nullable IEffect effect;
+ EffectDescriptor desc;
+}
diff --git a/media/libaudioclient/aidl/android/media/CreateRecordRequest.aidl b/media/libaudioclient/aidl/android/media/CreateRecordRequest.aidl
new file mode 100644
index 0000000..5b26d22
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/CreateRecordRequest.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioAttributesInternal;
+import android.media.AudioClient;
+import android.media.AudioConfigBase;
+import android.media.permission.Identity;
+
+/**
+ * CreateRecordRequest contains all input arguments sent by AudioRecord to AudioFlinger
+ * when calling createRecord() including arguments that will be updated by AudioFlinger
+ * and returned in CreateRecordResponse object.
+ *
+ * {@hide}
+ */
+parcelable CreateRecordRequest {
+ AudioAttributesInternal attr;
+ AudioConfigBase config;
+ AudioClient clientInfo;
+ /** Interpreted as audio_unique_id_t. */
+ int riid;
+ int maxSharedAudioHistoryMs;
+ /** Bitmask, indexed by AudioInputFlags. */
+ int flags;
+ long frameCount;
+ long notificationFrameCount;
+ /** Interpreted as audio_port_handle_t. */
+ int selectedDeviceId;
+ int sessionId;
+}
diff --git a/media/libaudioclient/aidl/android/media/CreateRecordResponse.aidl b/media/libaudioclient/aidl/android/media/CreateRecordResponse.aidl
new file mode 100644
index 0000000..d78b3fc
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/CreateRecordResponse.aidl
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.IAudioRecord;
+import android.media.SharedFileRegion;
+
+/**
+ * CreateRecordResponse contains all output arguments returned by AudioFlinger to AudioRecord
+ * when calling createRecord() including arguments that were passed as I/O for update by
+ * CreateRecordRequest.
+ *
+ * {@hide}
+ */
+parcelable CreateRecordResponse {
+ /** Bitmask, indexed by AudioInputFlags. */
+ int flags;
+ long frameCount;
+ long notificationFrameCount;
+ /** Interpreted as audio_port_handle_t. */
+ int selectedDeviceId;
+ int sessionId;
+ int sampleRate;
+ /** Interpreted as audio_io_handle_t. */
+ int inputId;
+ @nullable SharedFileRegion cblk;
+ @nullable SharedFileRegion buffers;
+ /** Interpreted as audio_port_handle_t. */
+ int portId;
+ /** The newly created record. */
+ @nullable IAudioRecord audioRecord;
+}
diff --git a/media/libaudioclient/aidl/android/media/CreateTrackRequest.aidl b/media/libaudioclient/aidl/android/media/CreateTrackRequest.aidl
new file mode 100644
index 0000000..014b3ca
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/CreateTrackRequest.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioAttributesInternal;
+import android.media.AudioClient;
+import android.media.AudioConfig;
+import android.media.IAudioTrackCallback;
+import android.media.SharedFileRegion;
+
+/**
+ * CreateTrackInput contains all input arguments sent by AudioTrack to AudioFlinger
+ * when calling createTrack() including arguments that will be updated by AudioFlinger
+ * and returned in CreateTrackResponse object.
+ *
+ * {@hide}
+ */
+parcelable CreateTrackRequest {
+ AudioAttributesInternal attr;
+ AudioConfig config;
+ AudioClient clientInfo;
+ @nullable SharedFileRegion sharedBuffer;
+ int notificationsPerBuffer;
+ float speed;
+ IAudioTrackCallback audioTrackCallback;
+ @utf8InCpp String opPackageName;
+ /** Bitmask, indexed by AudioOutputFlags. */
+ int flags;
+ long frameCount;
+ long notificationFrameCount;
+ /** Interpreted as audio_port_handle_t. */
+ int selectedDeviceId;
+ int sessionId;
+}
diff --git a/media/libaudioclient/aidl/android/media/CreateTrackResponse.aidl b/media/libaudioclient/aidl/android/media/CreateTrackResponse.aidl
new file mode 100644
index 0000000..6bdd8e4
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/CreateTrackResponse.aidl
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.IAudioTrack;
+
+/**
+ * CreateTrackOutput contains all output arguments returned by AudioFlinger to AudioTrack
+ * when calling createTrack() including arguments that were passed as I/O for update by
+ * CreateTrackRequest.
+ *
+ * {@hide}
+ */
+parcelable CreateTrackResponse {
+ /** Bitmask, indexed by AudioOutputFlags. */
+ int flags;
+ long frameCount;
+ long notificationFrameCount;
+ /** Interpreted as audio_port_handle_t. */
+ int selectedDeviceId;
+ int sessionId;
+ int sampleRate;
+ long afFrameCount;
+ int afSampleRate;
+ int afLatencyMs;
+ /** Interpreted as audio_io_handle_t. */
+ int outputId;
+ /** Interpreted as audio_port_handle_t. */
+ int portId;
+ /** The newly created track. */
+ @nullable IAudioTrack audioTrack;
+}
diff --git a/media/libaudioclient/aidl/android/media/DeviceRole.aidl b/media/libaudioclient/aidl/android/media/DeviceRole.aidl
new file mode 100644
index 0000000..b741843
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/DeviceRole.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum DeviceRole {
+ /** Default routing rules and priority apply. */
+ NONE = 0,
+ /* Devices are specified as preferred devices. */
+ PREFERRED = 1,
+ /* Devices cannot be used. */
+ DISABLED = 2,
+}
diff --git a/media/libaudioclient/aidl/android/media/EffectDescriptor.aidl b/media/libaudioclient/aidl/android/media/EffectDescriptor.aidl
new file mode 100644
index 0000000..35a3d74
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/EffectDescriptor.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioUuid;
+
+/**
+ * {@hide}
+ */
+parcelable EffectDescriptor {
+ /** UUID of to the OpenSL ES interface implemented by this effect. */
+ AudioUuid type;
+ /** UUID for this particular implementation. */
+ AudioUuid uuid;
+ /** Version of the effect control API implemented. */
+ int apiVersion;
+ /** Effect engine capabilities/requirements flags. */
+ int flags;
+ /** CPU load indication.. */
+ int cpuLoad;
+ /** Data Memory usage.. */
+ int memoryUsage;
+ /** Human readable effect name. */
+ @utf8InCpp String name;
+ /** Human readable effect implementor name. */
+ @utf8InCpp String implementor;
+}
diff --git a/media/libaudioclient/aidl/android/media/ExtraAudioDescriptor.aidl b/media/libaudioclient/aidl/android/media/ExtraAudioDescriptor.aidl
new file mode 100644
index 0000000..ec5b67a
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/ExtraAudioDescriptor.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioEncapsulationType;
+import android.media.AudioStandard;
+
+/**
+ * The audio descriptor that descibes playback/capture capabilities according to
+ * a particular standard.
+ *
+ * {@hide}
+ */
+parcelable ExtraAudioDescriptor {
+ AudioStandard standard;
+ byte[] audioDescriptor;
+ AudioEncapsulationType encapsulationType;
+}
diff --git a/media/libaudioclient/aidl/android/media/GetInputForAttrResponse.aidl b/media/libaudioclient/aidl/android/media/GetInputForAttrResponse.aidl
new file mode 100644
index 0000000..9696124
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/GetInputForAttrResponse.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * {@hide}
+ */
+parcelable GetInputForAttrResponse {
+ /** Interpreted as audio_io_handle_t. */
+ int input;
+ /** Interpreted as audio_port_handle_t. */
+ int selectedDeviceId;
+ /** Interpreted as audio_port_handle_t. */
+ int portId;
+}
diff --git a/media/libaudioclient/aidl/android/media/GetOutputForAttrResponse.aidl b/media/libaudioclient/aidl/android/media/GetOutputForAttrResponse.aidl
new file mode 100644
index 0000000..164fb9d
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/GetOutputForAttrResponse.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioStreamType;
+
+/**
+ * {@hide}
+ */
+parcelable GetOutputForAttrResponse {
+ /** Interpreted as audio_io_handle_t. */
+ int output;
+ AudioStreamType stream;
+ /** Interpreted as audio_port_handle_t. */
+ int selectedDeviceId;
+ /** Interpreted as audio_port_handle_t. */
+ int portId;
+ /** Interpreted as audio_io_handle_t[]. */
+ int[] secondaryOutputs;
+}
diff --git a/media/libaudioclient/aidl/android/media/IAudioFlingerClient.aidl b/media/libaudioclient/aidl/android/media/IAudioFlingerClient.aidl
new file mode 100644
index 0000000..421c31c
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/IAudioFlingerClient.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioIoConfigEvent;
+import android.media.AudioIoDescriptor;
+
+/**
+ * A callback interface for AudioFlinger.
+ *
+ * {@hide}
+ */
+interface IAudioFlingerClient {
+ oneway void ioConfigChanged(AudioIoConfigEvent event,
+ in AudioIoDescriptor ioDesc);
+}
diff --git a/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl b/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
new file mode 100644
index 0000000..abbced5
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioMode;
+import android.media.AudioPatch;
+import android.media.AudioPort;
+import android.media.AudioPortConfig;
+import android.media.AudioStreamType;
+import android.media.AudioUniqueIdUse;
+import android.media.AudioUuid;
+import android.media.AudioVibratorInfo;
+import android.media.CreateEffectRequest;
+import android.media.CreateEffectResponse;
+import android.media.CreateRecordRequest;
+import android.media.CreateRecordResponse;
+import android.media.CreateTrackRequest;
+import android.media.CreateTrackResponse;
+import android.media.OpenInputRequest;
+import android.media.OpenInputResponse;
+import android.media.OpenOutputRequest;
+import android.media.OpenOutputResponse;
+import android.media.EffectDescriptor;
+import android.media.IAudioFlingerClient;
+import android.media.IAudioRecord;
+import android.media.IAudioTrack;
+import android.media.MicrophoneInfoData;
+import android.media.RenderPosition;
+import android.media.audio.common.AudioFormat;
+
+/**
+ * {@hide}
+ */
+interface IAudioFlingerService {
+ /**
+ * Creates an audio track and registers it with AudioFlinger, or null if the track cannot be
+ * created.
+ */
+ CreateTrackResponse createTrack(in CreateTrackRequest request);
+
+ CreateRecordResponse createRecord(in CreateRecordRequest request);
+
+ // FIXME Surprisingly, format/latency don't work for input handles
+
+ /**
+ * Queries the audio hardware state. This state never changes, and therefore can be cached.
+ */
+ int sampleRate(int /* audio_io_handle_t */ ioHandle);
+
+ AudioFormat format(int /* audio_io_handle_t */ output);
+
+ long frameCount(int /* audio_io_handle_t */ ioHandle);
+
+ /**
+ * Return the estimated latency in milliseconds.
+ */
+ int latency(int /* audio_io_handle_t */ output);
+
+ /*
+ * Sets/gets the audio hardware state. This will probably be used by
+ * the preference panel, mostly.
+ */
+ void setMasterVolume(float value);
+ void setMasterMute(boolean muted);
+
+ float masterVolume();
+ boolean masterMute();
+
+ void setMasterBalance(float balance);
+ float getMasterBalance();
+
+ /*
+ * Set/gets stream type state. This will probably be used by
+ * the preference panel, mostly.
+ */
+ void setStreamVolume(AudioStreamType stream, float value, int /* audio_io_handle_t */ output);
+ void setStreamMute(AudioStreamType stream, boolean muted);
+ float streamVolume(AudioStreamType stream, int /* audio_io_handle_t */ output);
+ boolean streamMute(AudioStreamType stream);
+
+ // set audio mode.
+ void setMode(AudioMode mode);
+
+ // mic mute/state
+ void setMicMute(boolean state);
+ boolean getMicMute();
+ void setRecordSilenced(int /* audio_port_handle_t */ portId,
+ boolean silenced);
+
+ void setParameters(int /* audio_io_handle_t */ ioHandle,
+ @utf8InCpp String keyValuePairs);
+ @utf8InCpp String getParameters(int /* audio_io_handle_t */ ioHandle,
+ @utf8InCpp String keys);
+
+ // Register an object to receive audio input/output change and track notifications.
+ // For a given calling pid, AudioFlinger disregards any registrations after the first.
+ // Thus the IAudioFlingerClient must be a singleton per process.
+ void registerClient(IAudioFlingerClient client);
+
+ // Retrieve the audio recording buffer size in bytes.
+ // FIXME This API assumes a route, and so should be deprecated.
+ long getInputBufferSize(int sampleRate,
+ AudioFormat format,
+ int /* audio_channel_mask_t */ channelMask);
+
+ OpenOutputResponse openOutput(in OpenOutputRequest request);
+ int /* audio_io_handle_t */ openDuplicateOutput(int /* audio_io_handle_t */ output1,
+ int /* audio_io_handle_t */ output2);
+ void closeOutput(int /* audio_io_handle_t */ output);
+ void suspendOutput(int /* audio_io_handle_t */ output);
+ void restoreOutput(int /* audio_io_handle_t */ output);
+
+ OpenInputResponse openInput(in OpenInputRequest request);
+ void closeInput(int /* audio_io_handle_t */ input);
+
+ void invalidateStream(AudioStreamType stream);
+
+ void setVoiceVolume(float volume);
+
+ RenderPosition getRenderPosition(int /* audio_io_handle_t */ output);
+
+ int getInputFramesLost(int /* audio_io_handle_t */ ioHandle);
+
+ int /* audio_unique_id_t */ newAudioUniqueId(AudioUniqueIdUse use);
+
+ void acquireAudioSessionId(int /* audio_session_t */ audioSession,
+ int /* pid_t */ pid,
+ int /* uid_t */ uid);
+ void releaseAudioSessionId(int /* audio_session_t */ audioSession,
+ int /* pid_t */ pid);
+
+ int queryNumberEffects();
+
+ EffectDescriptor queryEffect(int index);
+
+ /** preferredTypeFlag is interpreted as a uint32_t with the "effect flag" format. */
+ EffectDescriptor getEffectDescriptor(in AudioUuid effectUUID,
+ in AudioUuid typeUUID,
+ int preferredTypeFlag);
+
+ CreateEffectResponse createEffect(in CreateEffectRequest request);
+
+ void moveEffects(int /* audio_session_t */ session,
+ int /* audio_io_handle_t */ srcOutput,
+ int /* audio_io_handle_t */ dstOutput);
+
+ void setEffectSuspended(int effectId,
+ int /* audio_session_t */ sessionId,
+ boolean suspended);
+
+ int /* audio_module_handle_t */ loadHwModule(@utf8InCpp String name);
+
+ // helpers for android.media.AudioManager.getProperty(), see description there for meaning
+ // FIXME move these APIs to AudioPolicy to permit a more accurate implementation
+ // that looks on primary device for a stream with fast flag, primary flag, or first one.
+ int getPrimaryOutputSamplingRate();
+ long getPrimaryOutputFrameCount();
+
+ // Intended for AudioService to inform AudioFlinger of device's low RAM attribute,
+ // and should be called at most once. For a definition of what "low RAM" means, see
+ // android.app.ActivityManager.isLowRamDevice(). The totalMemory parameter
+ // is obtained from android.app.ActivityManager.MemoryInfo.totalMem.
+ void setLowRamDevice(boolean isLowRamDevice, long totalMemory);
+
+ /* Get attributes for a given audio port */
+ AudioPort getAudioPort(in AudioPort port);
+
+ /* Create an audio patch between several source and sink ports */
+ int /* audio_patch_handle_t */ createAudioPatch(in AudioPatch patch);
+
+ /* Release an audio patch */
+ void releaseAudioPatch(int /* audio_patch_handle_t */ handle);
+
+ /* List existing audio patches */
+ AudioPatch[] listAudioPatches(int maxCount);
+ /* Set audio port configuration */
+ void setAudioPortConfig(in AudioPortConfig config);
+
+ /* Get the HW synchronization source used for an audio session */
+ int /* audio_hw_sync_t */ getAudioHwSyncForSession(int /* audio_session_t */ sessionId);
+
+ /* Indicate JAVA services are ready (scheduling, power management ...) */
+ oneway void systemReady();
+
+ // Returns the number of frames per audio HAL buffer.
+ long frameCountHAL(int /* audio_io_handle_t */ ioHandle);
+
+ /* List available microphones and their characteristics */
+ MicrophoneInfoData[] getMicrophones();
+
+ void setAudioHalPids(in int[] /* pid_t[] */ pids);
+
+ // Set vibrators' information.
+ // The value will be used to initialize HapticGenerator.
+ void setVibratorInfos(in AudioVibratorInfo[] vibratorInfos);
+}
diff --git a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
new file mode 100644
index 0000000..f8924f3
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.audio.common.AudioFormat;
+
+import android.media.AudioAttributesEx;
+import android.media.AudioAttributesInternal;
+import android.media.AudioConfig;
+import android.media.AudioConfigBase;
+import android.media.AudioDevice;
+import android.media.AudioMix;
+import android.media.AudioMode;
+import android.media.AudioOffloadInfo;
+import android.media.AudioOffloadMode;
+import android.media.AudioPatch;
+import android.media.AudioPolicyDeviceState;
+import android.media.AudioPolicyForcedConfig;
+import android.media.AudioPolicyForceUse;
+import android.media.AudioPort;
+import android.media.AudioPortConfig;
+import android.media.AudioPortRole;
+import android.media.AudioPortType;
+import android.media.AudioProductStrategy;
+import android.media.AudioSourceType;
+import android.media.AudioStreamType;
+import android.media.AudioUsage;
+import android.media.AudioUuid;
+import android.media.AudioVolumeGroup;
+import android.media.DeviceRole;
+import android.media.EffectDescriptor;
+import android.media.GetInputForAttrResponse;
+import android.media.GetOutputForAttrResponse;
+import android.media.IAudioPolicyServiceClient;
+import android.media.ICaptureStateListener;
+import android.media.Int;
+import android.media.permission.Identity;
+import android.media.SoundTriggerSession;
+
+/**
+ * IAudioPolicyService interface (see AudioPolicyInterface for method descriptions).
+ *
+ * {@hide}
+ */
+interface IAudioPolicyService {
+ oneway void onNewAudioModulesAvailable();
+
+ void setDeviceConnectionState(in AudioDevice device,
+ in AudioPolicyDeviceState state,
+ @utf8InCpp String deviceName,
+ in AudioFormat encodedFormat);
+
+ AudioPolicyDeviceState getDeviceConnectionState(in AudioDevice device);
+
+ void handleDeviceConfigChange(in AudioDevice device,
+ @utf8InCpp String deviceName,
+ in AudioFormat encodedFormat);
+
+ void setPhoneState(AudioMode state, int /* uid_t */ uid);
+
+ void setForceUse(AudioPolicyForceUse usage,
+ AudioPolicyForcedConfig config);
+
+ AudioPolicyForcedConfig getForceUse(AudioPolicyForceUse usage);
+
+ int /* audio_io_handle_t */ getOutput(AudioStreamType stream);
+
+ GetOutputForAttrResponse getOutputForAttr(in AudioAttributesInternal attr,
+ int /* audio_session_t */ session,
+ in Identity identity,
+ in AudioConfig config,
+ int /* Bitmask, indexed by AudioOutputFlags */ flags,
+ int /* audio_port_handle_t */ selectedDeviceId);
+
+ void startOutput(int /* audio_port_handle_t */ portId);
+
+ void stopOutput(int /* audio_port_handle_t */ portId);
+
+ void releaseOutput(int /* audio_port_handle_t */ portId);
+
+ GetInputForAttrResponse getInputForAttr(in AudioAttributesInternal attr,
+ int /* audio_io_handle_t */ input,
+ int /* audio_unique_id_t */ riid,
+ int /* audio_session_t */ session,
+ in Identity identity,
+ in AudioConfigBase config,
+ int /* Bitmask, indexed by AudioInputFlags */ flags,
+ int /* audio_port_handle_t */ selectedDeviceId);
+
+
+ void startInput(int /* audio_port_handle_t */ portId);
+
+ void stopInput(int /* audio_port_handle_t */ portId);
+
+ void releaseInput(int /* audio_port_handle_t */ portId);
+
+ void initStreamVolume(AudioStreamType stream,
+ int indexMin,
+ int indexMax);
+
+ void setStreamVolumeIndex(AudioStreamType stream,
+ int /* audio_devices_t */ device,
+ int index);
+
+ int getStreamVolumeIndex(AudioStreamType stream,
+ int /* audio_devices_t */ device);
+
+ void setVolumeIndexForAttributes(in AudioAttributesInternal attr,
+ int /* audio_devices_t */ device,
+ int index);
+
+ int getVolumeIndexForAttributes(in AudioAttributesInternal attr,
+ int /* audio_devices_t */ device);
+
+ int getMaxVolumeIndexForAttributes(in AudioAttributesInternal attr);
+
+ int getMinVolumeIndexForAttributes(in AudioAttributesInternal attr);
+
+ int /* product_strategy_t */ getStrategyForStream(AudioStreamType stream);
+
+ int /* bitmask of audio_devices_t */ getDevicesForStream(AudioStreamType stream);
+
+ AudioDevice[] getDevicesForAttributes(in AudioAttributesEx attr);
+
+ int /* audio_io_handle_t */ getOutputForEffect(in EffectDescriptor desc);
+
+ void registerEffect(in EffectDescriptor desc,
+ int /* audio_io_handle_t */ io,
+ int /* product_strategy_t */ strategy,
+ int /* audio_session_t */ session,
+ int id);
+
+ void unregisterEffect(int id);
+
+ void setEffectEnabled(int id, boolean enabled);
+
+ void moveEffectsToIo(in int[] ids, int /* audio_io_handle_t */ io);
+
+ boolean isStreamActive(AudioStreamType stream, int inPastMs);
+
+ boolean isStreamActiveRemotely(AudioStreamType stream, int inPastMs);
+
+ boolean isSourceActive(AudioSourceType source);
+
+ /**
+ * On input, count represents the maximum length of the returned array.
+ * On output, count is the total number of elements, which may be larger than the array size.
+ * Passing '0' on input and inspecting the value on output is a common way of determining the
+ * number of elements without actually retrieving them.
+ */
+ EffectDescriptor[] queryDefaultPreProcessing(int /* audio_session_t */ audioSession,
+ inout Int count);
+
+ int /* audio_unique_id_t */ addSourceDefaultEffect(in AudioUuid type,
+ @utf8InCpp String opPackageName,
+ in AudioUuid uuid,
+ int priority,
+ AudioSourceType source);
+
+ int /* audio_unique_id_t */ addStreamDefaultEffect(in AudioUuid type,
+ @utf8InCpp String opPackageName,
+ in AudioUuid uuid,
+ int priority,
+ AudioUsage usage);
+
+ void removeSourceDefaultEffect(int /* audio_unique_id_t */ id);
+
+ void removeStreamDefaultEffect(int /* audio_unique_id_t */ id);
+
+ void setSupportedSystemUsages(in AudioUsage[] systemUsages);
+
+ void setAllowedCapturePolicy(int /* uid_t */ uid,
+ int /* Bitmask of AudioFlags */ capturePolicy);
+
+ /**
+ * Check if offload is possible for given format, stream type, sample rate,
+ * bit rate, duration, video and streaming or offload property is enabled.
+ */
+ AudioOffloadMode getOffloadSupport(in AudioOffloadInfo info);
+
+ /**
+ * Check if direct playback is possible for given format, sample rate, channel mask and flags.
+ */
+ boolean isDirectOutputSupported(in AudioConfigBase config,
+ in AudioAttributesInternal attributes);
+
+ /**
+ * List available audio ports and their attributes. Returns the generation.
+ *
+ * On input, count represents the maximum length of the returned array.
+ * On output, count is the total number of elements, which may be larger than the array size.
+ * Passing '0' on input and inspecting the value on output is a common way of determining the
+ * number of elements without actually retrieving them.
+ */
+ int listAudioPorts(AudioPortRole role,
+ AudioPortType type,
+ inout Int count,
+ out AudioPort[] ports);
+
+ /** Get attributes for a given audio port. */
+ AudioPort getAudioPort(in AudioPort port);
+
+ /**
+ * Create an audio patch between several source and sink ports.
+ * The handle argument is used when updating an existing patch.
+ */
+ int /* audio_patch_handle_t */ createAudioPatch(in AudioPatch patch, int handle);
+
+ /** Release an audio patch. */
+ void releaseAudioPatch(int /* audio_patch_handle_t */ handle);
+
+ /**
+ * List existing audio patches. Returns the generation.
+ *
+ * On input, count represents the maximum length of the returned array.
+ * On output, count is the total number of elements, which may be larger than the array size.
+ * Passing '0' on input and inspecting the value on output is a common way of determining the
+ * number of elements without actually retrieving them.
+ */
+ int listAudioPatches(inout Int count, out AudioPatch[] patches);
+
+ /** Set audio port configuration. */
+ void setAudioPortConfig(in AudioPortConfig config);
+
+ void registerClient(IAudioPolicyServiceClient client);
+
+ void setAudioPortCallbacksEnabled(boolean enabled);
+
+ void setAudioVolumeGroupCallbacksEnabled(boolean enabled);
+
+ SoundTriggerSession acquireSoundTriggerSession();
+
+ void releaseSoundTriggerSession(int /* audio_session_t */ session);
+
+ AudioMode getPhoneState();
+
+ void registerPolicyMixes(in AudioMix[] mixes, boolean registration);
+
+ void setUidDeviceAffinities(int /* uid_t */ uid, in AudioDevice[] devices);
+
+ void removeUidDeviceAffinities(int /* uid_t */ uid);
+
+ void setUserIdDeviceAffinities(int userId, in AudioDevice[] devices);
+
+ void removeUserIdDeviceAffinities(int userId);
+
+ int /* audio_port_handle_t */ startAudioSource(in AudioPortConfig source,
+ in AudioAttributesInternal attributes);
+
+ void stopAudioSource(int /* audio_port_handle_t */ portId);
+
+ void setMasterMono(boolean mono);
+
+ boolean getMasterMono();
+
+ float getStreamVolumeDB(AudioStreamType stream, int index, int /* audio_devices_t */ device);
+
+ /**
+ * Populates supported surround formats and their enabled state in formats and formatsEnabled.
+ *
+ * On input, count represents the maximum length of the returned array.
+ * On output, count is the total number of elements, which may be larger than the array size.
+ * Passing '0' on input and inspecting the value on output is a common way of determining the
+ * number of elements without actually retrieving them.
+ */
+ void getSurroundFormats(inout Int count,
+ out AudioFormat[] formats,
+ out boolean[] formatsEnabled);
+
+ /**
+ * Populates the surround formats reported by the HDMI devices in formats.
+ *
+ * On input, count represents the maximum length of the returned array.
+ * On output, count is the total number of elements, which may be larger than the array size.
+ * Passing '0' on input and inspecting the value on output is a common way of determining the
+ * number of elements without actually retrieving them.
+ */
+ void getReportedSurroundFormats(inout Int count,
+ out AudioFormat[] formats);
+
+ AudioFormat[] getHwOffloadEncodingFormatsSupportedForA2DP();
+
+ void setSurroundFormatEnabled(AudioFormat audioFormat, boolean enabled);
+
+ void setAssistantUid(int /* uid_t */ uid);
+
+ void setA11yServicesUids(in int[] /* uid_t[] */ uids);
+
+ void setCurrentImeUid(int /* uid_t */ uid);
+
+ boolean isHapticPlaybackSupported();
+
+ AudioProductStrategy[] listAudioProductStrategies();
+ int /* product_strategy_t */ getProductStrategyFromAudioAttributes(in AudioAttributesEx aa,
+ boolean fallbackOnDefault);
+
+ AudioVolumeGroup[] listAudioVolumeGroups();
+ int /* volume_group_t */ getVolumeGroupFromAudioAttributes(in AudioAttributesEx aa,
+ boolean fallbackOnDefault);
+
+ void setRttEnabled(boolean enabled);
+
+ boolean isCallScreenModeSupported();
+
+ void setDevicesRoleForStrategy(int /* product_strategy_t */ strategy,
+ DeviceRole role,
+ in AudioDevice[] devices);
+
+ void removeDevicesRoleForStrategy(int /* product_strategy_t */ strategy,
+ DeviceRole role);
+
+ AudioDevice[] getDevicesForRoleAndStrategy(int /* product_strategy_t */ strategy,
+ DeviceRole role);
+
+ void setDevicesRoleForCapturePreset(AudioSourceType audioSource,
+ DeviceRole role,
+ in AudioDevice[] devices);
+
+ void addDevicesRoleForCapturePreset(AudioSourceType audioSource,
+ DeviceRole role,
+ in AudioDevice[] devices);
+
+ void removeDevicesRoleForCapturePreset(AudioSourceType audioSource,
+ DeviceRole role,
+ in AudioDevice[] devices);
+
+ void clearDevicesRoleForCapturePreset(AudioSourceType audioSource,
+ DeviceRole role);
+
+ AudioDevice[] getDevicesForRoleAndCapturePreset(AudioSourceType audioSource,
+ DeviceRole role);
+
+ boolean registerSoundTriggerCaptureStateListener(ICaptureStateListener listener);
+}
diff --git a/media/libaudioclient/aidl/android/media/IAudioPolicyServiceClient.aidl b/media/libaudioclient/aidl/android/media/IAudioPolicyServiceClient.aidl
new file mode 100644
index 0000000..a7782b8
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/IAudioPolicyServiceClient.aidl
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioConfigBase;
+import android.media.AudioSourceType;
+import android.media.EffectDescriptor;
+import android.media.RecordClientInfo;
+
+/**
+ * {@hide}
+ */
+oneway interface IAudioPolicyServiceClient {
+ /** Notifies a change of volume group. */
+ void onAudioVolumeGroupChanged(int /* volume_group_t */ group,
+ int flags);
+ /** Notifies a change of audio port configuration. */
+ void onAudioPortListUpdate();
+ /** Notifies a change of audio patch configuration. */
+ void onAudioPatchListUpdate();
+ /** Notifies a change in the mixing state of a specific mix in a dynamic audio policy. */
+ void onDynamicPolicyMixStateUpdate(@utf8InCpp String regId,
+ int state);
+ /** Notifies a change of audio recording configuration. */
+ void onRecordingConfigurationUpdate(int event,
+ in RecordClientInfo clientInfo,
+ in AudioConfigBase clientConfig,
+ in EffectDescriptor[] clientEffects,
+ in AudioConfigBase deviceConfig,
+ in EffectDescriptor[] effects,
+ int /* audio_patch_handle_t */ patchHandle,
+ AudioSourceType source);
+ /** Notifies a change of audio routing */
+ void onRoutingUpdated();
+}
diff --git a/media/libaudioclient/aidl/android/media/IAudioRecord.aidl b/media/libaudioclient/aidl/android/media/IAudioRecord.aidl
index ecf58b6..44ef80b 100644
--- a/media/libaudioclient/aidl/android/media/IAudioRecord.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioRecord.aidl
@@ -16,9 +16,13 @@
package android.media;
-import android.media.MicrophoneInfo;
+import android.media.MicrophoneInfoData;
-/* Native code must specify namespace media (media::IAudioRecord) when referring to this class */
+/**
+ * Native code must specify namespace media (media::IAudioRecord) when referring to this class.
+ *
+ * {@hide}
+ */
interface IAudioRecord {
/* After it's created the track is not active. Call start() to
@@ -35,7 +39,7 @@
/* Get a list of current active microphones.
*/
- void getActiveMicrophones(out MicrophoneInfo[] activeMicrophones);
+ void getActiveMicrophones(out MicrophoneInfoData[] activeMicrophones);
/* Set the microphone direction (for processing).
*/
@@ -44,4 +48,6 @@
/* Set the microphone zoom (for processing).
*/
void setPreferredMicrophoneFieldDimension(float zoom);
+
+ void shareAudioHistory(@utf8InCpp String sharedAudioPackageName, long sharedAudioStartMs);
}
diff --git a/media/libaudioclient/aidl/android/media/IAudioTrack.aidl b/media/libaudioclient/aidl/android/media/IAudioTrack.aidl
new file mode 100644
index 0000000..ac58925
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/IAudioTrack.aidl
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioDualMonoMode;
+import android.media.AudioPlaybackRate;
+import android.media.AudioTimestampInternal;
+import android.media.SharedFileRegion;
+import android.media.VolumeShaperConfiguration;
+import android.media.VolumeShaperOperation;
+import android.media.VolumeShaperState;
+
+/**
+ * Unless otherwise noted, methods returning int expect it to be interpreted as a status_t.
+ *
+ * {@hide}
+ */
+interface IAudioTrack {
+ /** Get this track's control block */
+ @nullable SharedFileRegion getCblk();
+
+ /**
+ * After it's created the track is not active. Call start() to
+ * make it active.
+ */
+ int start();
+
+ /**
+ * Stop a track. If set, the callback will cease being called and
+ * obtainBuffer will return an error. Buffers that are already released
+ * will continue to be processed, unless/until flush() is called.
+ */
+ void stop();
+
+ /**
+ * Flush a stopped or paused track. All pending/released buffers are discarded.
+ * This function has no effect if the track is not stopped or paused.
+ */
+ void flush();
+
+ /**
+ * Pause a track. If set, the callback will cease being called and
+ * obtainBuffer will return an error. Buffers that are already released
+ * will continue to be processed, unless/until flush() is called.
+ */
+ void pause();
+
+ /**
+ * Attach track auxiliary output to specified effect. Use effectId = 0
+ * to detach track from effect.
+ */
+ int attachAuxEffect(int effectId);
+
+ /** Send parameters to the audio hardware. */
+ int setParameters(@utf8InCpp String keyValuePairs);
+
+ /** Selects the presentation (if available). */
+ int selectPresentation(int presentationId, int programId);
+
+ /** Return NO_ERROR if timestamp is valid. */
+ int getTimestamp(out AudioTimestampInternal timestamp);
+
+ /** Signal the playback thread for a change in control block. */
+ void signal();
+
+ /** Sets the volume shaper. Returns the volume shaper status. */
+ int applyVolumeShaper(in VolumeShaperConfiguration configuration,
+ in VolumeShaperOperation operation);
+
+ /** Gets the volume shaper state. */
+ @nullable VolumeShaperState getVolumeShaperState(int id);
+
+ /**
+ * Returns DualMonoMode setting associated with this AudioTrack.
+ */
+ AudioDualMonoMode getDualMonoMode();
+
+ /**
+ * Sets DualMonoMode setting.
+ */
+ void setDualMonoMode(in AudioDualMonoMode mode);
+
+ /**
+ * Returns the AudioDescriptionMixLevel.
+ */
+ float getAudioDescriptionMixLevel();
+
+ /**
+ * Sets the AudioDescriptionMixLevel.
+ */
+ void setAudioDescriptionMixLevel(float leveldB);
+
+ /**
+ * Returns the AudioPlaybackRate.
+ */
+ AudioPlaybackRate getPlaybackRateParameters();
+
+ /**
+ * Sets the AudioPlaybackRate.
+ */
+ void setPlaybackRateParameters(in AudioPlaybackRate playbackRate);
+}
diff --git a/media/libaudioclient/aidl/android/media/IAudioTrackCallback.aidl b/media/libaudioclient/aidl/android/media/IAudioTrackCallback.aidl
index 21553b5..f593e22 100644
--- a/media/libaudioclient/aidl/android/media/IAudioTrackCallback.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioTrackCallback.aidl
@@ -17,7 +17,7 @@
package android.media;
/**
- * @hide
+ * {@hide}
*/
interface IAudioTrackCallback {
oneway void onCodecFormatChanged(in byte[] audioMetadata);
diff --git a/media/libaudioclient/aidl/android/media/ICaptureStateListener.aidl b/media/libaudioclient/aidl/android/media/ICaptureStateListener.aidl
index 8502282..3b2206a 100644
--- a/media/libaudioclient/aidl/android/media/ICaptureStateListener.aidl
+++ b/media/libaudioclient/aidl/android/media/ICaptureStateListener.aidl
@@ -16,6 +16,9 @@
package android.media;
+/**
+ * {@hide}
+ */
interface ICaptureStateListener {
void setCaptureState(boolean active);
}
diff --git a/media/libaudioclient/aidl/android/media/IEffect.aidl b/media/libaudioclient/aidl/android/media/IEffect.aidl
new file mode 100644
index 0000000..813cd5c
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/IEffect.aidl
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.SharedFileRegion;
+
+/**
+ * The IEffect interface enables control of the effect module activity and parameters.
+ *
+ * {@hide}
+ */
+interface IEffect {
+ /**
+ * Activates the effect module by connecting it to the audio path.
+ * @return a status_t code.
+ */
+ int enable();
+
+ /**
+ * Deactivates the effect module by disconnecting it from the audio path.
+ * @return a status_t code.
+ */
+ int disable();
+
+ /**
+ * Sends control, reads or writes parameters. Same behavior as the command() method in the
+ * effect control interface.
+ * Refer to system/audio_effect.h for a description of the valid command codes and their
+ * associated parameter and return messages. The cmdData and response parameters are expected to
+ * contain the respective types in a standard C memory layout.
+ *
+ * TODO(ytai): replace opaque byte arrays with strongly typed parameters.
+ */
+ int command(int cmdCode, in byte[] cmdData, int maxResponseSize, out byte[] response);
+
+ /**
+ * Disconnects the IEffect interface from the effect module.
+ * This will also delete the effect module and release the effect engine in the library if this
+ * is the last client disconnected. To release control of the effect module, the application can
+ * disconnect or delete the IEffect interface.
+ */
+ void disconnect();
+
+ /**
+ * returns a pointer to a shared memory area used to pass multiple parameters to the effect
+ * module without multiplying the binder calls.
+ *
+ * TODO(ytai): Explain how this should be used exactly.
+ */
+ SharedFileRegion getCblk();
+}
diff --git a/media/libaudioclient/aidl/android/media/IEffectClient.aidl b/media/libaudioclient/aidl/android/media/IEffectClient.aidl
new file mode 100644
index 0000000..3b6bcf1
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/IEffectClient.aidl
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * A callback interface for getting effect-related notifications.
+ *
+ * {@hide}
+ */
+interface IEffectClient {
+ /**
+ * Called whenever the status of granting control over the effect to the application
+ * has changed.
+ * @param controlGranted true iff the application has the control of the effect module.
+ */
+ oneway void controlStatusChanged(boolean controlGranted);
+
+ /**
+ * Called whenever the effect has been enabled or disabled. Received only if the client is not
+ * currently controlling the effect.
+ * @param enabled true if the effect module has been activated, false if deactivated.
+ */
+ oneway void enableStatusChanged(boolean enabled);
+
+ /**
+ * A command has been send to the effect engine. Received only if the client is not currently
+ * controlling the effect. See IEffect.command() for a description of buffer contents.
+ *
+ * TODO(ytai): replace opaque byte arrays with strongly typed parameters.
+ */
+ oneway void commandExecuted(int cmdCode, in byte[] cmdData, in byte[] replyData);
+}
diff --git a/media/libaudioclient/aidl/android/media/IPlayer.aidl b/media/libaudioclient/aidl/android/media/IPlayer.aidl
index a90fcdd..43bb7f3 100644
--- a/media/libaudioclient/aidl/android/media/IPlayer.aidl
+++ b/media/libaudioclient/aidl/android/media/IPlayer.aidl
@@ -16,11 +16,11 @@
package android.media;
-import android.media.VolumeShaper.Configuration;
-import android.media.VolumeShaper.Operation;
+import android.media.VolumeShaperConfiguration;
+import android.media.VolumeShaperOperation;
/**
- * @hide
+ * {@hide}
*/
interface IPlayer {
oneway void start();
@@ -29,6 +29,6 @@
oneway void setVolume(float vol);
oneway void setPan(float pan);
oneway void setStartDelayMs(int delayMs);
- oneway void applyVolumeShaper(in Configuration configuration,
- in Operation operation);
+ oneway void applyVolumeShaper(in VolumeShaperConfiguration configuration,
+ in VolumeShaperOperation operation);
}
diff --git a/media/libaudioclient/aidl/android/media/Int.aidl b/media/libaudioclient/aidl/android/media/Int.aidl
new file mode 100644
index 0000000..24f4d62
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/Int.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * This is a simple wrapper around an 'int', putting it in a parcelable, so it can be used as an
+ * inout parameter, be made @nullable, etc.
+ *
+ * {@hide}
+ */
+parcelable Int {
+ int value;
+}
diff --git a/media/libaudioclient/aidl/android/media/MicrophoneInfo.aidl b/media/libaudioclient/aidl/android/media/MicrophoneInfo.aidl
deleted file mode 100644
index d6e46cb..0000000
--- a/media/libaudioclient/aidl/android/media/MicrophoneInfo.aidl
+++ /dev/null
@@ -1,19 +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.
- */
-
-package android.media;
-
-parcelable MicrophoneInfo cpp_header "media/MicrophoneInfo.h";
diff --git a/media/libaudioclient/aidl/android/media/OpenInputRequest.aidl b/media/libaudioclient/aidl/android/media/OpenInputRequest.aidl
new file mode 100644
index 0000000..2e55526
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/OpenInputRequest.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioConfig;
+import android.media.AudioDevice;
+import android.media.AudioSourceType;
+
+/**
+ * {@hide}
+ */
+parcelable OpenInputRequest {
+ /** Interpreted as audio_module_handle_t. */
+ int module;
+ /** Interpreted as audio_io_handle_t. */
+ int input;
+ AudioConfig config;
+ AudioDevice device;
+ AudioSourceType source;
+ /** Bitmask, indexed by AudioInputFlag. */
+ int flags;
+}
diff --git a/media/libaudioclient/aidl/android/media/OpenInputResponse.aidl b/media/libaudioclient/aidl/android/media/OpenInputResponse.aidl
new file mode 100644
index 0000000..b613ba5
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/OpenInputResponse.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioConfig;
+import android.media.AudioDevice;
+
+/**
+ * {@hide}
+ */
+parcelable OpenInputResponse {
+ /** Interpreted as audio_io_handle_t. */
+ int input;
+ AudioConfig config;
+ AudioDevice device;
+}
diff --git a/media/libaudioclient/aidl/android/media/OpenOutputRequest.aidl b/media/libaudioclient/aidl/android/media/OpenOutputRequest.aidl
new file mode 100644
index 0000000..06b12e9
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/OpenOutputRequest.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioConfig;
+import android.media.AudioPort;
+
+/**
+ * {@hide}
+ */
+parcelable OpenOutputRequest {
+ /** Interpreted as audio_module_handle_t. */
+ int module;
+ AudioConfig config;
+ /** Type must be DEVICE. */
+ AudioPort device;
+ /** Bitmask, indexed by AudioOutputFlag. */
+ int flags;
+}
diff --git a/media/libaudioclient/aidl/android/media/OpenOutputResponse.aidl b/media/libaudioclient/aidl/android/media/OpenOutputResponse.aidl
new file mode 100644
index 0000000..a051969
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/OpenOutputResponse.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioConfig;
+
+/**
+ * {@hide}
+ */
+parcelable OpenOutputResponse {
+ /** Interpreted as audio_io_handle_t. */
+ int output;
+ AudioConfig config;
+ int latencyMs;
+ /** Bitmask, indexed by AudioOutputFlag. */
+ int flags;
+}
diff --git a/media/libaudioclient/aidl/android/media/RecordClientInfo.aidl b/media/libaudioclient/aidl/android/media/RecordClientInfo.aidl
new file mode 100644
index 0000000..3280460
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/RecordClientInfo.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioSourceType;
+
+/**
+ * {@hide}
+ */
+parcelable RecordClientInfo {
+ /** Interpreted as audio_unique_id_t. */
+ int riid;
+ /** Interpreted as uid_t. */
+ int uid;
+ /** Interpreted as audio_session_t. */
+ int session;
+ AudioSourceType source;
+ /** Interpreted as audio_port_handle_t. */
+ int portId;
+ boolean silenced;
+}
diff --git a/media/libaudioclient/aidl/android/media/RenderPosition.aidl b/media/libaudioclient/aidl/android/media/RenderPosition.aidl
new file mode 100644
index 0000000..98dc17a
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/RenderPosition.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * {@hide}
+ */
+parcelable RenderPosition {
+ int halFrames;
+ int dspFrames;
+}
diff --git a/media/libaudioclient/aidl/android/media/SoundTriggerSession.aidl b/media/libaudioclient/aidl/android/media/SoundTriggerSession.aidl
new file mode 100644
index 0000000..a829e59
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/SoundTriggerSession.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * {@hide}
+ */
+parcelable SoundTriggerSession {
+ /** Interpreted as audio_session_t. */
+ int session;
+ /** Interpreted as audio_io_handle_t. */
+ int ioHandle;
+ /** Interpreted as audio_devices_t. */
+ int device;
+}
diff --git a/media/libaudioclient/aidl/android/media/VolumeShaper/Configuration.aidl b/media/libaudioclient/aidl/android/media/VolumeShaper/Configuration.aidl
deleted file mode 100644
index fd0e60f..0000000
--- a/media/libaudioclient/aidl/android/media/VolumeShaper/Configuration.aidl
+++ /dev/null
@@ -1,19 +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.
- */
-
-package android.media.VolumeShaper;
-
-parcelable Configuration cpp_header "media/VolumeShaper.h";
diff --git a/media/libaudioclient/aidl/android/media/VolumeShaper/Operation.aidl b/media/libaudioclient/aidl/android/media/VolumeShaper/Operation.aidl
deleted file mode 100644
index 4290d9d..0000000
--- a/media/libaudioclient/aidl/android/media/VolumeShaper/Operation.aidl
+++ /dev/null
@@ -1,19 +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.
- */
-
-package android.media.VolumeShaper;
-
-parcelable Operation cpp_header "media/VolumeShaper.h";
diff --git a/media/libaudioclient/aidl/android/media/VolumeShaper/State.aidl b/media/libaudioclient/aidl/android/media/VolumeShaper/State.aidl
deleted file mode 100644
index f6a22b8..0000000
--- a/media/libaudioclient/aidl/android/media/VolumeShaper/State.aidl
+++ /dev/null
@@ -1,19 +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.
- */
-
-package android.media.VolumeShaper;
-
-parcelable State cpp_header "media/VolumeShaper.h";
diff --git a/media/libaudioclient/fuzzer/Android.bp b/media/libaudioclient/fuzzer/Android.bp
new file mode 100644
index 0000000..21e25b9
--- /dev/null
+++ b/media/libaudioclient/fuzzer/Android.bp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_fuzz {
+ name: "audioflinger_fuzzer",
+ srcs: [
+ "audioflinger_fuzzer.cpp",
+ ],
+ static_libs: [
+ "android.hardware.audio.common@7.0-enums",
+ "effect-aidl-cpp",
+ "libaudioclient",
+ "libbase",
+ "libcgrouprc",
+ "libcgrouprc_format",
+ "libcutils",
+ "libjsoncpp",
+ "liblog",
+ "libmediametrics",
+ "libmediametricsservice",
+ "libmedia_helper",
+ "libprocessgroup",
+ "shared-file-region-aidl-cpp",
+ ],
+ shared_libs: [
+ "android.hardware.audio.common-util",
+ "audioclient-types-aidl-cpp",
+ "audioflinger-aidl-cpp",
+ "audiopolicy-aidl-cpp",
+ "audiopolicy-types-aidl-cpp",
+ "av-types-aidl-cpp",
+ "capture_state_listener-aidl-cpp",
+ "libaudioclient_aidl_conversion",
+ "libaudioflinger",
+ "libaudiofoundation",
+ "libaudiomanager",
+ "libaudiopolicy",
+ "libaudioutils",
+ "libbinder",
+ "libdl",
+ "libmediautils",
+ "libnblog",
+ "libutils",
+ "libxml2",
+ "mediametricsservice-aidl-cpp",
+ "media_permission-aidl-cpp",
+ ],
+ header_libs: [
+ "libaudiofoundation_headers",
+ "libmedia_headers",
+ ],
+ fuzz_config: {
+ cc: [
+ "android-media-fuzzing-reports@google.com",
+ ],
+ componentid: 155276,
+ },
+}
diff --git a/media/libaudioclient/fuzzer/README.md b/media/libaudioclient/fuzzer/README.md
new file mode 100644
index 0000000..ada6c49
--- /dev/null
+++ b/media/libaudioclient/fuzzer/README.md
@@ -0,0 +1,80 @@
+# Fuzzer for libaudioflinger
+
+## Plugin Design Considerations
+The fuzzer plugin for libaudioflinger is designed based on the understanding of the
+library and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer. The fuzzer
+covers libaudioflinger APIs as called from libaudioclient through IPC.
+
+libaudioflinger supports the following parameters:
+1. Unique IDs (parameter name: `uniqueId`)
+2. Audio Mode (parameter name: `mode`)
+3. Session ID (parameter name: `sessionId`)
+4. Encapsulation Mode (parameter name: `encapsulationMode`)
+5. Audio Port Role (parameter name: `portRole`)
+6. Audio Port Type (parameter name: `portType`)
+7. Audio Stream Type (parameter name: `streamType`)
+8. Audio Format (parameter name: `format`)
+9. Audio Channel Mask (parameter name: `channelMask`)
+10. Usage (parameter name: `usage`)
+11. Audio Content Type (parameter name: `contentType`)
+12. Input Source (parameter name: `inputSource`)
+13. Input Flags (parameter name: `inputFlags`)
+14. Output Flags (parameter name: `outputFlags`)
+15. Audio Gain Mode (parameter name: `gainMode`)
+16. Audio Device (parameter name: `device`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `uniqueId` | 0. `AUDIO_UNIQUE_ID_USE_UNSPECIFIED` 1. `AUDIO_UNIQUE_ID_USE_SESSION` 2. `AUDIO_UNIQUE_ID_USE_MODULE` 3. `AUDIO_UNIQUE_ID_USE_EFFECT` 4. `AUDIO_UNIQUE_ID_USE_PATCH` 5. `AUDIO_UNIQUE_ID_USE_OUTPUT` 6. `AUDIO_UNIQUE_ID_USE_INPUT` 7. `AUDIO_UNIQUE_ID_USE_CLIENT` 8. `AUDIO_UNIQUE_ID_USE_MAX` | Value obtained from FuzzedDataProvider
+| `mode` | 0.`AUDIO_MODE_INVALID` 1. `AUDIO_MODE_CURRENT` 2. ` AUDIO_MODE_NORMAL` 3. `AUDIO_MODE_RINGTONE` 4. `AUDIO_MODE_IN_CALL` 5. `AUDIO_MODE_IN_COMMUNICATION` 6. `AUDIO_MODE_CALL_SCREEN` | Value obtained from FuzzedDataProvider|
+| `sessionId` | 0. `AUDIO_SESSION_NONE` 1. `AUDIO_SESSION_OUTPUT_STAGE` 2. `AUDIO_SESSION_DEVICE` | Value obtained from FuzzedDataProvider|
+| `encapsulationMode` | 0. `AUDIO_ENCAPSULATION_MODE_NONE` 1. `AUDIO_ENCAPSULATION_MODE_ELEMENTARY_STREAM` 2. `AUDIO_ENCAPSULATION_MODE_HANDLE` | Value obtained from FuzzedDataProvider|
+| `portRole` | 0. `AUDIO_PORT_ROLE_NONE` 1. `AUDIO_PORT_ROLE_SOURCE` 2. `AUDIO_PORT_ROLE_SINK` | Value obtained from FuzzedDataProvider|
+| `portType` | 0. `AUDIO_PORT_TYPE_NONE` 1. `AUDIO_PORT_TYPE_DEVICE` 2. `AUDIO_PORT_TYPE_MIX` 3. `AUDIO_PORT_TYPE_SESSION`| Value obtained from FuzzedDataProvider|
+| `streamType` | 15 values of type `audio_stream_type_t` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `format` | 77 values of type `audio_format_t` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `channelMask` | 83 values of type `audio_channel_mask_t` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `usage` | 22 values of type `audio_usage_t` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `contentType` | 5 values of type `audio_content_type_t` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `inputSource` | 14 values of type `audio_source_t` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `inputFlags` | 9 values of type `audio_input_flags_t` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `outputFlags` | 16 values of type `audio_output_flags_t` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `gainMode` | 3 values of type `audio_gain_mode_t` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `device` | 66 values of type `audio_devices_t` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+
+This also ensures that the plugin is always deterministic for any given input.
+
+##### Maximize utilization of input data
+The plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesn't `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build audioflinger_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+ $ mm -j$(nproc) audioflinger_fuzzer
+```
+
+#### Steps to run
+Create a directory CORPUS_DIR and copy some files to that folder
+Push this directory to device.
+
+To run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/audioflinger_fuzzer/audioflinger_fuzzer CORPUS_DIR
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.co
diff --git a/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp b/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
new file mode 100644
index 0000000..1b75917
--- /dev/null
+++ b/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
@@ -0,0 +1,737 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * NOTE
+ * 1) The input to AudioFlinger binder calls are fuzzed in this fuzzer
+ * 2) AudioFlinger crashes due to the fuzzer are detected by the
+ Binder DeathRecipient, where the fuzzer aborts if AudioFlinger dies
+ */
+
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#include <android/media/permission/Identity.h>
+#include <binder/IServiceManager.h>
+#include <binder/MemoryDealer.h>
+#include <media/AidlConversion.h>
+#include <media/AudioEffect.h>
+#include <media/AudioRecord.h>
+#include <media/AudioSystem.h>
+#include <media/AudioTrack.h>
+#include <media/IAudioFlinger.h>
+#include "fuzzer/FuzzedDataProvider.h"
+
+#define MAX_STRING_LENGTH 256
+#define MAX_ARRAY_LENGTH 256
+
+constexpr int32_t kMinSampleRateHz = 4000;
+constexpr int32_t kMaxSampleRateHz = 192000;
+constexpr int32_t kSampleRateUnspecified = 0;
+
+using namespace std;
+using namespace android;
+
+namespace xsd {
+using namespace ::android::audio::policy::configuration::V7_0;
+}
+
+using media::permission::Identity;
+
+constexpr audio_unique_id_use_t kUniqueIds[] = {
+ AUDIO_UNIQUE_ID_USE_UNSPECIFIED, AUDIO_UNIQUE_ID_USE_SESSION, AUDIO_UNIQUE_ID_USE_MODULE,
+ AUDIO_UNIQUE_ID_USE_EFFECT, AUDIO_UNIQUE_ID_USE_PATCH, AUDIO_UNIQUE_ID_USE_OUTPUT,
+ AUDIO_UNIQUE_ID_USE_INPUT, AUDIO_UNIQUE_ID_USE_CLIENT, AUDIO_UNIQUE_ID_USE_MAX,
+};
+
+constexpr audio_mode_t kModes[] = {
+ AUDIO_MODE_INVALID, AUDIO_MODE_CURRENT, AUDIO_MODE_NORMAL, AUDIO_MODE_RINGTONE,
+ AUDIO_MODE_IN_CALL, AUDIO_MODE_IN_COMMUNICATION, AUDIO_MODE_CALL_SCREEN};
+
+constexpr audio_session_t kSessionId[] = {AUDIO_SESSION_NONE, AUDIO_SESSION_OUTPUT_STAGE,
+ AUDIO_SESSION_DEVICE};
+
+constexpr audio_encapsulation_mode_t kEncapsulation[] = {
+ AUDIO_ENCAPSULATION_MODE_NONE,
+ AUDIO_ENCAPSULATION_MODE_ELEMENTARY_STREAM,
+ AUDIO_ENCAPSULATION_MODE_HANDLE,
+};
+
+constexpr audio_port_role_t kPortRoles[] = {
+ AUDIO_PORT_ROLE_NONE,
+ AUDIO_PORT_ROLE_SOURCE,
+ AUDIO_PORT_ROLE_SINK,
+};
+
+constexpr audio_port_type_t kPortTypes[] = {
+ AUDIO_PORT_TYPE_NONE,
+ AUDIO_PORT_TYPE_DEVICE,
+ AUDIO_PORT_TYPE_MIX,
+ AUDIO_PORT_TYPE_SESSION,
+};
+
+template <typename T, typename X, typename FUNC>
+std::vector<T> getFlags(const xsdc_enum_range<X> &range, const FUNC &func,
+ const std::string &findString = {}) {
+ std::vector<T> vec;
+ for (const auto &xsdEnumVal : range) {
+ T enumVal;
+ std::string enumString = toString(xsdEnumVal);
+ if (enumString.find(findString) != std::string::npos &&
+ func(enumString.c_str(), &enumVal)) {
+ vec.push_back(enumVal);
+ }
+ }
+ return vec;
+}
+
+static const std::vector<audio_stream_type_t> kStreamtypes =
+ getFlags<audio_stream_type_t, xsd::AudioStreamType, decltype(audio_stream_type_from_string)>(
+ xsdc_enum_range<xsd::AudioStreamType>{}, audio_stream_type_from_string);
+
+static const std::vector<audio_format_t> kFormats =
+ getFlags<audio_format_t, xsd::AudioFormat, decltype(audio_format_from_string)>(
+ xsdc_enum_range<xsd::AudioFormat>{}, audio_format_from_string);
+
+static const std::vector<audio_channel_mask_t> kChannelMasks =
+ getFlags<audio_channel_mask_t, xsd::AudioChannelMask, decltype(audio_channel_mask_from_string)>(
+ xsdc_enum_range<xsd::AudioChannelMask>{}, audio_channel_mask_from_string);
+
+static const std::vector<audio_usage_t> kUsages =
+ getFlags<audio_usage_t, xsd::AudioUsage, decltype(audio_usage_from_string)>(
+ xsdc_enum_range<xsd::AudioUsage>{}, audio_usage_from_string);
+
+static const std::vector<audio_content_type_t> kContentType =
+ getFlags<audio_content_type_t, xsd::AudioContentType, decltype(audio_content_type_from_string)>(
+ xsdc_enum_range<xsd::AudioContentType>{}, audio_content_type_from_string);
+
+static const std::vector<audio_source_t> kInputSources =
+ getFlags<audio_source_t, xsd::AudioSource, decltype(audio_source_from_string)>(
+ xsdc_enum_range<xsd::AudioSource>{}, audio_source_from_string);
+
+static const std::vector<audio_gain_mode_t> kGainModes =
+ getFlags<audio_gain_mode_t, xsd::AudioGainMode, decltype(audio_gain_mode_from_string)>(
+ xsdc_enum_range<xsd::AudioGainMode>{}, audio_gain_mode_from_string);
+
+static const std::vector<audio_devices_t> kDevices =
+ getFlags<audio_devices_t, xsd::AudioDevice, decltype(audio_device_from_string)>(
+ xsdc_enum_range<xsd::AudioDevice>{}, audio_device_from_string);
+
+static const std::vector<audio_input_flags_t> kInputFlags =
+ getFlags<audio_input_flags_t, xsd::AudioInOutFlag, decltype(audio_input_flag_from_string)>(
+ xsdc_enum_range<xsd::AudioInOutFlag>{}, audio_input_flag_from_string, "_INPUT_");
+
+static const std::vector<audio_output_flags_t> kOutputFlags =
+ getFlags<audio_output_flags_t, xsd::AudioInOutFlag, decltype(audio_output_flag_from_string)>(
+ xsdc_enum_range<xsd::AudioInOutFlag>{}, audio_output_flag_from_string, "_OUTPUT_");
+
+template <typename T, size_t size>
+T getValue(FuzzedDataProvider *fdp, const T (&arr)[size]) {
+ return arr[fdp->ConsumeIntegralInRange<int32_t>(0, size - 1)];
+}
+
+template <typename T>
+T getValue(FuzzedDataProvider *fdp, std::vector<T> vec) {
+ return vec[fdp->ConsumeIntegralInRange<int32_t>(0, vec.size() - 1)];
+}
+
+int32_t getSampleRate(FuzzedDataProvider *fdp) {
+ if (fdp->ConsumeBool()) {
+ return fdp->ConsumeIntegralInRange<int32_t>(kMinSampleRateHz, kMaxSampleRateHz);
+ }
+ return kSampleRateUnspecified;
+}
+
+class DeathNotifier : public IBinder::DeathRecipient {
+ public:
+ void binderDied(const wp<IBinder> &) { abort(); }
+};
+
+class AudioFlingerFuzzer {
+ public:
+ AudioFlingerFuzzer(const uint8_t *data, size_t size);
+ void process();
+
+ private:
+ FuzzedDataProvider mFdp;
+ void invokeAudioTrack();
+ void invokeAudioRecord();
+ status_t invokeAudioEffect();
+ void invokeAudioSystem();
+ status_t invokeAudioInputDevice();
+ status_t invokeAudioOutputDevice();
+ void invokeAudioPatch();
+
+ sp<DeathNotifier> mDeathNotifier;
+};
+
+AudioFlingerFuzzer::AudioFlingerFuzzer(const uint8_t *data, size_t size) : mFdp(data, size) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("media.audio_flinger"));
+ if (binder == nullptr) {
+ return;
+ }
+ mDeathNotifier = new DeathNotifier();
+ binder->linkToDeath(mDeathNotifier);
+}
+
+void AudioFlingerFuzzer::invokeAudioTrack() {
+ uint32_t sampleRate = getSampleRate(&mFdp);
+ audio_format_t format = getValue(&mFdp, kFormats);
+ audio_channel_mask_t channelMask = getValue(&mFdp, kChannelMasks);
+ size_t frameCount = static_cast<size_t>(mFdp.ConsumeIntegral<uint32_t>());
+ int32_t notificationFrames = mFdp.ConsumeIntegral<int32_t>();
+ uint32_t useSharedBuffer = mFdp.ConsumeBool();
+ audio_output_flags_t flags = getValue(&mFdp, kOutputFlags);
+ audio_session_t sessionId = getValue(&mFdp, kSessionId);
+ audio_usage_t usage = getValue(&mFdp, kUsages);
+ audio_content_type_t contentType = getValue(&mFdp, kContentType);
+ audio_attributes_t attributes = {};
+ sp<IMemory> sharedBuffer;
+ sp<MemoryDealer> heap = nullptr;
+ audio_offload_info_t offloadInfo = AUDIO_INFO_INITIALIZER;
+
+ bool offload = false;
+ bool fast = ((flags & AUDIO_OUTPUT_FLAG_FAST) != 0);
+
+ if (useSharedBuffer != 0) {
+ size_t heapSize = audio_channel_count_from_out_mask(channelMask) *
+ audio_bytes_per_sample(format) * frameCount;
+ heap = new MemoryDealer(heapSize, "AudioTrack Heap Base");
+ sharedBuffer = heap->allocate(heapSize);
+ frameCount = 0;
+ notificationFrames = 0;
+ }
+ if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
+ offloadInfo.sample_rate = sampleRate;
+ offloadInfo.channel_mask = channelMask;
+ offloadInfo.format = format;
+ offload = true;
+ }
+
+ attributes.content_type = contentType;
+ attributes.usage = usage;
+ sp<AudioTrack> track = new AudioTrack();
+
+ // TODO b/182392769: use identity util
+ Identity i;
+ i.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(getuid()));
+ i.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(getpid()));
+ track->set(AUDIO_STREAM_DEFAULT, sampleRate, format, channelMask, frameCount, flags, nullptr,
+ nullptr, notificationFrames, sharedBuffer, false, sessionId,
+ ((fast && sharedBuffer == 0) || offload) ? AudioTrack::TRANSFER_CALLBACK
+ : AudioTrack::TRANSFER_DEFAULT,
+ offload ? &offloadInfo : nullptr, i, &attributes, false, 1.0f,
+ AUDIO_PORT_HANDLE_NONE);
+
+ status_t status = track->initCheck();
+ if (status != NO_ERROR) {
+ track.clear();
+ return;
+ }
+ track->getSampleRate();
+ track->latency();
+ track->getUnderrunCount();
+ track->streamType();
+ track->channelCount();
+ track->getNotificationPeriodInFrames();
+ uint32_t bufferSizeInFrames = mFdp.ConsumeIntegral<uint32_t>();
+ track->setBufferSizeInFrames(bufferSizeInFrames);
+ track->getBufferSizeInFrames();
+
+ int64_t duration = mFdp.ConsumeIntegral<int64_t>();
+ track->getBufferDurationInUs(&duration);
+ sp<IMemory> sharedBuffer2 = track->sharedBuffer();
+ track->setCallerName(mFdp.ConsumeRandomLengthString(MAX_STRING_LENGTH));
+
+ track->setVolume(mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>());
+ track->setVolume(mFdp.ConsumeFloatingPoint<float>());
+ track->setAuxEffectSendLevel(mFdp.ConsumeFloatingPoint<float>());
+
+ float auxEffectSendLevel;
+ track->getAuxEffectSendLevel(&auxEffectSendLevel);
+ track->setSampleRate(getSampleRate(&mFdp));
+ track->getSampleRate();
+ track->getOriginalSampleRate();
+
+ AudioPlaybackRate playbackRate = {};
+ playbackRate.mSpeed = mFdp.ConsumeFloatingPoint<float>();
+ playbackRate.mPitch = mFdp.ConsumeFloatingPoint<float>();
+ track->setPlaybackRate(playbackRate);
+ track->getPlaybackRate();
+ track->setLoop(mFdp.ConsumeIntegral<uint32_t>(), mFdp.ConsumeIntegral<uint32_t>(),
+ mFdp.ConsumeIntegral<uint32_t>());
+ track->setMarkerPosition(mFdp.ConsumeIntegral<uint32_t>());
+
+ uint32_t marker = {};
+ track->getMarkerPosition(&marker);
+ track->setPositionUpdatePeriod(mFdp.ConsumeIntegral<uint32_t>());
+
+ uint32_t updatePeriod = {};
+ track->getPositionUpdatePeriod(&updatePeriod);
+ track->setPosition(mFdp.ConsumeIntegral<uint32_t>());
+ uint32_t position = {};
+ track->getPosition(&position);
+ track->getBufferPosition(&position);
+ track->reload();
+ track->start();
+ track->pause();
+ track->flush();
+ track->stop();
+ track->stopped();
+}
+
+void AudioFlingerFuzzer::invokeAudioRecord() {
+ int32_t notificationFrames = mFdp.ConsumeIntegral<int32_t>();
+ uint32_t sampleRate = getSampleRate(&mFdp);
+ size_t frameCount = static_cast<size_t>(mFdp.ConsumeIntegral<uint32_t>());
+ audio_format_t format = getValue(&mFdp, kFormats);
+ audio_channel_mask_t channelMask = getValue(&mFdp, kChannelMasks);
+ audio_input_flags_t flags = getValue(&mFdp, kInputFlags);
+ audio_session_t sessionId = getValue(&mFdp, kSessionId);
+ audio_source_t inputSource = getValue(&mFdp, kInputSources);
+
+ audio_attributes_t attributes = {};
+ bool fast = ((flags & AUDIO_OUTPUT_FLAG_FAST) != 0);
+
+ attributes.source = inputSource;
+
+ // TODO b/182392769: use identity util
+ Identity i;
+ i.packageName = std::string(mFdp.ConsumeRandomLengthString().c_str());
+ sp<AudioRecord> record = new AudioRecord(i);
+ record->set(AUDIO_SOURCE_DEFAULT, sampleRate, format, channelMask, frameCount, nullptr, nullptr,
+ notificationFrames, false, sessionId,
+ fast ? AudioRecord::TRANSFER_CALLBACK : AudioRecord::TRANSFER_DEFAULT, flags,
+ getuid(), getpid(), &attributes, AUDIO_PORT_HANDLE_NONE);
+ status_t status = record->initCheck();
+ if (status != NO_ERROR) {
+ return;
+ }
+ record->latency();
+ record->format();
+ record->channelCount();
+ record->frameCount();
+ record->frameSize();
+ record->inputSource();
+ record->getNotificationPeriodInFrames();
+ record->start();
+ record->stop();
+ record->stopped();
+
+ uint32_t marker = mFdp.ConsumeIntegral<uint32_t>();
+ record->setMarkerPosition(marker);
+ record->getMarkerPosition(&marker);
+
+ uint32_t updatePeriod = mFdp.ConsumeIntegral<uint32_t>();
+ record->setPositionUpdatePeriod(updatePeriod);
+ record->getPositionUpdatePeriod(&updatePeriod);
+
+ uint32_t position;
+ record->getPosition(&position);
+
+ ExtendedTimestamp timestamp;
+ record->getTimestamp(×tamp);
+ record->getSessionId();
+ record->getCallerName();
+ android::AudioRecord::Buffer audioBuffer;
+ int32_t waitCount = mFdp.ConsumeIntegral<int32_t>();
+ size_t nonContig = static_cast<size_t>(mFdp.ConsumeIntegral<uint32_t>());
+ audioBuffer.frameCount = static_cast<size_t>(mFdp.ConsumeIntegral<uint32_t>());
+ record->obtainBuffer(&audioBuffer, waitCount, &nonContig);
+ bool blocking = false;
+ record->read(audioBuffer.raw, audioBuffer.size, blocking);
+ record->getInputFramesLost();
+ record->getFlags();
+
+ std::vector<media::MicrophoneInfo> activeMicrophones;
+ record->getActiveMicrophones(&activeMicrophones);
+ record->releaseBuffer(&audioBuffer);
+
+ audio_port_handle_t deviceId =
+ static_cast<audio_port_handle_t>(mFdp.ConsumeIntegral<int32_t>());
+ record->setInputDevice(deviceId);
+ record->getInputDevice();
+ record->getRoutedDeviceId();
+ record->getPortId();
+}
+
+struct EffectClient : public android::media::BnEffectClient {
+ EffectClient() {}
+ binder::Status controlStatusChanged(bool controlGranted __unused) override {
+ return binder::Status::ok();
+ }
+ binder::Status enableStatusChanged(bool enabled __unused) override {
+ return binder::Status::ok();
+ }
+ binder::Status commandExecuted(int32_t cmdCode __unused,
+ const std::vector<uint8_t> &cmdData __unused,
+ const std::vector<uint8_t> &replyData __unused) override {
+ return binder::Status::ok();
+ }
+};
+
+status_t AudioFlingerFuzzer::invokeAudioEffect() {
+ effect_uuid_t type;
+ type.timeLow = mFdp.ConsumeIntegral<uint32_t>();
+ type.timeMid = mFdp.ConsumeIntegral<uint16_t>();
+ type.timeHiAndVersion = mFdp.ConsumeIntegral<uint16_t>();
+ type.clockSeq = mFdp.ConsumeIntegral<uint16_t>();
+ for (int i = 0; i < 6; ++i) {
+ type.node[i] = mFdp.ConsumeIntegral<uint8_t>();
+ }
+
+ effect_descriptor_t descriptor = {};
+ descriptor.type = type;
+ descriptor.uuid = *EFFECT_UUID_NULL;
+
+ sp<EffectClient> effectClient(new EffectClient());
+
+ const int32_t priority = mFdp.ConsumeIntegral<int32_t>();
+ audio_session_t sessionId = static_cast<audio_session_t>(mFdp.ConsumeIntegral<int32_t>());
+ const audio_io_handle_t io = mFdp.ConsumeIntegral<int32_t>();
+ std::string opPackageName = static_cast<std::string>(mFdp.ConsumeRandomLengthString().c_str());
+ AudioDeviceTypeAddr device;
+
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (!af) {
+ return NO_ERROR;
+ }
+
+ media::CreateEffectRequest request{};
+ request.desc =
+ VALUE_OR_RETURN_STATUS(legacy2aidl_effect_descriptor_t_EffectDescriptor(descriptor));
+ request.client = effectClient;
+ request.priority = priority;
+ request.output = io;
+ request.sessionId = sessionId;
+ request.device = VALUE_OR_RETURN_STATUS(legacy2aidl_AudioDeviceTypeAddress(device));
+ // TODO b/182392769: use identity util
+ request.identity.packageName = opPackageName;
+ request.identity.pid = VALUE_OR_RETURN_STATUS(legacy2aidl_pid_t_int32_t(getpid()));
+ request.probe = false;
+
+ media::CreateEffectResponse response{};
+ status_t status = af->createEffect(request, &response);
+
+ if (status != OK) {
+ return NO_ERROR;
+ }
+
+ descriptor =
+ VALUE_OR_RETURN_STATUS(aidl2legacy_EffectDescriptor_effect_descriptor_t(response.desc));
+
+ uint32_t numEffects;
+ af->queryNumberEffects(&numEffects);
+
+ uint32_t queryIndex = mFdp.ConsumeIntegral<uint32_t>();
+ af->queryEffect(queryIndex, &descriptor);
+
+ effect_descriptor_t getDescriptor;
+ uint32_t preferredTypeFlag = mFdp.ConsumeIntegral<int32_t>();
+ af->getEffectDescriptor(&descriptor.uuid, &descriptor.type, preferredTypeFlag, &getDescriptor);
+
+ sessionId = static_cast<audio_session_t>(mFdp.ConsumeIntegral<int32_t>());
+ audio_io_handle_t srcOutput = mFdp.ConsumeIntegral<int32_t>();
+ audio_io_handle_t dstOutput = mFdp.ConsumeIntegral<int32_t>();
+ af->moveEffects(sessionId, srcOutput, dstOutput);
+
+ int effectId = mFdp.ConsumeIntegral<int32_t>();
+ sessionId = static_cast<audio_session_t>(mFdp.ConsumeIntegral<int32_t>());
+ af->setEffectSuspended(effectId, sessionId, mFdp.ConsumeBool());
+ return NO_ERROR;
+}
+
+void AudioFlingerFuzzer::invokeAudioSystem() {
+ AudioSystem::muteMicrophone(mFdp.ConsumeBool());
+ AudioSystem::setMasterMute(mFdp.ConsumeBool());
+ AudioSystem::setMasterVolume(mFdp.ConsumeFloatingPoint<float>());
+ AudioSystem::setMasterBalance(mFdp.ConsumeFloatingPoint<float>());
+ AudioSystem::setVoiceVolume(mFdp.ConsumeFloatingPoint<float>());
+
+ float volume;
+ AudioSystem::getMasterVolume(&volume);
+
+ bool state;
+ AudioSystem::getMasterMute(&state);
+ AudioSystem::isMicrophoneMuted(&state);
+
+ audio_stream_type_t stream = getValue(&mFdp, kStreamtypes);
+ AudioSystem::setStreamMute(getValue(&mFdp, kStreamtypes), mFdp.ConsumeBool());
+
+ stream = getValue(&mFdp, kStreamtypes);
+ AudioSystem::setStreamVolume(stream, mFdp.ConsumeFloatingPoint<float>(),
+ mFdp.ConsumeIntegral<int32_t>());
+
+ audio_mode_t mode = getValue(&mFdp, kModes);
+ AudioSystem::setMode(mode);
+
+ size_t frameCount;
+ stream = getValue(&mFdp, kStreamtypes);
+ AudioSystem::getOutputFrameCount(&frameCount, stream);
+
+ uint32_t latency;
+ stream = getValue(&mFdp, kStreamtypes);
+ AudioSystem::getOutputLatency(&latency, stream);
+
+ stream = getValue(&mFdp, kStreamtypes);
+ AudioSystem::getStreamVolume(stream, &volume, mFdp.ConsumeIntegral<int32_t>());
+
+ stream = getValue(&mFdp, kStreamtypes);
+ AudioSystem::getStreamMute(stream, &state);
+
+ uint32_t samplingRate;
+ AudioSystem::getSamplingRate(mFdp.ConsumeIntegral<int32_t>(), &samplingRate);
+
+ AudioSystem::getFrameCount(mFdp.ConsumeIntegral<int32_t>(), &frameCount);
+ AudioSystem::getLatency(mFdp.ConsumeIntegral<int32_t>(), &latency);
+ AudioSystem::setVoiceVolume(mFdp.ConsumeFloatingPoint<float>());
+
+ uint32_t halFrames;
+ uint32_t dspFrames;
+ AudioSystem::getRenderPosition(mFdp.ConsumeIntegral<int32_t>(), &halFrames, &dspFrames);
+
+ AudioSystem::getInputFramesLost(mFdp.ConsumeIntegral<int32_t>());
+ AudioSystem::getInputFramesLost(mFdp.ConsumeIntegral<int32_t>());
+
+ audio_unique_id_use_t uniqueIdUse = getValue(&mFdp, kUniqueIds);
+ AudioSystem::newAudioUniqueId(uniqueIdUse);
+
+ audio_session_t sessionId = getValue(&mFdp, kSessionId);
+ pid_t pid = mFdp.ConsumeBool() ? getpid() : mFdp.ConsumeIntegral<int32_t>();
+ uid_t uid = mFdp.ConsumeBool() ? getuid() : mFdp.ConsumeIntegral<int32_t>();
+ AudioSystem::acquireAudioSessionId(sessionId, pid, uid);
+
+ pid = mFdp.ConsumeBool() ? getpid() : mFdp.ConsumeIntegral<int32_t>();
+ sessionId = getValue(&mFdp, kSessionId);
+ AudioSystem::releaseAudioSessionId(sessionId, pid);
+
+ sessionId = getValue(&mFdp, kSessionId);
+ AudioSystem::getAudioHwSyncForSession(sessionId);
+
+ AudioSystem::systemReady();
+ AudioSystem::getFrameCountHAL(mFdp.ConsumeIntegral<int32_t>(), &frameCount);
+
+ size_t buffSize;
+ uint32_t sampleRate = getSampleRate(&mFdp);
+ audio_format_t format = getValue(&mFdp, kFormats);
+ audio_channel_mask_t channelMask = getValue(&mFdp, kChannelMasks);
+ AudioSystem::getInputBufferSize(sampleRate, format, channelMask, &buffSize);
+
+ AudioSystem::getPrimaryOutputSamplingRate();
+ AudioSystem::getPrimaryOutputFrameCount();
+ AudioSystem::setLowRamDevice(mFdp.ConsumeBool(), mFdp.ConsumeIntegral<int64_t>());
+
+ std::vector<media::MicrophoneInfo> microphones;
+ AudioSystem::getMicrophones(µphones);
+
+ std::vector<pid_t> pids;
+ pids.insert(pids.begin(), getpid());
+ for (int i = 1; i < mFdp.ConsumeIntegralInRange<int32_t>(2, MAX_ARRAY_LENGTH); ++i) {
+ pids.insert(pids.begin() + i, static_cast<pid_t>(mFdp.ConsumeIntegral<int32_t>()));
+ }
+ AudioSystem::setAudioHalPids(pids);
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (!af) {
+ return;
+ }
+ af->setRecordSilenced(mFdp.ConsumeIntegral<uint32_t>(), mFdp.ConsumeBool());
+
+ float balance = mFdp.ConsumeFloatingPoint<float>();
+ af->getMasterBalance(&balance);
+ af->invalidateStream(static_cast<audio_stream_type_t>(mFdp.ConsumeIntegral<uint32_t>()));
+}
+
+status_t AudioFlingerFuzzer::invokeAudioInputDevice() {
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (!af) {
+ return NO_ERROR;
+ }
+
+ audio_config_t config = {};
+ audio_module_handle_t module = mFdp.ConsumeIntegral<int32_t>();
+ audio_io_handle_t input = mFdp.ConsumeIntegral<int32_t>();
+ config.frame_count = mFdp.ConsumeIntegral<uint32_t>();
+ String8 address = static_cast<String8>(mFdp.ConsumeRandomLengthString().c_str());
+
+ config.channel_mask = getValue(&mFdp, kChannelMasks);
+ config.format = getValue(&mFdp, kFormats);
+
+ config.offload_info = AUDIO_INFO_INITIALIZER;
+ config.offload_info.bit_rate = mFdp.ConsumeIntegral<uint32_t>();
+ config.offload_info.bit_width = mFdp.ConsumeIntegral<uint32_t>();
+ config.offload_info.content_id = mFdp.ConsumeIntegral<uint32_t>();
+ config.offload_info.channel_mask = getValue(&mFdp, kChannelMasks);
+ config.offload_info.duration_us = mFdp.ConsumeIntegral<int64_t>();
+ config.offload_info.encapsulation_mode = getValue(&mFdp, kEncapsulation);
+ config.offload_info.format = getValue(&mFdp, kFormats);
+ config.offload_info.has_video = mFdp.ConsumeBool();
+ config.offload_info.is_streaming = mFdp.ConsumeBool();
+ config.offload_info.sample_rate = getSampleRate(&mFdp);
+ config.offload_info.sync_id = mFdp.ConsumeIntegral<uint32_t>();
+ config.offload_info.stream_type = getValue(&mFdp, kStreamtypes);
+ config.offload_info.usage = getValue(&mFdp, kUsages);
+
+ config.sample_rate = getSampleRate(&mFdp);
+
+ audio_devices_t device = getValue(&mFdp, kDevices);
+ audio_source_t source = getValue(&mFdp, kInputSources);
+ audio_input_flags_t flags = getValue(&mFdp, kInputFlags);
+
+ AudioDeviceTypeAddr deviceTypeAddr(device, address.c_str());
+
+ media::OpenInputRequest request{};
+ request.module = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_module_handle_t_int32_t(module));
+ request.input = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(input));
+ request.config = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_config_t_AudioConfig(config));
+ request.device = VALUE_OR_RETURN_STATUS(legacy2aidl_AudioDeviceTypeAddress(deviceTypeAddr));
+ request.source = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_source_t_AudioSourceType(source));
+ request.flags = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_input_flags_t_int32_t_mask(flags));
+
+ media::OpenInputResponse response{};
+ status_t status = af->openInput(request, &response);
+ if (status != NO_ERROR) {
+ return NO_ERROR;
+ }
+
+ input = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_module_handle_t(response.input));
+ af->closeInput(input);
+ return NO_ERROR;
+}
+
+status_t AudioFlingerFuzzer::invokeAudioOutputDevice() {
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (!af) {
+ return NO_ERROR;
+ }
+
+ audio_config_t config = {};
+ audio_module_handle_t module = mFdp.ConsumeIntegral<int32_t>();
+ audio_io_handle_t output = mFdp.ConsumeIntegral<int32_t>();
+ config.frame_count = mFdp.ConsumeIntegral<uint32_t>();
+ String8 address = static_cast<String8>(mFdp.ConsumeRandomLengthString().c_str());
+
+ config.channel_mask = getValue(&mFdp, kChannelMasks);
+
+ config.offload_info = AUDIO_INFO_INITIALIZER;
+ config.offload_info.bit_rate = mFdp.ConsumeIntegral<uint32_t>();
+ config.offload_info.bit_width = mFdp.ConsumeIntegral<uint32_t>();
+ config.offload_info.channel_mask = getValue(&mFdp, kChannelMasks);
+ config.offload_info.content_id = mFdp.ConsumeIntegral<uint32_t>();
+ config.offload_info.duration_us = mFdp.ConsumeIntegral<int64_t>();
+ config.offload_info.encapsulation_mode = getValue(&mFdp, kEncapsulation);
+ config.offload_info.format = getValue(&mFdp, kFormats);
+ config.offload_info.has_video = mFdp.ConsumeBool();
+ config.offload_info.is_streaming = mFdp.ConsumeBool();
+ config.offload_info.sample_rate = getSampleRate(&mFdp);
+ config.offload_info.stream_type = getValue(&mFdp, kStreamtypes);
+ config.offload_info.sync_id = mFdp.ConsumeIntegral<uint32_t>();
+ config.offload_info.usage = getValue(&mFdp, kUsages);
+
+ config.format = getValue(&mFdp, kFormats);
+ config.sample_rate = getSampleRate(&mFdp);
+
+ sp<DeviceDescriptorBase> device = new DeviceDescriptorBase(getValue(&mFdp, kDevices));
+ audio_output_flags_t flags = getValue(&mFdp, kOutputFlags);
+
+ media::OpenOutputRequest request{};
+ media::OpenOutputResponse response{};
+
+ request.module = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_module_handle_t_int32_t(module));
+ request.config = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_config_t_AudioConfig(config));
+ request.device = VALUE_OR_RETURN_STATUS(legacy2aidl_DeviceDescriptorBase(device));
+ request.flags = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_output_flags_t_int32_t_mask(flags));
+
+ status_t status = af->openOutput(request, &response);
+ if (status != NO_ERROR) {
+ return NO_ERROR;
+ }
+ output = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_io_handle_t(response.output));
+
+ audio_io_handle_t output1 = mFdp.ConsumeIntegral<int32_t>();
+ af->openDuplicateOutput(output, output1);
+ af->suspendOutput(output);
+ af->restoreOutput(output);
+ af->closeOutput(output);
+ return NO_ERROR;
+}
+
+void AudioFlingerFuzzer::invokeAudioPatch() {
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (!af) {
+ return;
+ }
+ struct audio_patch patch = {};
+ audio_patch_handle_t handle = mFdp.ConsumeIntegral<int32_t>();
+
+ patch.id = mFdp.ConsumeIntegral<int32_t>();
+ patch.num_sources = mFdp.ConsumeIntegral<uint32_t>();
+ patch.num_sinks = mFdp.ConsumeIntegral<uint32_t>();
+
+ for (int i = 0; i < AUDIO_PATCH_PORTS_MAX; ++i) {
+ patch.sources[i].config_mask = mFdp.ConsumeIntegral<uint32_t>();
+ patch.sources[i].channel_mask = getValue(&mFdp, kChannelMasks);
+ patch.sources[i].format = getValue(&mFdp, kFormats);
+ patch.sources[i].gain.channel_mask = getValue(&mFdp, kChannelMasks);
+ patch.sources[i].gain.index = mFdp.ConsumeIntegral<int32_t>();
+ patch.sources[i].gain.mode = getValue(&mFdp, kGainModes);
+ patch.sources[i].gain.ramp_duration_ms = mFdp.ConsumeIntegral<uint32_t>();
+ patch.sources[i].id = static_cast<audio_format_t>(mFdp.ConsumeIntegral<int32_t>());
+ patch.sources[i].role = getValue(&mFdp, kPortRoles);
+ patch.sources[i].sample_rate = getSampleRate(&mFdp);
+ patch.sources[i].type = getValue(&mFdp, kPortTypes);
+
+ patch.sinks[i].config_mask = mFdp.ConsumeIntegral<uint32_t>();
+ patch.sinks[i].channel_mask = getValue(&mFdp, kChannelMasks);
+ patch.sinks[i].format = getValue(&mFdp, kFormats);
+ patch.sinks[i].gain.channel_mask = getValue(&mFdp, kChannelMasks);
+ patch.sinks[i].gain.index = mFdp.ConsumeIntegral<int32_t>();
+ patch.sinks[i].gain.mode = getValue(&mFdp, kGainModes);
+ patch.sinks[i].gain.ramp_duration_ms = mFdp.ConsumeIntegral<uint32_t>();
+ patch.sinks[i].id = static_cast<audio_format_t>(mFdp.ConsumeIntegral<int32_t>());
+ patch.sinks[i].role = getValue(&mFdp, kPortRoles);
+ patch.sinks[i].sample_rate = getSampleRate(&mFdp);
+ patch.sinks[i].type = getValue(&mFdp, kPortTypes);
+ }
+
+ status_t status = af->createAudioPatch(&patch, &handle);
+ if (status != NO_ERROR) {
+ return;
+ }
+
+ unsigned int num_patches = mFdp.ConsumeIntegral<uint32_t>();
+ struct audio_patch patches = {};
+ af->listAudioPatches(&num_patches, &patches);
+ af->releaseAudioPatch(handle);
+}
+
+void AudioFlingerFuzzer::process() {
+ invokeAudioEffect();
+ invokeAudioInputDevice();
+ invokeAudioOutputDevice();
+ invokeAudioPatch();
+ invokeAudioRecord();
+ invokeAudioSystem();
+ invokeAudioTrack();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 1) {
+ return 0;
+ }
+ AudioFlingerFuzzer audioFuzzer(data, size);
+ audioFuzzer.process();
+ return 0;
+}
diff --git a/media/libaudioclient/include/media/AidlConversion.h b/media/libaudioclient/include/media/AidlConversion.h
new file mode 100644
index 0000000..1dd9d60
--- /dev/null
+++ b/media/libaudioclient/include/media/AidlConversion.h
@@ -0,0 +1,412 @@
+/*
+ * 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 <limits>
+#include <type_traits>
+
+#include <system/audio.h>
+
+#include <android/media/AudioAttributesInternal.h>
+#include <android/media/AudioClient.h>
+#include <android/media/AudioConfig.h>
+#include <android/media/AudioConfigBase.h>
+#include <android/media/AudioDualMonoMode.h>
+#include <android/media/AudioEncapsulationMode.h>
+#include <android/media/AudioEncapsulationMetadataType.h>
+#include <android/media/AudioEncapsulationType.h>
+#include <android/media/AudioFlag.h>
+#include <android/media/AudioGain.h>
+#include <android/media/AudioGainMode.h>
+#include <android/media/AudioInputFlags.h>
+#include <android/media/AudioIoConfigEvent.h>
+#include <android/media/AudioIoDescriptor.h>
+#include <android/media/AudioMixLatencyClass.h>
+#include <android/media/AudioMode.h>
+#include <android/media/AudioOutputFlags.h>
+#include <android/media/AudioPlaybackRate.h>
+#include <android/media/AudioPort.h>
+#include <android/media/AudioPortConfigType.h>
+#include <android/media/AudioPortDeviceExt.h>
+#include <android/media/AudioPortExt.h>
+#include <android/media/AudioPortMixExt.h>
+#include <android/media/AudioPortSessionExt.h>
+#include <android/media/AudioProfile.h>
+#include <android/media/AudioTimestampInternal.h>
+#include <android/media/AudioUniqueIdUse.h>
+#include <android/media/EffectDescriptor.h>
+#include <android/media/ExtraAudioDescriptor.h>
+
+#include <android/media/SharedFileRegion.h>
+#include <binder/IMemory.h>
+#include <media/AidlConversionUtil.h>
+#include <media/AudioClient.h>
+#include <media/AudioCommonTypes.h>
+#include <media/AudioIoDescriptor.h>
+#include <media/AudioTimestamp.h>
+#include <system/audio_effect.h>
+
+namespace android {
+
+// maxSize is the size of the C-string buffer (including the 0-terminator), NOT the max length of
+// the string.
+status_t aidl2legacy_string(std::string_view aidl, char* dest, size_t maxSize);
+ConversionResult<std::string> legacy2aidl_string(const char* legacy, size_t maxSize);
+
+ConversionResult<audio_module_handle_t> aidl2legacy_int32_t_audio_module_handle_t(int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_audio_module_handle_t_int32_t(audio_module_handle_t legacy);
+
+ConversionResult<audio_io_handle_t> aidl2legacy_int32_t_audio_io_handle_t(int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_audio_io_handle_t_int32_t(audio_io_handle_t legacy);
+
+ConversionResult<audio_port_handle_t> aidl2legacy_int32_t_audio_port_handle_t(int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_audio_port_handle_t_int32_t(audio_port_handle_t legacy);
+
+ConversionResult<audio_patch_handle_t> aidl2legacy_int32_t_audio_patch_handle_t(int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_audio_patch_handle_t_int32_t(audio_patch_handle_t legacy);
+
+ConversionResult<audio_unique_id_t> aidl2legacy_int32_t_audio_unique_id_t(int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_audio_unique_id_t_int32_t(audio_unique_id_t legacy);
+
+ConversionResult<audio_hw_sync_t> aidl2legacy_int32_t_audio_hw_sync_t(int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_audio_hw_sync_t_int32_t(audio_hw_sync_t legacy);
+
+// The legacy enum is unnamed. Thus, we use int32_t.
+ConversionResult<int32_t> aidl2legacy_AudioPortConfigType_int32_t(
+ media::AudioPortConfigType aidl);
+// The legacy enum is unnamed. Thus, we use int32_t.
+ConversionResult<media::AudioPortConfigType> legacy2aidl_int32_t_AudioPortConfigType(
+ int32_t legacy);
+
+ConversionResult<unsigned int> aidl2legacy_int32_t_config_mask(int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_config_mask_int32_t(unsigned int legacy);
+
+ConversionResult<audio_channel_mask_t> aidl2legacy_int32_t_audio_channel_mask_t(int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_audio_channel_mask_t_int32_t(audio_channel_mask_t legacy);
+
+ConversionResult<pid_t> aidl2legacy_int32_t_pid_t(int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_pid_t_int32_t(pid_t legacy);
+
+ConversionResult<uid_t> aidl2legacy_int32_t_uid_t(int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_uid_t_int32_t(uid_t legacy);
+
+ConversionResult<String8> aidl2legacy_string_view_String8(std::string_view aidl);
+ConversionResult<std::string> legacy2aidl_String8_string(const String8& legacy);
+
+ConversionResult<String16> aidl2legacy_string_view_String16(std::string_view aidl);
+ConversionResult<std::string> legacy2aidl_String16_string(const String16& legacy);
+
+ConversionResult<std::optional<String16>>
+aidl2legacy_optional_string_view_optional_String16(std::optional<std::string_view> aidl);
+ConversionResult<std::optional<std::string_view>>
+legacy2aidl_optional_String16_optional_string(std::optional<String16> legacy);
+
+ConversionResult<audio_io_config_event> aidl2legacy_AudioIoConfigEvent_audio_io_config_event(
+ media::AudioIoConfigEvent aidl);
+ConversionResult<media::AudioIoConfigEvent> legacy2aidl_audio_io_config_event_AudioIoConfigEvent(
+ audio_io_config_event legacy);
+
+ConversionResult<audio_port_role_t> aidl2legacy_AudioPortRole_audio_port_role_t(
+ media::AudioPortRole aidl);
+ConversionResult<media::AudioPortRole> legacy2aidl_audio_port_role_t_AudioPortRole(
+ audio_port_role_t legacy);
+
+ConversionResult<audio_port_type_t> aidl2legacy_AudioPortType_audio_port_type_t(
+ media::AudioPortType aidl);
+ConversionResult<media::AudioPortType> legacy2aidl_audio_port_type_t_AudioPortType(
+ audio_port_type_t legacy);
+
+ConversionResult<audio_format_t> aidl2legacy_AudioFormat_audio_format_t(
+ media::audio::common::AudioFormat aidl);
+ConversionResult<media::audio::common::AudioFormat> legacy2aidl_audio_format_t_AudioFormat(
+ audio_format_t legacy);
+
+ConversionResult<audio_gain_mode_t>
+aidl2legacy_AudioGainMode_audio_gain_mode_t(media::AudioGainMode aidl);
+ConversionResult<media::AudioGainMode>
+legacy2aidl_audio_gain_mode_t_AudioGainMode(audio_gain_mode_t legacy);
+
+ConversionResult<audio_gain_mode_t> aidl2legacy_int32_t_audio_gain_mode_t_mask(int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_audio_gain_mode_t_int32_t_mask(audio_gain_mode_t legacy);
+
+ConversionResult<audio_devices_t> aidl2legacy_int32_t_audio_devices_t(int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_audio_devices_t_int32_t(audio_devices_t legacy);
+
+ConversionResult<audio_gain_config> aidl2legacy_AudioGainConfig_audio_gain_config(
+ const media::AudioGainConfig& aidl, media::AudioPortRole role, media::AudioPortType type);
+ConversionResult<media::AudioGainConfig> legacy2aidl_audio_gain_config_AudioGainConfig(
+ const audio_gain_config& legacy, audio_port_role_t role, audio_port_type_t type);
+
+ConversionResult<audio_input_flags_t> aidl2legacy_AudioInputFlags_audio_input_flags_t(
+ media::AudioInputFlags aidl);
+ConversionResult<media::AudioInputFlags> legacy2aidl_audio_input_flags_t_AudioInputFlags(
+ audio_input_flags_t legacy);
+
+ConversionResult<audio_output_flags_t> aidl2legacy_AudioOutputFlags_audio_output_flags_t(
+ media::AudioOutputFlags aidl);
+ConversionResult<media::AudioOutputFlags> legacy2aidl_audio_output_flags_t_AudioOutputFlags(
+ audio_output_flags_t legacy);
+
+ConversionResult<audio_input_flags_t> aidl2legacy_int32_t_audio_input_flags_t_mask(
+ int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_audio_input_flags_t_int32_t_mask(
+ audio_input_flags_t legacy);
+
+ConversionResult<audio_output_flags_t> aidl2legacy_int32_t_audio_output_flags_t_mask(
+ int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_audio_output_flags_t_int32_t_mask(
+ audio_output_flags_t legacy);
+
+ConversionResult<audio_io_flags> aidl2legacy_AudioIoFlags_audio_io_flags(
+ const media::AudioIoFlags& aidl, media::AudioPortRole role, media::AudioPortType type);
+ConversionResult<media::AudioIoFlags> legacy2aidl_audio_io_flags_AudioIoFlags(
+ const audio_io_flags& legacy, audio_port_role_t role, audio_port_type_t type);
+
+ConversionResult<audio_port_config_device_ext>
+aidl2legacy_AudioPortConfigDeviceExt_audio_port_config_device_ext(
+ const media::AudioPortConfigDeviceExt& aidl);
+ConversionResult<media::AudioPortConfigDeviceExt>
+legacy2aidl_audio_port_config_device_ext_AudioPortConfigDeviceExt(
+ const audio_port_config_device_ext& legacy);
+
+ConversionResult<audio_stream_type_t> aidl2legacy_AudioStreamType_audio_stream_type_t(
+ media::AudioStreamType aidl);
+ConversionResult<media::AudioStreamType> legacy2aidl_audio_stream_type_t_AudioStreamType(
+ audio_stream_type_t legacy);
+
+ConversionResult<audio_source_t> aidl2legacy_AudioSourceType_audio_source_t(
+ media::AudioSourceType aidl);
+ConversionResult<media::AudioSourceType> legacy2aidl_audio_source_t_AudioSourceType(
+ audio_source_t legacy);
+
+ConversionResult<audio_session_t> aidl2legacy_int32_t_audio_session_t(int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_audio_session_t_int32_t(audio_session_t legacy);
+
+ConversionResult<audio_port_config_mix_ext> aidl2legacy_AudioPortConfigMixExt(
+ const media::AudioPortConfigMixExt& aidl, media::AudioPortRole role);
+ConversionResult<media::AudioPortConfigMixExt> legacy2aidl_AudioPortConfigMixExt(
+ const audio_port_config_mix_ext& legacy, audio_port_role_t role);
+
+ConversionResult<audio_port_config_session_ext>
+aidl2legacy_AudioPortConfigSessionExt_audio_port_config_session_ext(
+ const media::AudioPortConfigSessionExt& aidl);
+ConversionResult<media::AudioPortConfigSessionExt>
+legacy2aidl_audio_port_config_session_ext_AudioPortConfigSessionExt(
+ const audio_port_config_session_ext& legacy);
+
+ConversionResult<audio_port_config> aidl2legacy_AudioPortConfig_audio_port_config(
+ const media::AudioPortConfig& aidl);
+ConversionResult<media::AudioPortConfig> legacy2aidl_audio_port_config_AudioPortConfig(
+ const audio_port_config& legacy);
+
+ConversionResult<struct audio_patch> aidl2legacy_AudioPatch_audio_patch(
+ const media::AudioPatch& aidl);
+ConversionResult<media::AudioPatch> legacy2aidl_audio_patch_AudioPatch(
+ const struct audio_patch& legacy);
+
+ConversionResult<sp<AudioIoDescriptor>> aidl2legacy_AudioIoDescriptor_AudioIoDescriptor(
+ const media::AudioIoDescriptor& aidl);
+
+ConversionResult<media::AudioIoDescriptor> legacy2aidl_AudioIoDescriptor_AudioIoDescriptor(
+ const sp<AudioIoDescriptor>& legacy);
+
+ConversionResult<AudioClient> aidl2legacy_AudioClient_AudioClient(
+ const media::AudioClient& aidl);
+ConversionResult<media::AudioClient> legacy2aidl_AudioClient_AudioClient(
+ const AudioClient& legacy);
+
+ConversionResult<audio_content_type_t>
+aidl2legacy_AudioContentType_audio_content_type_t(media::AudioContentType aidl);
+ConversionResult<media::AudioContentType>
+legacy2aidl_audio_content_type_t_AudioContentType(audio_content_type_t legacy);
+
+ConversionResult<audio_usage_t>
+aidl2legacy_AudioUsage_audio_usage_t(media::AudioUsage aidl);
+ConversionResult<media::AudioUsage>
+legacy2aidl_audio_usage_t_AudioUsage(audio_usage_t legacy);
+
+ConversionResult<audio_flags_mask_t>
+aidl2legacy_AudioFlag_audio_flags_mask_t(media::AudioFlag aidl);
+ConversionResult<media::AudioFlag>
+legacy2aidl_audio_flags_mask_t_AudioFlag(audio_flags_mask_t legacy);
+
+ConversionResult<audio_flags_mask_t>
+aidl2legacy_int32_t_audio_flags_mask_t_mask(int32_t aidl);
+ConversionResult<int32_t>
+legacy2aidl_audio_flags_mask_t_int32_t_mask(audio_flags_mask_t legacy);
+
+ConversionResult<audio_attributes_t>
+aidl2legacy_AudioAttributesInternal_audio_attributes_t(const media::AudioAttributesInternal& aidl);
+ConversionResult<media::AudioAttributesInternal>
+legacy2aidl_audio_attributes_t_AudioAttributesInternal(const audio_attributes_t& legacy);
+
+ConversionResult<audio_encapsulation_mode_t>
+aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t(media::AudioEncapsulationMode aidl);
+ConversionResult<media::AudioEncapsulationMode>
+legacy2aidl_audio_encapsulation_mode_t_AudioEncapsulationMode(audio_encapsulation_mode_t legacy);
+
+ConversionResult<audio_offload_info_t>
+aidl2legacy_AudioOffloadInfo_audio_offload_info_t(const media::AudioOffloadInfo& aidl);
+ConversionResult<media::AudioOffloadInfo>
+legacy2aidl_audio_offload_info_t_AudioOffloadInfo(const audio_offload_info_t& legacy);
+
+ConversionResult<audio_config_t>
+aidl2legacy_AudioConfig_audio_config_t(const media::AudioConfig& aidl);
+ConversionResult<media::AudioConfig>
+legacy2aidl_audio_config_t_AudioConfig(const audio_config_t& legacy);
+
+ConversionResult<audio_config_base_t>
+aidl2legacy_AudioConfigBase_audio_config_base_t(const media::AudioConfigBase& aidl);
+ConversionResult<media::AudioConfigBase>
+legacy2aidl_audio_config_base_t_AudioConfigBase(const audio_config_base_t& legacy);
+
+ConversionResult<sp<IMemory>>
+aidl2legacy_SharedFileRegion_IMemory(const media::SharedFileRegion& aidl);
+ConversionResult<media::SharedFileRegion>
+legacy2aidl_IMemory_SharedFileRegion(const sp<IMemory>& legacy);
+
+ConversionResult<sp<IMemory>>
+aidl2legacy_NullableSharedFileRegion_IMemory(const std::optional<media::SharedFileRegion>& aidl);
+ConversionResult<std::optional<media::SharedFileRegion>>
+legacy2aidl_NullableIMemory_SharedFileRegion(const sp<IMemory>& legacy);
+
+ConversionResult<AudioTimestamp>
+aidl2legacy_AudioTimestampInternal_AudioTimestamp(const media::AudioTimestampInternal& aidl);
+ConversionResult<media::AudioTimestampInternal>
+legacy2aidl_AudioTimestamp_AudioTimestampInternal(const AudioTimestamp& legacy);
+
+ConversionResult<audio_uuid_t>
+aidl2legacy_AudioUuid_audio_uuid_t(const media::AudioUuid& aidl);
+ConversionResult<media::AudioUuid>
+legacy2aidl_audio_uuid_t_AudioUuid(const audio_uuid_t& legacy);
+
+ConversionResult<effect_descriptor_t>
+aidl2legacy_EffectDescriptor_effect_descriptor_t(const media::EffectDescriptor& aidl);
+ConversionResult<media::EffectDescriptor>
+legacy2aidl_effect_descriptor_t_EffectDescriptor(const effect_descriptor_t& legacy);
+
+ConversionResult<audio_encapsulation_metadata_type_t>
+aidl2legacy_AudioEncapsulationMetadataType_audio_encapsulation_metadata_type_t(
+ media::AudioEncapsulationMetadataType aidl);
+ConversionResult<media::AudioEncapsulationMetadataType>
+legacy2aidl_audio_encapsulation_metadata_type_t_AudioEncapsulationMetadataType(
+ audio_encapsulation_metadata_type_t legacy);
+
+ConversionResult<uint32_t>
+aidl2legacy_AudioEncapsulationMode_mask(int32_t aidl);
+ConversionResult<int32_t>
+legacy2aidl_AudioEncapsulationMode_mask(uint32_t legacy);
+
+ConversionResult<uint32_t>
+aidl2legacy_AudioEncapsulationMetadataType_mask(int32_t aidl);
+ConversionResult<int32_t>
+legacy2aidl_AudioEncapsulationMetadataType_mask(uint32_t legacy);
+
+ConversionResult<audio_mix_latency_class_t>
+aidl2legacy_AudioMixLatencyClass_audio_mix_latency_class_t(
+ media::AudioMixLatencyClass aidl);
+ConversionResult<media::AudioMixLatencyClass>
+legacy2aidl_audio_mix_latency_class_t_AudioMixLatencyClass(
+ audio_mix_latency_class_t legacy);
+
+ConversionResult<audio_port_device_ext>
+aidl2legacy_AudioPortDeviceExt_audio_port_device_ext(const media::AudioPortDeviceExt& aidl);
+ConversionResult<media::AudioPortDeviceExt>
+legacy2aidl_audio_port_device_ext_AudioPortDeviceExt(const audio_port_device_ext& legacy);
+
+ConversionResult<audio_port_mix_ext>
+aidl2legacy_AudioPortMixExt_audio_port_mix_ext(const media::AudioPortMixExt& aidl);
+ConversionResult<media::AudioPortMixExt>
+legacy2aidl_audio_port_mix_ext_AudioPortMixExt(const audio_port_mix_ext& legacy);
+
+ConversionResult<audio_port_session_ext>
+aidl2legacy_AudioPortSessionExt_audio_port_session_ext(const media::AudioPortSessionExt& aidl);
+ConversionResult<media::AudioPortSessionExt>
+legacy2aidl_audio_port_session_ext_AudioPortSessionExt(const audio_port_session_ext& legacy);
+
+ConversionResult<audio_profile>
+aidl2legacy_AudioProfile_audio_profile(const media::AudioProfile& aidl);
+ConversionResult<media::AudioProfile>
+legacy2aidl_audio_profile_AudioProfile(const audio_profile& legacy);
+
+ConversionResult<audio_gain>
+aidl2legacy_AudioGain_audio_gain(const media::AudioGain& aidl);
+ConversionResult<media::AudioGain>
+legacy2aidl_audio_gain_AudioGain(const audio_gain& legacy);
+
+ConversionResult<audio_port_v7>
+aidl2legacy_AudioPort_audio_port_v7(const media::AudioPort& aidl);
+ConversionResult<media::AudioPort>
+legacy2aidl_audio_port_v7_AudioPort(const audio_port_v7& legacy);
+
+ConversionResult<audio_mode_t>
+aidl2legacy_AudioMode_audio_mode_t(media::AudioMode aidl);
+ConversionResult<media::AudioMode>
+legacy2aidl_audio_mode_t_AudioMode(audio_mode_t legacy);
+
+ConversionResult<audio_unique_id_use_t>
+aidl2legacy_AudioUniqueIdUse_audio_unique_id_use_t(media::AudioUniqueIdUse aidl);
+ConversionResult<media::AudioUniqueIdUse>
+legacy2aidl_audio_unique_id_use_t_AudioUniqueIdUse(audio_unique_id_use_t legacy);
+
+ConversionResult<volume_group_t>
+aidl2legacy_int32_t_volume_group_t(int32_t aidl);
+ConversionResult<int32_t>
+legacy2aidl_volume_group_t_int32_t(volume_group_t legacy);
+
+ConversionResult<audio_dual_mono_mode_t>
+aidl2legacy_AudioDualMonoMode_audio_dual_mono_mode_t(media::AudioDualMonoMode aidl);
+ConversionResult<media::AudioDualMonoMode>
+legacy2aidl_audio_dual_mono_mode_t_AudioDualMonoMode(audio_dual_mono_mode_t legacy);
+
+ConversionResult<audio_timestretch_fallback_mode_t>
+aidl2legacy_int32_t_audio_timestretch_fallback_mode_t(int32_t aidl);
+ConversionResult<int32_t>
+legacy2aidl_audio_timestretch_fallback_mode_t_int32_t(audio_timestretch_fallback_mode_t legacy);
+
+ConversionResult<audio_timestretch_stretch_mode_t>
+aidl2legacy_int32_t_audio_timestretch_stretch_mode_t(int32_t aidl);
+ConversionResult<int32_t>
+legacy2aidl_audio_timestretch_stretch_mode_t_int32_t(audio_timestretch_stretch_mode_t legacy);
+
+ConversionResult<audio_playback_rate_t>
+aidl2legacy_AudioPlaybackRate_audio_playback_rate_t(const media::AudioPlaybackRate& aidl);
+ConversionResult<media::AudioPlaybackRate>
+legacy2aidl_audio_playback_rate_t_AudioPlaybackRate(const audio_playback_rate_t& legacy);
+
+ConversionResult<audio_standard_t>
+aidl2legacy_AudioStandard_audio_standard_t(media::AudioStandard aidl);
+ConversionResult<media::AudioStandard>
+legacy2aidl_audio_standard_t_AudioStandard(audio_standard_t legacy);
+
+ConversionResult<audio_extra_audio_descriptor>
+aidl2legacy_ExtraAudioDescriptor_audio_extra_audio_descriptor(
+ const media::ExtraAudioDescriptor& aidl);
+ConversionResult<media::ExtraAudioDescriptor>
+legacy2aidl_audio_extra_audio_descriptor_ExtraAudioDescriptor(
+ const audio_extra_audio_descriptor& legacy);
+
+ConversionResult<audio_encapsulation_type_t>
+aidl2legacy_AudioEncapsulationType_audio_encapsulation_type_t(
+ const media::AudioEncapsulationType& aidl);
+ConversionResult<media::AudioEncapsulationType>
+legacy2aidl_audio_encapsulation_type_t_AudioEncapsulationType(
+ const audio_encapsulation_type_t & legacy);
+
+
+
+} // namespace android
diff --git a/media/libaudioclient/include/media/AidlConversionUtil.h b/media/libaudioclient/include/media/AidlConversionUtil.h
new file mode 100644
index 0000000..c1a2be3
--- /dev/null
+++ b/media/libaudioclient/include/media/AidlConversionUtil.h
@@ -0,0 +1,318 @@
+/*
+ * 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 <limits>
+#include <type_traits>
+#include <utility>
+
+#include <android-base/expected.h>
+#include <binder/Status.h>
+
+namespace android {
+
+template <typename T>
+using ConversionResult = base::expected<T, status_t>;
+
+// Convenience macros for working with ConversionResult, useful for writing converted for aggregate
+// types.
+
+#define VALUE_OR_RETURN(result) \
+ ({ \
+ auto _tmp = (result); \
+ if (!_tmp.ok()) return base::unexpected(_tmp.error()); \
+ std::move(_tmp.value()); \
+ })
+
+#define RETURN_IF_ERROR(result) \
+ if (status_t _tmp = (result); _tmp != OK) return base::unexpected(_tmp);
+
+#define VALUE_OR_RETURN_STATUS(x) \
+ ({ \
+ auto _tmp = (x); \
+ if (!_tmp.ok()) return _tmp.error(); \
+ std::move(_tmp.value()); \
+ })
+
+#define VALUE_OR_FATAL(result) \
+ ({ \
+ auto _tmp = (result); \
+ LOG_ALWAYS_FATAL_IF(!_tmp.ok(), \
+ "Function: %s Line: %d Failed result (%d)",\
+ __FUNCTION__, __LINE__, _tmp.error()); \
+ std::move(_tmp.value()); \
+ })
+
+/**
+ * A generic template to safely cast between integral types, respecting limits of the destination
+ * type.
+ */
+template<typename To, typename From>
+ConversionResult<To> convertIntegral(From from) {
+ // Special handling is required for signed / vs. unsigned comparisons, since otherwise we may
+ // have the signed converted to unsigned and produce wrong results.
+ if (std::is_signed_v<From> && !std::is_signed_v<To>) {
+ if (from < 0 || from > std::numeric_limits<To>::max()) {
+ return base::unexpected(BAD_VALUE);
+ }
+ } else if (std::is_signed_v<To> && !std::is_signed_v<From>) {
+ if (from > std::numeric_limits<To>::max()) {
+ return base::unexpected(BAD_VALUE);
+ }
+ } else {
+ if (from < std::numeric_limits<To>::min() || from > std::numeric_limits<To>::max()) {
+ return base::unexpected(BAD_VALUE);
+ }
+ }
+ return static_cast<To>(from);
+}
+
+/**
+ * A generic template to safely cast between types, that are intended to be the same size, but
+ * interpreted differently.
+ */
+template<typename To, typename From>
+ConversionResult<To> convertReinterpret(From from) {
+ static_assert(sizeof(From) == sizeof(To));
+ return static_cast<To>(from);
+}
+
+/**
+ * A generic template that helps convert containers of convertible types, using iterators.
+ */
+template<typename InputIterator, typename OutputIterator, typename Func>
+status_t convertRange(InputIterator start,
+ InputIterator end,
+ OutputIterator out,
+ const Func& itemConversion) {
+ for (InputIterator iter = start; iter != end; ++iter, ++out) {
+ *out = VALUE_OR_RETURN_STATUS(itemConversion(*iter));
+ }
+ return OK;
+}
+
+/**
+ * A generic template that helps convert containers of convertible types.
+ */
+template<typename OutputContainer, typename InputContainer, typename Func>
+ConversionResult<OutputContainer>
+convertContainer(const InputContainer& input, const Func& itemConversion) {
+ OutputContainer output;
+ auto ins = std::inserter(output, output.begin());
+ for (const auto& item : input) {
+ *ins = VALUE_OR_RETURN(itemConversion(item));
+ }
+ return output;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// The code below establishes:
+// IntegralTypeOf<T>, which works for either integral types (in which case it evaluates to T), or
+// enum types (in which case it evaluates to std::underlying_type_T<T>).
+
+template<typename T, typename = std::enable_if_t<std::is_integral_v<T> || std::is_enum_v<T>>>
+struct IntegralTypeOfStruct {
+ using Type = T;
+};
+
+template<typename T>
+struct IntegralTypeOfStruct<T, std::enable_if_t<std::is_enum_v<T>>> {
+ using Type = std::underlying_type_t<T>;
+};
+
+template<typename T>
+using IntegralTypeOf = typename IntegralTypeOfStruct<T>::Type;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Utilities for handling bitmasks.
+
+template<typename Enum>
+Enum indexToEnum_index(int index) {
+ static_assert(std::is_enum_v<Enum> || std::is_integral_v<Enum>);
+ return static_cast<Enum>(index);
+}
+
+template<typename Enum>
+Enum indexToEnum_bitmask(int index) {
+ static_assert(std::is_enum_v<Enum> || std::is_integral_v<Enum>);
+ return static_cast<Enum>(1 << index);
+}
+
+template<typename Mask, typename Enum>
+Mask enumToMask_bitmask(Enum e) {
+ static_assert(std::is_enum_v<Enum> || std::is_integral_v<Enum>);
+ static_assert(std::is_enum_v<Mask> || std::is_integral_v<Mask>);
+ return static_cast<Mask>(e);
+}
+
+template<typename Mask, typename Enum>
+Mask enumToMask_index(Enum e) {
+ static_assert(std::is_enum_v<Enum> || std::is_integral_v<Enum>);
+ static_assert(std::is_enum_v<Mask> || std::is_integral_v<Mask>);
+ return static_cast<Mask>(static_cast<std::make_unsigned_t<IntegralTypeOf<Mask>>>(1)
+ << static_cast<int>(e));
+}
+
+template<typename DestMask, typename SrcMask, typename DestEnum, typename SrcEnum>
+ConversionResult<DestMask> convertBitmask(
+ SrcMask src, const std::function<ConversionResult<DestEnum>(SrcEnum)>& enumConversion,
+ const std::function<SrcEnum(int)>& srcIndexToEnum,
+ const std::function<DestMask(DestEnum)>& destEnumToMask) {
+ using UnsignedDestMask = std::make_unsigned_t<IntegralTypeOf<DestMask>>;
+ using UnsignedSrcMask = std::make_unsigned_t<IntegralTypeOf<SrcMask>>;
+
+ UnsignedDestMask dest = static_cast<UnsignedDestMask>(0);
+ UnsignedSrcMask usrc = static_cast<UnsignedSrcMask>(src);
+
+ int srcBitIndex = 0;
+ while (usrc != 0) {
+ if (usrc & 1) {
+ SrcEnum srcEnum = srcIndexToEnum(srcBitIndex);
+ DestEnum destEnum = VALUE_OR_RETURN(enumConversion(srcEnum));
+ DestMask destMask = destEnumToMask(destEnum);
+ dest |= destMask;
+ }
+ ++srcBitIndex;
+ usrc >>= 1;
+ }
+ return static_cast<DestMask>(dest);
+}
+
+template<typename Mask, typename Enum>
+bool bitmaskIsSet(Mask mask, Enum index) {
+ return (mask & enumToMask_index<Mask, Enum>(index)) != 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Utilities for working with AIDL unions.
+// UNION_GET(obj, fieldname) returns a ConversionResult<T> containing either the strongly-typed
+// value of the respective field, or BAD_VALUE if the union is not set to the requested field.
+// UNION_SET(obj, fieldname, value) sets the requested field to the given value.
+
+template<typename T, typename T::Tag tag>
+using UnionFieldType = std::decay_t<decltype(std::declval<T>().template get<tag>())>;
+
+template<typename T, typename T::Tag tag>
+ConversionResult<UnionFieldType<T, tag>> unionGetField(const T& u) {
+ if (u.getTag() != tag) {
+ return base::unexpected(BAD_VALUE);
+ }
+ return u.template get<tag>();
+}
+
+#define UNION_GET(u, field) \
+ unionGetField<std::decay_t<decltype(u)>, std::decay_t<decltype(u)>::Tag::field>(u)
+
+#define UNION_SET(u, field, value) \
+ (u).set<std::decay_t<decltype(u)>::Tag::field>(value)
+
+namespace aidl_utils {
+
+/**
+ * Return the equivalent Android status_t from a binder exception code.
+ *
+ * Generally one should use statusTFromBinderStatus() instead.
+ *
+ * Exception codes can be generated from a remote Java service exception, translate
+ * them for use on the Native side.
+ *
+ * Note: for EX_TRANSACTION_FAILED and EX_SERVICE_SPECIFIC a more detailed error code
+ * can be found from transactionError() or serviceSpecificErrorCode().
+ */
+static inline status_t statusTFromExceptionCode(int32_t exceptionCode) {
+ using namespace ::android::binder;
+ switch (exceptionCode) {
+ case Status::EX_NONE:
+ return OK;
+ case Status::EX_SECURITY: // Java SecurityException, rethrows locally in Java
+ return PERMISSION_DENIED;
+ case Status::EX_BAD_PARCELABLE: // Java BadParcelableException, rethrows in Java
+ case Status::EX_ILLEGAL_ARGUMENT: // Java IllegalArgumentException, rethrows in Java
+ case Status::EX_NULL_POINTER: // Java NullPointerException, rethrows in Java
+ return BAD_VALUE;
+ case Status::EX_ILLEGAL_STATE: // Java IllegalStateException, rethrows in Java
+ case Status::EX_UNSUPPORTED_OPERATION: // Java UnsupportedOperationException, rethrows
+ return INVALID_OPERATION;
+ case Status::EX_HAS_REPLY_HEADER: // Native strictmode violation
+ case Status::EX_PARCELABLE: // Java bootclass loader (not standard exception), rethrows
+ case Status::EX_NETWORK_MAIN_THREAD: // Java NetworkOnMainThreadException, rethrows
+ case Status::EX_TRANSACTION_FAILED: // Native - see error code
+ case Status::EX_SERVICE_SPECIFIC: // Java ServiceSpecificException,
+ // rethrows in Java with integer error code
+ return UNKNOWN_ERROR;
+ }
+ return UNKNOWN_ERROR;
+}
+
+/**
+ * Return the equivalent Android status_t from a binder status.
+ *
+ * Used to handle errors from a AIDL method declaration
+ *
+ * [oneway] void method(type0 param0, ...)
+ *
+ * or the following (where return_type is not a status_t)
+ *
+ * return_type method(type0 param0, ...)
+ */
+static inline status_t statusTFromBinderStatus(const ::android::binder::Status &status) {
+ return status.isOk() ? OK // check OK,
+ : status.serviceSpecificErrorCode() // service-side error, not standard Java exception
+ // (fromServiceSpecificError)
+ ?: status.transactionError() // a native binder transaction error (fromStatusT)
+ ?: statusTFromExceptionCode(status.exceptionCode()); // a service-side error with a
+ // standard Java exception (fromExceptionCode)
+}
+
+/**
+ * Return a binder::Status from native service status.
+ *
+ * This is used for methods not returning an explicit status_t,
+ * where Java callers expect an exception, not an integer return value.
+ */
+static inline ::android::binder::Status binderStatusFromStatusT(
+ status_t status, const char *optionalMessage = nullptr) {
+ const char * const emptyIfNull = optionalMessage == nullptr ? "" : optionalMessage;
+ // From binder::Status instructions:
+ // Prefer a generic exception code when possible, then a service specific
+ // code, and finally a status_t for low level failures or legacy support.
+ // Exception codes and service specific errors map to nicer exceptions for
+ // Java clients.
+
+ using namespace ::android::binder;
+ switch (status) {
+ case OK:
+ return Status::ok();
+ case PERMISSION_DENIED: // throw SecurityException on Java side
+ return Status::fromExceptionCode(Status::EX_SECURITY, emptyIfNull);
+ case BAD_VALUE: // throw IllegalArgumentException on Java side
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, emptyIfNull);
+ case INVALID_OPERATION: // throw IllegalStateException on Java side
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, emptyIfNull);
+ }
+
+ // A service specific error will not show on status.transactionError() so
+ // be sure to use statusTFromBinderStatus() for reliable error handling.
+
+ // throw a ServiceSpecificException.
+ return Status::fromServiceSpecificError(status, emptyIfNull);
+}
+
+
+} // namespace aidl_utils
+
+} // namespace android
diff --git a/media/libaudioclient/include/media/AudioAttributes.h b/media/libaudioclient/include/media/AudioAttributes.h
index 001c629..24bd179 100644
--- a/media/libaudioclient/include/media/AudioAttributes.h
+++ b/media/libaudioclient/include/media/AudioAttributes.h
@@ -17,7 +17,9 @@
#pragma once
+#include <android/media/AudioAttributesEx.h>
#include <media/AudioCommonTypes.h>
+#include <media/AidlConversionUtil.h>
#include <system/audio.h>
#include <system/audio_policy.h>
#include <binder/Parcelable.h>
@@ -57,4 +59,10 @@
volume_group_t mGroupId = VOLUME_GROUP_NONE;
};
+// AIDL conversion routines.
+ConversionResult<media::AudioAttributesEx>
+legacy2aidl_AudioAttributes_AudioAttributesEx(const AudioAttributes& legacy);
+ConversionResult<AudioAttributes>
+aidl2legacy_AudioAttributesEx_AudioAttributes(const media::AudioAttributesEx& aidl);
+
} // namespace android
diff --git a/media/libaudioclient/include/media/AudioClient.h b/media/libaudioclient/include/media/AudioClient.h
index 247af9e..295fd4f 100644
--- a/media/libaudioclient/include/media/AudioClient.h
+++ b/media/libaudioclient/include/media/AudioClient.h
@@ -18,38 +18,18 @@
#ifndef ANDROID_AUDIO_CLIENT_H
#define ANDROID_AUDIO_CLIENT_H
-#include <binder/Parcel.h>
-#include <binder/Parcelable.h>
-#include <system/audio.h>
-#include <utils/String16.h>
+#include <sys/types.h>
+#include <android/media/permission/Identity.h>
namespace android {
-class AudioClient : public Parcelable {
+class AudioClient {
public:
AudioClient() :
- clientUid(-1), clientPid(-1), clientTid(-1), packageName("") {}
+ clientTid(-1) {}
- uid_t clientUid;
- pid_t clientPid;
pid_t clientTid;
- String16 packageName;
-
- status_t readFromParcel(const Parcel *parcel) override {
- clientUid = parcel->readInt32();
- clientPid = parcel->readInt32();
- clientTid = parcel->readInt32();
- packageName = parcel->readString16();
- return NO_ERROR;
- }
-
- status_t writeToParcel(Parcel *parcel) const override {
- parcel->writeInt32(clientUid);
- parcel->writeInt32(clientPid);
- parcel->writeInt32(clientTid);
- parcel->writeString16(packageName);
- return NO_ERROR;
- }
+ android::media::permission::Identity identity;
};
}; // namespace android
diff --git a/media/libaudioclient/include/media/AudioEffect.h b/media/libaudioclient/include/media/AudioEffect.h
index 3d4bb4e..974ce62 100644
--- a/media/libaudioclient/include/media/AudioEffect.h
+++ b/media/libaudioclient/include/media/AudioEffect.h
@@ -21,16 +21,16 @@
#include <sys/types.h>
#include <media/IAudioFlinger.h>
-#include <media/IAudioPolicyService.h>
-#include <media/IEffect.h>
-#include <media/IEffectClient.h>
#include <media/AudioSystem.h>
#include <system/audio_effect.h>
+#include <android/media/permission/Identity.h>
#include <utils/RefBase.h>
#include <utils/Errors.h>
#include <binder/IInterface.h>
+#include "android/media/IEffect.h"
+#include "android/media/BnEffectClient.h"
namespace android {
@@ -337,9 +337,9 @@
*
* Parameters:
*
- * opPackageName: The package name used for app op checks.
+ * client: Identity for app-op checks
*/
- explicit AudioEffect(const String16& opPackageName);
+ explicit AudioEffect(const media::permission::Identity& client);
/* Terminates the AudioEffect and unregisters it from AudioFlinger.
* The effect engine is also destroyed if this AudioEffect was the last controlling
@@ -531,7 +531,7 @@
static const uint32_t kMaxPreProcessing = 10;
protected:
- const String16 mOpPackageName; // The package name used for app op checks.
+ media::permission::Identity mClientIdentity; // Identity used for app op checks.
bool mEnabled = false; // enable state
audio_session_t mSessionId = AUDIO_SESSION_OUTPUT_MIX; // audio session ID
int32_t mPriority = 0; // priority for effect control
@@ -549,45 +549,43 @@
// IEffectClient
virtual void controlStatusChanged(bool controlGranted);
virtual void enableStatusChanged(bool enabled);
- virtual void commandExecuted(uint32_t cmdCode,
- uint32_t cmdSize,
- void *pCmdData,
- uint32_t replySize,
- void *pReplyData);
+ virtual void commandExecuted(int32_t cmdCode,
+ const std::vector<uint8_t>& cmdData,
+ const std::vector<uint8_t>& replyData);
private:
// Implements the IEffectClient interface
class EffectClient :
- public android::BnEffectClient, public android::IBinder::DeathRecipient
+ public media::BnEffectClient, public android::IBinder::DeathRecipient
{
public:
EffectClient(AudioEffect *effect) : mEffect(effect){}
// IEffectClient
- virtual void controlStatusChanged(bool controlGranted) {
+ binder::Status controlStatusChanged(bool controlGranted) override {
sp<AudioEffect> effect = mEffect.promote();
if (effect != 0) {
effect->controlStatusChanged(controlGranted);
}
+ return binder::Status::ok();
}
- virtual void enableStatusChanged(bool enabled) {
+ binder::Status enableStatusChanged(bool enabled) override {
sp<AudioEffect> effect = mEffect.promote();
if (effect != 0) {
effect->enableStatusChanged(enabled);
}
+ return binder::Status::ok();
}
- virtual void commandExecuted(uint32_t cmdCode,
- uint32_t cmdSize,
- void *pCmdData,
- uint32_t replySize,
- void *pReplyData) {
+ binder::Status commandExecuted(int32_t cmdCode,
+ const std::vector<uint8_t>& cmdData,
+ const std::vector<uint8_t>& replyData) override {
sp<AudioEffect> effect = mEffect.promote();
if (effect != 0) {
- effect->commandExecuted(
- cmdCode, cmdSize, pCmdData, replySize, pReplyData);
+ effect->commandExecuted(cmdCode, cmdData, replyData);
}
+ return binder::Status::ok();
}
// IBinder::DeathRecipient
@@ -604,12 +602,10 @@
void binderDied();
- sp<IEffect> mIEffect; // IEffect binder interface
+ sp<media::IEffect> mIEffect; // IEffect binder interface
sp<EffectClient> mIEffectClient; // IEffectClient implementation
sp<IMemory> mCblkMemory; // shared memory for deferred parameter setting
effect_param_cblk_t* mCblk = nullptr; // control block for deferred parameter setting
- pid_t mClientPid = (pid_t)-1;
- uid_t mClientUid = (uid_t)-1;
};
diff --git a/media/libaudioclient/include/media/AudioPolicy.h b/media/libaudioclient/include/media/AudioPolicy.h
index 00fe278..08b3da1 100644
--- a/media/libaudioclient/include/media/AudioPolicy.h
+++ b/media/libaudioclient/include/media/AudioPolicy.h
@@ -120,7 +120,7 @@
String8 mDeviceAddress;
uint32_t mCbFlags; // flags indicating which callbacks to use, see kCbFlag*
/** Ignore the AUDIO_FLAG_NO_MEDIA_PROJECTION */
- bool mAllowPrivilegedPlaybackCapture = false;
+ bool mAllowPrivilegedMediaPlaybackCapture = false;
/** Indicates if the caller can capture voice communication output */
bool mVoiceCommunicationCaptureAllowed = false;
};
diff --git a/media/libaudioclient/include/media/AudioProductStrategy.h b/media/libaudioclient/include/media/AudioProductStrategy.h
index 7441095..b55b506 100644
--- a/media/libaudioclient/include/media/AudioProductStrategy.h
+++ b/media/libaudioclient/include/media/AudioProductStrategy.h
@@ -17,6 +17,8 @@
#pragma once
+#include <android/media/AudioProductStrategy.h>
+#include <media/AidlConversionUtil.h>
#include <media/AudioCommonTypes.h>
#include <media/AudioAttributes.h>
#include <system/audio.h>
@@ -62,5 +64,11 @@
using AudioProductStrategyVector = std::vector<AudioProductStrategy>;
+// AIDL conversion routines.
+ConversionResult<media::AudioProductStrategy>
+legacy2aidl_AudioProductStrategy(const AudioProductStrategy& legacy);
+ConversionResult<AudioProductStrategy>
+aidl2legacy_AudioProductStrategy(const media::AudioProductStrategy& aidl);
+
} // namespace android
diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h
index 2f66658..9965e25 100644
--- a/media/libaudioclient/include/media/AudioRecord.h
+++ b/media/libaudioclient/include/media/AudioRecord.h
@@ -32,6 +32,7 @@
#include <utils/threads.h>
#include "android/media/IAudioRecord.h"
+#include <android/media/permission/Identity.h>
namespace android {
@@ -148,9 +149,9 @@
*
* Parameters:
*
- * opPackageName: The package name used for app ops.
+ * clientIdentity: The identity of the owner of the record
*/
- AudioRecord(const String16& opPackageName);
+ AudioRecord(const media::permission::Identity& clientIdentity);
/* Creates an AudioRecord object and registers it with AudioFlinger.
* Once created, the track needs to be started before it can be used.
@@ -163,7 +164,7 @@
* format: Audio format (e.g AUDIO_FORMAT_PCM_16_BIT for signed
* 16 bits per sample).
* channelMask: Channel mask, such that audio_is_input_channel(channelMask) is true.
- * opPackageName: The package name used for app ops.
+ * client: The identity of the owner of the record
* frameCount: Minimum size of track PCM buffer in frames. This defines the
* application's contribution to the
* latency of the track. The actual size selected by the AudioRecord could
@@ -186,7 +187,7 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- const String16& opPackageName,
+ const media::permission::Identity& clientIdentity,
size_t frameCount = 0,
callback_t cbf = NULL,
void* user = NULL,
@@ -194,8 +195,6 @@
audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
transfer_type transferType = TRANSFER_DEFAULT,
audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE,
- uid_t uid = AUDIO_UID_INVALID,
- pid_t pid = -1,
const audio_attributes_t* pAttributes = NULL,
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
audio_microphone_direction_t
@@ -242,7 +241,8 @@
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
audio_microphone_direction_t
selectedMicDirection = MIC_DIRECTION_UNSPECIFIED,
- float selectedMicFieldDimension = MIC_FIELD_DIMENSION_DEFAULT);
+ float selectedMicFieldDimension = MIC_FIELD_DIMENSION_DEFAULT,
+ int32_t maxSharedAudioHistoryMs = 0);
/* Result of constructing the AudioRecord. This must be checked for successful initialization
* before using any AudioRecord API (except for set()), because using
@@ -304,6 +304,19 @@
void stop();
bool stopped() const;
+ /* Calls stop() and then wait for all of the callbacks to return.
+ * It is safe to call this if stop() or pause() has already been called.
+ *
+ * This function is called from the destructor. But since AudioRecord
+ * is ref counted, the destructor may be called later than desired.
+ * This can be called explicitly as part of closing an AudioRecord
+ * if you want to be certain that callbacks have completely finished.
+ *
+ * This is not thread safe and should only be called from one thread,
+ * ideally as the AudioRecord is being closed.
+ */
+ void stopAndJoinCallbacks();
+
/* Return the sink sample rate for this record track in Hz.
* If specified as zero in constructor or set(), this will be the source sample rate.
* Unlike AudioTrack, the sample rate is const after initialization, so doesn't need a lock.
@@ -578,6 +591,16 @@
*/
audio_port_handle_t getPortId() const { return mPortId; };
+ /* Sets the LogSessionId field which is used for metrics association of
+ * this object with other objects. A nullptr or empty string clears
+ * the logSessionId.
+ */
+ void setLogSessionId(const char *logSessionId);
+
+
+ status_t shareAudioHistory(const std::string& sharedPackageName,
+ int64_t sharedStartMs);
+
/*
* Dumps the state of an audio record.
*/
@@ -632,7 +655,7 @@
// caller must hold lock on mLock for all _l methods
- status_t createRecord_l(const Modulo<uint32_t> &epoch, const String16& opPackageName);
+ status_t createRecord_l(const Modulo<uint32_t> &epoch);
// FIXME enum is faster than strcmp() for parameter 'from'
status_t restoreRecord_l(const char *from);
@@ -673,7 +696,7 @@
status_t mStatus;
- String16 mOpPackageName; // The package name used for app ops.
+ media::permission::Identity mClientIdentity; // The identity of the owner of this record
size_t mFrameCount; // corresponds to current IAudioRecord, value is
// reported back by AudioFlinger to the client
@@ -701,6 +724,14 @@
audio_session_t mSessionId;
audio_port_handle_t mPortId; // Id from Audio Policy Manager
+
+ /**
+ * mLogSessionId is a string identifying this AudioRecord for the metrics service.
+ * It may be unique or shared with other objects. An empty string means the
+ * logSessionId is not set.
+ */
+ std::string mLogSessionId{};
+
transfer_type mTransfer;
// Next 5 fields may be changed if IAudioRecord is re-created, but always != 0
@@ -740,8 +771,6 @@
sp<DeathNotifier> mDeathNotifier;
uint32_t mSequence; // incremented for each new IAudioRecord attempt
- uid_t mClientUid;
- pid_t mClientPid;
audio_attributes_t mAttributes;
// For Device Selection API
@@ -755,6 +784,10 @@
audio_microphone_direction_t mSelectedMicDirection;
float mSelectedMicFieldDimension;
+ int32_t mMaxSharedAudioHistoryMs = 0;
+ std::string mSharedAudioPackageName = {};
+ int64_t mSharedAudioStartMs = 0;
+
private:
class MediaMetrics {
public:
diff --git a/media/libaudioclient/include/media/AudioSanitizer.h b/media/libaudioclient/include/media/AudioSanitizer.h
deleted file mode 100644
index 1475c7b..0000000
--- a/media/libaudioclient/include/media/AudioSanitizer.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_AUDIO_SANITIZER_H_
-#define ANDROID_AUDIO_SANITIZER_H_
-
-#include <system/audio.h>
-#include <system/audio_effect.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
-
-namespace android {
-
-class AudioSanitizer {
-public:
- static status_t sanitizeAudioAttributes(
- audio_attributes_t *attr, const char *bugNumber = nullptr);
-
- static status_t sanitizeEffectDescriptor(
- effect_descriptor_t *desc, const char *bugNumber = nullptr);
-
- static status_t sanitizeAudioPortConfig(
- struct audio_port_config *config, const char *bugNumber = nullptr);
-
- static status_t sanitizeAudioPort(
- struct audio_port *port, const char *bugNumber = nullptr);
-
- static status_t sanitizeAudioPatch(
- struct audio_patch *patch, const char *bugNumber = nullptr);
-};
-
-}; // namespace android
-
-#endif /*ANDROID_AUDIO_SANITIZER_H_*/
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 848743a..4c99dbd 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -19,13 +19,16 @@
#include <sys/types.h>
+#include <android/media/AudioVibratorInfo.h>
+#include <android/media/BnAudioFlingerClient.h>
+#include <android/media/BnAudioPolicyServiceClient.h>
+#include <android/media/permission/Identity.h>
+#include <media/AidlConversionUtil.h>
#include <media/AudioDeviceTypeAddr.h>
#include <media/AudioPolicy.h>
#include <media/AudioProductStrategy.h>
#include <media/AudioVolumeGroup.h>
#include <media/AudioIoDescriptor.h>
-#include <media/IAudioFlingerClient.h>
-#include <media/IAudioPolicyServiceClient.h>
#include <media/MicrophoneInfo.h>
#include <set>
#include <system/audio.h>
@@ -37,6 +40,23 @@
namespace android {
+struct record_client_info {
+ audio_unique_id_t riid;
+ uid_t uid;
+ audio_session_t session;
+ audio_source_t source;
+ audio_port_handle_t port_id;
+ bool silenced;
+};
+
+typedef struct record_client_info record_client_info_t;
+
+// AIDL conversion functions.
+ConversionResult<record_client_info_t>
+aidl2legacy_RecordClientInfo_record_client_info_t(const media::RecordClientInfo& aidl);
+ConversionResult<media::RecordClientInfo>
+legacy2aidl_record_client_info_t_RecordClientInfo(const record_client_info_t& legacy);
+
typedef void (*audio_error_callback)(status_t err);
typedef void (*dynamic_policy_callback)(int event, String8 regId, int val);
typedef void (*record_config_callback)(int event,
@@ -47,11 +67,15 @@
std::vector<effect_descriptor_t> effects,
audio_patch_handle_t patchHandle,
audio_source_t source);
+typedef void (*routing_callback)();
class IAudioFlinger;
-class IAudioPolicyService;
class String8;
+namespace media {
+class IAudioPolicyService;
+}
+
class AudioSystem
{
public:
@@ -120,6 +144,7 @@
static void setDynPolicyCallback(dynamic_policy_callback cb);
static void setRecordConfigCallback(record_config_callback);
+ static void setRoutingCallback(routing_callback cb);
// helper function to obtain AudioFlinger service handle
static const sp<IAudioFlinger> get_audio_flinger();
@@ -239,8 +264,7 @@
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
- pid_t pid,
- uid_t uid,
+ const media::permission::Identity& identity,
const audio_config_t *config,
audio_output_flags_t flags,
audio_port_handle_t *selectedDeviceId,
@@ -256,9 +280,7 @@
audio_io_handle_t *input,
audio_unique_id_t riid,
audio_session_t session,
- pid_t pid,
- uid_t uid,
- const String16& opPackageName,
+ const media::permission::Identity& identity,
const audio_config_base_t *config,
audio_input_flags_t flags,
audio_port_handle_t *selectedDeviceId,
@@ -288,7 +310,7 @@
static status_t getMinVolumeIndexForAttributes(const audio_attributes_t &attr, int &index);
- static uint32_t getStrategyForStream(audio_stream_type_t stream);
+ static product_strategy_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);
@@ -296,7 +318,7 @@
static audio_io_handle_t getOutputForEffect(const effect_descriptor_t *desc);
static status_t registerEffect(const effect_descriptor_t *desc,
audio_io_handle_t io,
- uint32_t strategy,
+ product_strategy_t strategy,
audio_session_t session,
int id);
static status_t unregisterEffect(int id);
@@ -307,7 +329,7 @@
// and output configuration cache (gOutputs)
static void clearAudioConfigCache();
- static const sp<IAudioPolicyService> get_audio_policy_service();
+ static const sp<media::IAudioPolicyService> get_audio_policy_service();
// helpers for android.media.AudioManager.getProperty(), see description there for meaning
static uint32_t getPrimaryOutputSamplingRate();
@@ -317,11 +339,12 @@
static status_t setSupportedSystemUsages(const std::vector<audio_usage_t>& systemUsages);
- static status_t setAllowedCapturePolicy(uid_t uid, audio_flags_mask_t flags);
+ static status_t setAllowedCapturePolicy(uid_t uid, audio_flags_mask_t capturePolicy);
- // Check if hw offload is possible for given format, stream type, sample rate,
- // bit rate, duration, video and streaming or offload property is enabled
- static bool isOffloadSupported(const audio_offload_info_t& info);
+ // Indicate if hw offload is possible for given format, stream type, sample rate,
+ // bit rate, duration, video and streaming or offload property is enabled and when possible
+ // if gapless transitions are supported.
+ static audio_offload_mode_t getOffloadSupport(const audio_offload_info_t& info);
// check presence of audio flinger service.
// returns NO_ERROR if binding to service succeeds, DEAD_OBJECT otherwise
@@ -331,11 +354,11 @@
static status_t listAudioPorts(audio_port_role_t role,
audio_port_type_t type,
unsigned int *num_ports,
- struct audio_port *ports,
+ struct audio_port_v7 *ports,
unsigned int *generation);
/* Get attributes for a given audio port */
- static status_t getAudioPort(struct audio_port *port);
+ static status_t getAudioPort(struct audio_port_v7 *port);
/* Create an audio patch between several source and sink ports */
static status_t createAudioPatch(const struct audio_patch *patch,
@@ -393,8 +416,9 @@
// populated. The actual number of surround formats should be returned at numSurroundFormats.
static status_t getSurroundFormats(unsigned int *numSurroundFormats,
audio_format_t *surroundFormats,
- bool *surroundFormatsEnabled,
- bool reported);
+ bool *surroundFormatsEnabled);
+ static status_t getReportedSurroundFormats(unsigned int *numSurroundFormats,
+ audio_format_t *surroundFormats);
static status_t setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled);
static status_t setAssistantUid(uid_t uid);
@@ -404,16 +428,17 @@
static bool isHapticPlaybackSupported();
static status_t listAudioProductStrategies(AudioProductStrategyVector &strategies);
- static status_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa,
- product_strategy_t &productStrategy);
+ static status_t getProductStrategyFromAudioAttributes(
+ const AudioAttributes &aa, product_strategy_t &productStrategy,
+ bool fallbackOnDefault = true);
static audio_attributes_t streamTypeToAttributes(audio_stream_type_t stream);
static audio_stream_type_t attributesToStreamType(const audio_attributes_t &attr);
static status_t listAudioVolumeGroups(AudioVolumeGroupVector &groups);
- static status_t getVolumeGroupFromAudioAttributes(const AudioAttributes &aa,
- volume_group_t &volumeGroup);
+ static status_t getVolumeGroupFromAudioAttributes(
+ const AudioAttributes &aa, volume_group_t &volumeGroup, bool fallbackOnDefault = true);
static status_t setRttEnabled(bool enabled);
@@ -529,9 +554,11 @@
static audio_port_handle_t getDeviceIdForIo(audio_io_handle_t audioIo);
+ static status_t setVibratorInfos(const std::vector<media::AudioVibratorInfo>& vibratorInfos);
+
private:
- class AudioFlingerClient: public IBinder::DeathRecipient, public BnAudioFlingerClient
+ class AudioFlingerClient: public IBinder::DeathRecipient, public media::BnAudioFlingerClient
{
public:
AudioFlingerClient() :
@@ -551,9 +578,9 @@
// indicate a change in the configuration of an output or input: keeps the cached
// values for output/input parameters up-to-date in client process
- virtual void ioConfigChanged(audio_io_config_event event,
- const sp<AudioIoDescriptor>& ioDesc);
-
+ binder::Status ioConfigChanged(
+ media::AudioIoConfigEvent event,
+ const media::AudioIoDescriptor& ioDesc) override;
status_t addAudioDeviceCallback(const wp<AudioDeviceCallback>& callback,
audio_io_handle_t audioIo,
@@ -579,7 +606,7 @@
};
class AudioPolicyServiceClient: public IBinder::DeathRecipient,
- public BnAudioPolicyServiceClient
+ public media::BnAudioPolicyServiceClient
{
public:
AudioPolicyServiceClient() {
@@ -597,18 +624,21 @@
virtual void binderDied(const wp<IBinder>& who);
// IAudioPolicyServiceClient
- virtual void onAudioPortListUpdate();
- virtual void onAudioPatchListUpdate();
- virtual void onAudioVolumeGroupChanged(volume_group_t group, int flags);
- virtual void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state);
- virtual void onRecordingConfigurationUpdate(int event,
- const record_client_info_t *clientInfo,
- const audio_config_base_t *clientConfig,
- std::vector<effect_descriptor_t> clientEffects,
- const audio_config_base_t *deviceConfig,
- std::vector<effect_descriptor_t> effects,
- audio_patch_handle_t patchHandle,
- audio_source_t source);
+ binder::Status onAudioVolumeGroupChanged(int32_t group, int32_t flags) override;
+ binder::Status onAudioPortListUpdate() override;
+ binder::Status onAudioPatchListUpdate() override;
+ binder::Status onDynamicPolicyMixStateUpdate(const std::string& regId,
+ int32_t state) override;
+ binder::Status onRecordingConfigurationUpdate(
+ int32_t event,
+ const media::RecordClientInfo& clientInfo,
+ const media::AudioConfigBase& clientConfig,
+ const std::vector<media::EffectDescriptor>& clientEffects,
+ const media::AudioConfigBase& deviceConfig,
+ const std::vector<media::EffectDescriptor>& effects,
+ int32_t patchHandle,
+ media::AudioSourceType source) override;
+ binder::Status onRoutingUpdated();
private:
Mutex mLock;
@@ -635,6 +665,7 @@
static std::set<audio_error_callback> gAudioErrorCallbacks;
static dynamic_policy_callback gDynPolicyCallback;
static record_config_callback gRecordConfigCallback;
+ static routing_callback gRoutingCallback;
static size_t gInBuffSize;
// previous parameters for recording buffer size queries
@@ -642,7 +673,7 @@
static audio_format_t gPrevInFormat;
static audio_channel_mask_t gPrevInChannelMask;
- static sp<IAudioPolicyService> gAudioPolicyService;
+ static sp<media::IAudioPolicyService> gAudioPolicyService;
};
}; // namespace android
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index 4afa9c9..c293343 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -17,18 +17,21 @@
#ifndef ANDROID_AUDIOTRACK_H
#define ANDROID_AUDIOTRACK_H
+#include <binder/IMemory.h>
#include <cutils/sched_policy.h>
#include <media/AudioSystem.h>
#include <media/AudioTimestamp.h>
-#include <media/IAudioTrack.h>
#include <media/AudioResamplerPublic.h>
#include <media/MediaMetricsItem.h>
#include <media/Modulo.h>
+#include <media/VolumeShaper.h>
#include <utils/threads.h>
+#include <android/media/permission/Identity.h>
#include <string>
#include "android/media/BnAudioTrackCallback.h"
+#include "android/media/IAudioTrack.h"
#include "android/media/IAudioTrackCallback.h"
namespace android {
@@ -179,7 +182,7 @@
*/
AudioTrack();
- AudioTrack(const std::string& opPackageName);
+ AudioTrack(const media::permission::Identity& identity);
/* Creates an AudioTrack object and registers it with AudioFlinger.
* Once created, the track needs to be started before it can be used.
@@ -227,10 +230,9 @@
* transferType: How data is transferred to AudioTrack.
* offloadInfo: If not NULL, provides offload parameters for
* AudioSystem::getOutputForAttr().
- * uid: User ID of the app which initially requested this AudioTrack
- * for power management tracking, or -1 for current user ID.
- * pid: Process ID of the app which initially requested this AudioTrack
- * for power management tracking, or -1 for current process ID.
+ * identity: The identity of the app which initiallly requested this AudioTrack.
+ * Includes the UID and PID for power management tracking, or -1 for
+ * current user/process ID, plus the package name.
* pAttributes: If not NULL, supersedes streamType for use case selection.
* doNotReconnect: If set to true, AudioTrack won't automatically recreate the IAudioTrack
binder to AudioFlinger.
@@ -257,13 +259,12 @@
audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
transfer_type transferType = TRANSFER_DEFAULT,
const audio_offload_info_t *offloadInfo = NULL,
- uid_t uid = AUDIO_UID_INVALID,
- pid_t pid = -1,
+ const media::permission::Identity& identity =
+ media::permission::Identity(),
const audio_attributes_t* pAttributes = NULL,
bool doNotReconnect = false,
float maxRequiredSpeed = 1.0f,
- audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
- const std::string& opPackageName = "");
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE);
/* Creates an audio track and registers it with AudioFlinger.
* With this constructor, the track is configured for static buffer mode.
@@ -289,12 +290,11 @@
audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
transfer_type transferType = TRANSFER_DEFAULT,
const audio_offload_info_t *offloadInfo = NULL,
- uid_t uid = AUDIO_UID_INVALID,
- pid_t pid = -1,
+ const media::permission::Identity& identity =
+ media::permission::Identity(),
const audio_attributes_t* pAttributes = NULL,
bool doNotReconnect = false,
- float maxRequiredSpeed = 1.0f,
- const std::string& opPackageName = "");
+ float maxRequiredSpeed = 1.0f);
/* Terminates the AudioTrack and unregisters it from AudioFlinger.
* Also destroys all resources associated with the AudioTrack.
@@ -338,8 +338,8 @@
audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
transfer_type transferType = TRANSFER_DEFAULT,
const audio_offload_info_t *offloadInfo = NULL,
- uid_t uid = AUDIO_UID_INVALID,
- pid_t pid = -1,
+ const media::permission::Identity& identity =
+ media::permission::Identity(),
const audio_attributes_t* pAttributes = NULL,
bool doNotReconnect = false,
float maxRequiredSpeed = 1.0f,
@@ -479,6 +479,19 @@
void stop();
bool stopped() const;
+ /* Call stop() and then wait for all of the callbacks to return.
+ * It is safe to call this if stop() or pause() has already been called.
+ *
+ * This function is called from the destructor. But since AudioTrack
+ * is ref counted, the destructor may be called later than desired.
+ * This can be called explicitly as part of closing an AudioTrack
+ * if you want to be certain that callbacks have completely finished.
+ *
+ * This is not thread safe and should only be called from one thread,
+ * ideally as the AudioTrack is being closed.
+ */
+ void stopAndJoinCallbacks();
+
/* Flush a stopped or paused track. All previously buffered data is discarded immediately.
* This has the effect of draining the buffers without mixing or output.
* Flush is intended for streaming mode, for example before switching to non-contiguous content.
@@ -673,9 +686,7 @@
* handle on audio hardware output, or AUDIO_IO_HANDLE_NONE if the
* track needed to be re-created but that failed
*/
-private:
audio_io_handle_t getOutput() const;
-public:
/* Selects the audio device to use for output of this AudioTrack. A value of
* AUDIO_PORT_HANDLE_NONE indicates default (AudioPolicyManager) routing.
@@ -999,6 +1010,23 @@
*/
audio_port_handle_t getPortId() const { return mPortId; };
+ /* Sets the LogSessionId field which is used for metrics association of
+ * this object with other objects. A nullptr or empty string clears
+ * the logSessionId.
+ */
+ void setLogSessionId(const char *logSessionId);
+
+ /* Sets the playerIId field to associate the AudioTrack with an interface managed by
+ * AudioService.
+ *
+ * If this value is not set, then the playerIId is reported as -1
+ * (not associated with an AudioService player interface).
+ *
+ * For metrics purposes, we keep the playerIId association in the native
+ * client AudioTrack to improve the robustness under track restoration.
+ */
+ void setPlayerIId(int playerIId);
+
void setAudioTrackCallback(const sp<media::IAudioTrackCallback>& callback) {
mAudioTrackCallback->setAudioTrackCallback(callback);
}
@@ -1102,7 +1130,7 @@
status_t setAudioDescriptionMixLevel_l(float leveldB);
// Next 4 fields may be changed if IAudioTrack is re-created, but always != 0
- sp<IAudioTrack> mAudioTrack;
+ sp<media::IAudioTrack> mAudioTrack;
sp<IMemory> mCblkMemory;
audio_track_cblk_t* mCblk; // re-load after mLock.unlock()
audio_io_handle_t mOutput = AUDIO_IO_HANDLE_NONE; // from AudioSystem::getOutputForAttr()
@@ -1268,6 +1296,19 @@
int mAuxEffectId;
audio_port_handle_t mPortId; // Id from Audio Policy Manager
+ /**
+ * mPlayerIId is the player id of the AudioTrack used by AudioManager.
+ * For an AudioTrack created by the Java interface, this is generally set once.
+ */
+ int mPlayerIId = -1; // AudioManager.h PLAYER_PIID_INVALID
+
+ /**
+ * mLogSessionId is a string identifying this AudioTrack for the metrics service.
+ * It may be unique or shared with other objects. An empty string means the
+ * logSessionId is not set.
+ */
+ std::string mLogSessionId{};
+
mutable Mutex mLock;
int mPreviousPriority; // before start()
@@ -1294,8 +1335,6 @@
sp<media::VolumeHandler> mVolumeHandler;
- const std::string mOpPackageName;
-
private:
class DeathNotifier : public IBinder::DeathRecipient {
public:
@@ -1308,8 +1347,7 @@
sp<DeathNotifier> mDeathNotifier;
uint32_t mSequence; // incremented for each new IAudioTrack attempt
- uid_t mClientUid;
- pid_t mClientPid;
+ media::permission::Identity mClientIdentity;
wp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
diff --git a/media/libaudioclient/include/media/AudioVolumeGroup.h b/media/libaudioclient/include/media/AudioVolumeGroup.h
index 9a6ea07..946d58a 100644
--- a/media/libaudioclient/include/media/AudioVolumeGroup.h
+++ b/media/libaudioclient/include/media/AudioVolumeGroup.h
@@ -17,6 +17,8 @@
#pragma once
+#include <android/media/AudioVolumeGroup.h>
+#include <media/AidlConversionUtil.h>
#include <media/AudioProductStrategy.h>
#include <system/audio.h>
#include <system/audio_policy.h>
@@ -51,4 +53,10 @@
using AudioVolumeGroupVector = std::vector<AudioVolumeGroup>;
+// AIDL conversion routines.
+ConversionResult<media::AudioVolumeGroup>
+legacy2aidl_AudioVolumeGroup(const AudioVolumeGroup& legacy);
+ConversionResult<AudioVolumeGroup>
+aidl2legacy_AudioVolumeGroup(const media::AudioVolumeGroup& aidl);
+
} // namespace android
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index bcc11f4..3a04569 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -24,108 +24,54 @@
#include <utils/RefBase.h>
#include <utils/Errors.h>
#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-#include <binder/Parcelable.h>
+#include <media/AidlConversion.h>
#include <media/AudioClient.h>
#include <media/DeviceDescriptorBase.h>
-#include <media/IAudioTrack.h>
-#include <media/IAudioFlingerClient.h>
#include <system/audio.h>
#include <system/audio_effect.h>
#include <system/audio_policy.h>
-#include <media/IEffect.h>
-#include <media/IEffectClient.h>
#include <utils/String8.h>
#include <media/MicrophoneInfo.h>
#include <string>
#include <vector>
+#include <android/media/AudioVibratorInfo.h>
+#include <android/media/BnAudioFlingerService.h>
+#include <android/media/BpAudioFlingerService.h>
+#include <android/media/permission/Identity.h>
+#include "android/media/CreateEffectRequest.h"
+#include "android/media/CreateEffectResponse.h"
+#include "android/media/CreateRecordRequest.h"
+#include "android/media/CreateRecordResponse.h"
+#include "android/media/CreateTrackRequest.h"
+#include "android/media/CreateTrackResponse.h"
#include "android/media/IAudioRecord.h"
+#include "android/media/IAudioFlingerClient.h"
+#include "android/media/IAudioTrack.h"
#include "android/media/IAudioTrackCallback.h"
+#include "android/media/IEffect.h"
+#include "android/media/IEffectClient.h"
+#include "android/media/OpenInputRequest.h"
+#include "android/media/OpenInputResponse.h"
+#include "android/media/OpenOutputRequest.h"
+#include "android/media/OpenOutputResponse.h"
namespace android {
// ----------------------------------------------------------------------------
-class IAudioFlinger : public IInterface
-{
+class IAudioFlinger : public RefBase {
public:
- DECLARE_META_INTERFACE(AudioFlinger);
+ static constexpr char DEFAULT_SERVICE_NAME[] = "media.audio_flinger";
+
+ virtual ~IAudioFlinger() = default;
/* CreateTrackInput contains all input arguments sent by AudioTrack to AudioFlinger
* when calling createTrack() including arguments that will be updated by AudioFlinger
* and returned in CreateTrackOutput object
*/
- class CreateTrackInput : public Parcelable {
+ class CreateTrackInput {
public:
- status_t readFromParcel(const Parcel *parcel) override {
- /* input arguments*/
- memset(&attr, 0, sizeof(audio_attributes_t));
- if (parcel->read(&attr, sizeof(audio_attributes_t)) != NO_ERROR) {
- return DEAD_OBJECT;
- }
- attr.tags[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE -1] = '\0';
- memset(&config, 0, sizeof(audio_config_t));
- if (parcel->read(&config, sizeof(audio_config_t)) != NO_ERROR) {
- return DEAD_OBJECT;
- }
- if (clientInfo.readFromParcel(parcel) != NO_ERROR) {
- 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->unsecurePointer() == NULL) {
- return BAD_VALUE;
- }
- }
- notificationsPerBuffer = parcel->readInt32();
- speed = parcel->readFloat();
- audioTrackCallback = interface_cast<media::IAudioTrackCallback>(
- parcel->readStrongBinder());
- const char* opPackageNamePtr = parcel->readCString();
- if (opPackageNamePtr == nullptr) {
- return FAILED_TRANSACTION;
- }
- opPackageName = opPackageNamePtr;
-
- /* input/output arguments*/
- (void)parcel->read(&flags, sizeof(audio_output_flags_t));
- frameCount = parcel->readInt64();
- notificationFrameCount = parcel->readInt64();
- (void)parcel->read(&selectedDeviceId, sizeof(audio_port_handle_t));
- (void)parcel->read(&sessionId, sizeof(audio_session_t));
- return NO_ERROR;
- }
-
- status_t writeToParcel(Parcel *parcel) const override {
- /* input arguments*/
- (void)parcel->write(&attr, sizeof(audio_attributes_t));
- (void)parcel->write(&config, sizeof(audio_config_t));
- (void)clientInfo.writeToParcel(parcel);
- if (sharedBuffer != 0) {
- (void)parcel->writeInt32(1);
- (void)parcel->writeStrongBinder(IInterface::asBinder(sharedBuffer));
- } else {
- (void)parcel->writeInt32(0);
- }
- (void)parcel->writeInt32(notificationsPerBuffer);
- (void)parcel->writeFloat(speed);
- (void)parcel->writeStrongBinder(IInterface::asBinder(audioTrackCallback));
- (void)parcel->writeCString(opPackageName.c_str());
-
- /* input/output arguments*/
- (void)parcel->write(&flags, sizeof(audio_output_flags_t));
- (void)parcel->writeInt64(frameCount);
- (void)parcel->writeInt64(notificationFrameCount);
- (void)parcel->write(&selectedDeviceId, sizeof(audio_port_handle_t));
- (void)parcel->write(&sessionId, sizeof(audio_session_t));
- return NO_ERROR;
- }
-
/* input */
audio_attributes_t attr;
audio_config_t config;
@@ -134,7 +80,6 @@
uint32_t notificationsPerBuffer;
float speed;
sp<media::IAudioTrackCallback> audioTrackCallback;
- std::string opPackageName;
/* input/output */
audio_output_flags_t flags;
@@ -142,50 +87,17 @@
size_t notificationFrameCount;
audio_port_handle_t selectedDeviceId;
audio_session_t sessionId;
+
+ ConversionResult<media::CreateTrackRequest> toAidl() const;
+ static ConversionResult<CreateTrackInput> fromAidl(const media::CreateTrackRequest& aidl);
};
/* CreateTrackOutput contains all output arguments returned by AudioFlinger to AudioTrack
* when calling createTrack() including arguments that were passed as I/O for update by
* CreateTrackInput.
*/
- class CreateTrackOutput : public Parcelable {
+ class CreateTrackOutput {
public:
- status_t readFromParcel(const Parcel *parcel) override {
- /* input/output arguments*/
- (void)parcel->read(&flags, sizeof(audio_output_flags_t));
- frameCount = parcel->readInt64();
- notificationFrameCount = parcel->readInt64();
- (void)parcel->read(&selectedDeviceId, sizeof(audio_port_handle_t));
- (void)parcel->read(&sessionId, sizeof(audio_session_t));
-
- /* output arguments*/
- sampleRate = parcel->readUint32();
- afFrameCount = parcel->readInt64();
- afSampleRate = parcel->readInt64();
- afLatencyMs = parcel->readInt32();
- (void)parcel->read(&outputId, sizeof(audio_io_handle_t));
- (void)parcel->read(&portId, sizeof(audio_port_handle_t));
- return NO_ERROR;
- }
-
- status_t writeToParcel(Parcel *parcel) const override {
- /* input/output arguments*/
- (void)parcel->write(&flags, sizeof(audio_output_flags_t));
- (void)parcel->writeInt64(frameCount);
- (void)parcel->writeInt64(notificationFrameCount);
- (void)parcel->write(&selectedDeviceId, sizeof(audio_port_handle_t));
- (void)parcel->write(&sessionId, sizeof(audio_session_t));
-
- /* output arguments*/
- (void)parcel->writeUint32(sampleRate);
- (void)parcel->writeInt64(afFrameCount);
- (void)parcel->writeInt64(afSampleRate);
- (void)parcel->writeInt32(afLatencyMs);
- (void)parcel->write(&outputId, sizeof(audio_io_handle_t));
- (void)parcel->write(&portId, sizeof(audio_port_handle_t));
- return NO_ERROR;
- }
-
/* input/output */
audio_output_flags_t flags;
size_t frameCount;
@@ -200,65 +112,25 @@
uint32_t afLatencyMs;
audio_io_handle_t outputId;
audio_port_handle_t portId;
+ sp<media::IAudioTrack> audioTrack;
+
+ ConversionResult<media::CreateTrackResponse> toAidl() const;
+ static ConversionResult<CreateTrackOutput> fromAidl(const media::CreateTrackResponse& aidl);
};
/* CreateRecordInput contains all input arguments sent by AudioRecord to AudioFlinger
* when calling createRecord() including arguments that will be updated by AudioFlinger
* and returned in CreateRecordOutput object
*/
- class CreateRecordInput : public Parcelable {
+ class CreateRecordInput {
public:
- status_t readFromParcel(const Parcel *parcel) override {
- /* input arguments*/
- memset(&attr, 0, sizeof(audio_attributes_t));
- if (parcel->read(&attr, sizeof(audio_attributes_t)) != NO_ERROR) {
- return DEAD_OBJECT;
- }
- attr.tags[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE -1] = '\0';
- memset(&config, 0, sizeof(audio_config_base_t));
- if (parcel->read(&config, sizeof(audio_config_base_t)) != NO_ERROR) {
- return DEAD_OBJECT;
- }
- if (clientInfo.readFromParcel(parcel) != NO_ERROR) {
- return DEAD_OBJECT;
- }
- opPackageName = parcel->readString16();
- if (parcel->read(&riid, sizeof(audio_unique_id_t)) != NO_ERROR) {
- return DEAD_OBJECT;
- }
-
- /* input/output arguments*/
- (void)parcel->read(&flags, sizeof(audio_input_flags_t));
- frameCount = parcel->readInt64();
- notificationFrameCount = parcel->readInt64();
- (void)parcel->read(&selectedDeviceId, sizeof(audio_port_handle_t));
- (void)parcel->read(&sessionId, sizeof(audio_session_t));
- return NO_ERROR;
- }
-
- status_t writeToParcel(Parcel *parcel) const override {
- /* input arguments*/
- (void)parcel->write(&attr, sizeof(audio_attributes_t));
- (void)parcel->write(&config, sizeof(audio_config_base_t));
- (void)clientInfo.writeToParcel(parcel);
- (void)parcel->writeString16(opPackageName);
- (void)parcel->write(&riid, sizeof(audio_unique_id_t));
-
- /* input/output arguments*/
- (void)parcel->write(&flags, sizeof(audio_input_flags_t));
- (void)parcel->writeInt64(frameCount);
- (void)parcel->writeInt64(notificationFrameCount);
- (void)parcel->write(&selectedDeviceId, sizeof(audio_port_handle_t));
- (void)parcel->write(&sessionId, sizeof(audio_session_t));
- return NO_ERROR;
- }
-
/* input */
audio_attributes_t attr;
audio_config_base_t config;
AudioClient clientInfo;
- String16 opPackageName;
+ media::permission::Identity identity;
audio_unique_id_t riid;
+ int32_t maxSharedAudioHistoryMs;
/* input/output */
audio_input_flags_t flags;
@@ -266,77 +138,17 @@
size_t notificationFrameCount;
audio_port_handle_t selectedDeviceId;
audio_session_t sessionId;
+
+ ConversionResult<media::CreateRecordRequest> toAidl() const;
+ static ConversionResult<CreateRecordInput> fromAidl(const media::CreateRecordRequest& aidl);
};
/* CreateRecordOutput contains all output arguments returned by AudioFlinger to AudioRecord
* when calling createRecord() including arguments that were passed as I/O for update by
* CreateRecordInput.
*/
- class CreateRecordOutput : public Parcelable {
+ class CreateRecordOutput {
public:
- status_t readFromParcel(const Parcel *parcel) override {
- /* input/output arguments*/
- (void)parcel->read(&flags, sizeof(audio_input_flags_t));
- frameCount = parcel->readInt64();
- notificationFrameCount = parcel->readInt64();
- (void)parcel->read(&selectedDeviceId, sizeof(audio_port_handle_t));
- (void)parcel->read(&sessionId, sizeof(audio_session_t));
-
- /* output arguments*/
- sampleRate = parcel->readUint32();
- (void)parcel->read(&inputId, sizeof(audio_io_handle_t));
- if (parcel->readInt32() != 0) {
- cblk = interface_cast<IMemory>(parcel->readStrongBinder());
- // 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());
- // 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;
- }
- }
- (void)parcel->read(&portId, sizeof(audio_port_handle_t));
- return NO_ERROR;
- }
-
- status_t writeToParcel(Parcel *parcel) const override {
- /* input/output arguments*/
- (void)parcel->write(&flags, sizeof(audio_input_flags_t));
- (void)parcel->writeInt64(frameCount);
- (void)parcel->writeInt64(notificationFrameCount);
- (void)parcel->write(&selectedDeviceId, sizeof(audio_port_handle_t));
- (void)parcel->write(&sessionId, sizeof(audio_session_t));
-
- /* output arguments*/
- (void)parcel->writeUint32(sampleRate);
- (void)parcel->write(&inputId, sizeof(audio_io_handle_t));
- if (cblk != 0) {
- (void)parcel->writeInt32(1);
- (void)parcel->writeStrongBinder(IInterface::asBinder(cblk));
- } else {
- (void)parcel->writeInt32(0);
- }
- if (buffers != 0) {
- (void)parcel->writeInt32(1);
- (void)parcel->writeStrongBinder(IInterface::asBinder(buffers));
- } else {
- (void)parcel->writeInt32(0);
- }
- (void)parcel->write(&portId, sizeof(audio_port_handle_t));
-
- return NO_ERROR;
- }
-
/* input/output */
audio_input_flags_t flags;
size_t frameCount;
@@ -350,21 +162,26 @@
sp<IMemory> cblk;
sp<IMemory> buffers;
audio_port_handle_t portId;
+ sp<media::IAudioRecord> audioRecord;
+
+ ConversionResult<media::CreateRecordResponse> toAidl() const;
+ static ConversionResult<CreateRecordOutput>
+ fromAidl(const media::CreateRecordResponse& aidl);
};
- // invariant on exit for all APIs that return an sp<>:
- // (return value != 0) == (*status == NO_ERROR)
-
/* create an audio track and registers it with AudioFlinger.
- * return null if the track cannot be created.
+ * The audioTrack field will be null if the track cannot be created and the status will reflect
+ * failure.
*/
- virtual sp<IAudioTrack> createTrack(const CreateTrackInput& input,
- CreateTrackOutput& output,
- status_t *status) = 0;
+ virtual status_t createTrack(const media::CreateTrackRequest& input,
+ media::CreateTrackResponse& output) = 0;
- virtual sp<media::IAudioRecord> createRecord(const CreateRecordInput& input,
- CreateRecordOutput& output,
- status_t *status) = 0;
+ /* create an audio record and registers it with AudioFlinger.
+ * The audioRecord field will be null if the track cannot be created and the status will reflect
+ * failure.
+ */
+ virtual status_t createRecord(const media::CreateRecordRequest& input,
+ media::CreateRecordResponse& output) = 0;
// FIXME Surprisingly, format/latency don't work for input handles
@@ -420,32 +237,24 @@
// Register an object to receive audio input/output change and track notifications.
// For a given calling pid, AudioFlinger disregards any registrations after the first.
// Thus the IAudioFlingerClient must be a singleton per process.
- virtual void registerClient(const sp<IAudioFlingerClient>& client) = 0;
+ virtual void registerClient(const sp<media::IAudioFlingerClient>& client) = 0;
// 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;
- virtual status_t openOutput(audio_module_handle_t module,
- audio_io_handle_t *output,
- audio_config_t *config,
- const sp<DeviceDescriptorBase>& device,
- uint32_t *latencyMs,
- audio_output_flags_t flags) = 0;
+ virtual status_t openOutput(const media::OpenOutputRequest& request,
+ media::OpenOutputResponse* response) = 0;
virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1,
audio_io_handle_t output2) = 0;
virtual status_t closeOutput(audio_io_handle_t output) = 0;
virtual status_t suspendOutput(audio_io_handle_t output) = 0;
virtual status_t restoreOutput(audio_io_handle_t output) = 0;
- virtual status_t openInput(audio_module_handle_t module,
- audio_io_handle_t *input,
- audio_config_t *config,
- audio_devices_t *device,
- const String8& address,
- audio_source_t source,
- audio_input_flags_t flags) = 0;
+ virtual status_t openInput(const media::OpenInputRequest& request,
+ media::OpenInputResponse* response) = 0;
+
virtual status_t closeInput(audio_io_handle_t input) = 0;
virtual status_t invalidateStream(audio_stream_type_t stream) = 0;
@@ -471,20 +280,8 @@
uint32_t preferredTypeFlag,
effect_descriptor_t *pDescriptor) const = 0;
- virtual sp<IEffect> createEffect(
- effect_descriptor_t *pDesc,
- const sp<IEffectClient>& client,
- int32_t priority,
- // AudioFlinger doesn't take over handle reference from client
- audio_io_handle_t output,
- audio_session_t sessionId,
- const AudioDeviceTypeAddr& device,
- const String16& callingPackage,
- pid_t pid,
- bool probe,
- status_t *status,
- int *id,
- int *enabled) = 0;
+ virtual status_t createEffect(const media::CreateEffectRequest& request,
+ media::CreateEffectResponse* response) = 0;
virtual status_t moveEffects(audio_session_t session, audio_io_handle_t srcOutput,
audio_io_handle_t dstOutput) = 0;
@@ -507,12 +304,8 @@
// is obtained from android.app.ActivityManager.MemoryInfo.totalMem.
virtual status_t setLowRamDevice(bool isLowRamDevice, int64_t totalMemory) = 0;
- /* List available audio ports and their attributes */
- virtual status_t listAudioPorts(unsigned int *num_ports,
- struct audio_port *ports) = 0;
-
/* Get attributes for a given audio port */
- virtual status_t getAudioPort(struct audio_port *port) = 0;
+ virtual status_t getAudioPort(struct audio_port_v7 *port) = 0;
/* Create an audio patch between several source and sink ports */
virtual status_t createAudioPatch(const struct audio_patch *patch,
@@ -540,24 +333,296 @@
virtual status_t getMicrophones(std::vector<media::MicrophoneInfo> *microphones) = 0;
virtual status_t setAudioHalPids(const std::vector<pid_t>& pids) = 0;
+
+ // Set vibrators' information.
+ // The values will be used to initialize HapticGenerator.
+ virtual status_t setVibratorInfos(
+ const std::vector<media::AudioVibratorInfo>& vibratorInfos) = 0;
};
-
-// ----------------------------------------------------------------------------
-
-class BnAudioFlinger : public BnInterface<IAudioFlinger>
-{
+/**
+ * A client-side adapter, wrapping an IAudioFlingerService instance and presenting it as an
+ * IAudioFlinger. Intended to be used by legacy client code that was written against IAudioFlinger,
+ * before IAudioFlingerService was introduced as an AIDL service.
+ * New clients should not use this adapter, but rather IAudioFlingerService directly, via
+ * BpAudioFlingerService.
+ */
+class AudioFlingerClientAdapter : public IAudioFlinger {
public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
+ explicit AudioFlingerClientAdapter(const sp<media::IAudioFlingerService> delegate);
- // Requests media.log to start merging log buffers
- virtual void requestLogMerge() = 0;
+ status_t createTrack(const media::CreateTrackRequest& input,
+ media::CreateTrackResponse& output) override;
+ status_t createRecord(const media::CreateRecordRequest& input,
+ media::CreateRecordResponse& output) override;
+ uint32_t sampleRate(audio_io_handle_t ioHandle) const override;
+ audio_format_t format(audio_io_handle_t output) const override;
+ size_t frameCount(audio_io_handle_t ioHandle) const override;
+ uint32_t latency(audio_io_handle_t output) const override;
+ status_t setMasterVolume(float value) override;
+ status_t setMasterMute(bool muted) override;
+ float masterVolume() const override;
+ bool masterMute() const override;
+ status_t setMasterBalance(float balance) override;
+ status_t getMasterBalance(float* balance) const override;
+ status_t setStreamVolume(audio_stream_type_t stream, float value,
+ audio_io_handle_t output) override;
+ status_t setStreamMute(audio_stream_type_t stream, bool muted) override;
+ float streamVolume(audio_stream_type_t stream,
+ audio_io_handle_t output) const override;
+ bool streamMute(audio_stream_type_t stream) const override;
+ status_t setMode(audio_mode_t mode) override;
+ status_t setMicMute(bool state) override;
+ bool getMicMute() const override;
+ void setRecordSilenced(audio_port_handle_t portId, bool silenced) override;
+ status_t setParameters(audio_io_handle_t ioHandle,
+ const String8& keyValuePairs) override;
+ String8 getParameters(audio_io_handle_t ioHandle, const String8& keys)
+ const override;
+ void registerClient(const sp<media::IAudioFlingerClient>& client) override;
+ size_t getInputBufferSize(uint32_t sampleRate, audio_format_t format,
+ audio_channel_mask_t channelMask) const override;
+ status_t openOutput(const media::OpenOutputRequest& request,
+ media::OpenOutputResponse* response) override;
+ audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1,
+ audio_io_handle_t output2) override;
+ status_t closeOutput(audio_io_handle_t output) override;
+ status_t suspendOutput(audio_io_handle_t output) override;
+ status_t restoreOutput(audio_io_handle_t output) override;
+ status_t openInput(const media::OpenInputRequest& request,
+ media::OpenInputResponse* response) override;
+ status_t closeInput(audio_io_handle_t input) override;
+ status_t invalidateStream(audio_stream_type_t stream) override;
+ status_t setVoiceVolume(float volume) override;
+ status_t getRenderPosition(uint32_t* halFrames, uint32_t* dspFrames,
+ audio_io_handle_t output) const override;
+ uint32_t getInputFramesLost(audio_io_handle_t ioHandle) const override;
+ audio_unique_id_t newAudioUniqueId(audio_unique_id_use_t use) override;
+ void acquireAudioSessionId(audio_session_t audioSession, pid_t pid, uid_t uid) override;
+ void releaseAudioSessionId(audio_session_t audioSession, pid_t pid) override;
+ status_t queryNumberEffects(uint32_t* numEffects) const override;
+ status_t queryEffect(uint32_t index, effect_descriptor_t* pDescriptor) const override;
+ status_t getEffectDescriptor(const effect_uuid_t* pEffectUUID,
+ const effect_uuid_t* pTypeUUID,
+ uint32_t preferredTypeFlag,
+ effect_descriptor_t* pDescriptor) const override;
+ status_t createEffect(const media::CreateEffectRequest& request,
+ media::CreateEffectResponse* response) override;
+ status_t moveEffects(audio_session_t session, audio_io_handle_t srcOutput,
+ audio_io_handle_t dstOutput) override;
+ void setEffectSuspended(int effectId,
+ audio_session_t sessionId,
+ bool suspended) override;
+ audio_module_handle_t loadHwModule(const char* name) override;
+ uint32_t getPrimaryOutputSamplingRate() override;
+ size_t getPrimaryOutputFrameCount() override;
+ status_t setLowRamDevice(bool isLowRamDevice, int64_t totalMemory) override;
+ status_t getAudioPort(struct audio_port_v7* port) override;
+ status_t createAudioPatch(const struct audio_patch* patch,
+ audio_patch_handle_t* handle) override;
+ status_t releaseAudioPatch(audio_patch_handle_t handle) override;
+ status_t listAudioPatches(unsigned int* num_patches,
+ struct audio_patch* patches) override;
+ status_t setAudioPortConfig(const struct audio_port_config* config) override;
+ audio_hw_sync_t getAudioHwSyncForSession(audio_session_t sessionId) override;
+ status_t systemReady() override;
+ size_t frameCountHAL(audio_io_handle_t ioHandle) const override;
+ status_t getMicrophones(std::vector<media::MicrophoneInfo>* microphones) override;
+ status_t setAudioHalPids(const std::vector<pid_t>& pids) override;
+ status_t setVibratorInfos(const std::vector<media::AudioVibratorInfo>& vibratorInfos) override;
+
+private:
+ const sp<media::IAudioFlingerService> mDelegate;
};
-// ----------------------------------------------------------------------------
+/**
+ * A server-side adapter, wrapping an IAudioFlinger instance and presenting it as an
+ * IAudioFlingerService. Intended to be used by legacy server code that was written against
+ * IAudioFlinger, before IAudioFlingerService was introduced as an AIDL service.
+ * New servers should not use this adapter, but rather implement IAudioFlingerService directly, via
+ * BnAudioFlingerService.
+ */
+class AudioFlingerServerAdapter : public media::BnAudioFlingerService {
+public:
+ using Status = binder::Status;
+
+ /**
+ * Legacy server should implement this interface in order to be wrapped.
+ */
+ class Delegate : public IAudioFlinger {
+ protected:
+ friend class AudioFlingerServerAdapter;
+
+ enum class TransactionCode {
+ CREATE_TRACK = media::BnAudioFlingerService::TRANSACTION_createTrack,
+ CREATE_RECORD = media::BnAudioFlingerService::TRANSACTION_createRecord,
+ SAMPLE_RATE = media::BnAudioFlingerService::TRANSACTION_sampleRate,
+ FORMAT = media::BnAudioFlingerService::TRANSACTION_format,
+ FRAME_COUNT = media::BnAudioFlingerService::TRANSACTION_frameCount,
+ LATENCY = media::BnAudioFlingerService::TRANSACTION_latency,
+ SET_MASTER_VOLUME = media::BnAudioFlingerService::TRANSACTION_setMasterVolume,
+ SET_MASTER_MUTE = media::BnAudioFlingerService::TRANSACTION_setMasterMute,
+ MASTER_VOLUME = media::BnAudioFlingerService::TRANSACTION_masterVolume,
+ MASTER_MUTE = media::BnAudioFlingerService::TRANSACTION_masterMute,
+ SET_STREAM_VOLUME = media::BnAudioFlingerService::TRANSACTION_setStreamVolume,
+ SET_STREAM_MUTE = media::BnAudioFlingerService::TRANSACTION_setStreamMute,
+ STREAM_VOLUME = media::BnAudioFlingerService::TRANSACTION_streamVolume,
+ STREAM_MUTE = media::BnAudioFlingerService::TRANSACTION_streamMute,
+ SET_MODE = media::BnAudioFlingerService::TRANSACTION_setMode,
+ SET_MIC_MUTE = media::BnAudioFlingerService::TRANSACTION_setMicMute,
+ GET_MIC_MUTE = media::BnAudioFlingerService::TRANSACTION_getMicMute,
+ SET_RECORD_SILENCED = media::BnAudioFlingerService::TRANSACTION_setRecordSilenced,
+ SET_PARAMETERS = media::BnAudioFlingerService::TRANSACTION_setParameters,
+ GET_PARAMETERS = media::BnAudioFlingerService::TRANSACTION_getParameters,
+ REGISTER_CLIENT = media::BnAudioFlingerService::TRANSACTION_registerClient,
+ GET_INPUTBUFFERSIZE = media::BnAudioFlingerService::TRANSACTION_getInputBufferSize,
+ OPEN_OUTPUT = media::BnAudioFlingerService::TRANSACTION_openOutput,
+ OPEN_DUPLICATE_OUTPUT = media::BnAudioFlingerService::TRANSACTION_openDuplicateOutput,
+ CLOSE_OUTPUT = media::BnAudioFlingerService::TRANSACTION_closeOutput,
+ SUSPEND_OUTPUT = media::BnAudioFlingerService::TRANSACTION_suspendOutput,
+ RESTORE_OUTPUT = media::BnAudioFlingerService::TRANSACTION_restoreOutput,
+ OPEN_INPUT = media::BnAudioFlingerService::TRANSACTION_openInput,
+ CLOSE_INPUT = media::BnAudioFlingerService::TRANSACTION_closeInput,
+ INVALIDATE_STREAM = media::BnAudioFlingerService::TRANSACTION_invalidateStream,
+ SET_VOICE_VOLUME = media::BnAudioFlingerService::TRANSACTION_setVoiceVolume,
+ GET_RENDER_POSITION = media::BnAudioFlingerService::TRANSACTION_getRenderPosition,
+ GET_INPUT_FRAMES_LOST = media::BnAudioFlingerService::TRANSACTION_getInputFramesLost,
+ NEW_AUDIO_UNIQUE_ID = media::BnAudioFlingerService::TRANSACTION_newAudioUniqueId,
+ ACQUIRE_AUDIO_SESSION_ID = media::BnAudioFlingerService::TRANSACTION_acquireAudioSessionId,
+ RELEASE_AUDIO_SESSION_ID = media::BnAudioFlingerService::TRANSACTION_releaseAudioSessionId,
+ QUERY_NUM_EFFECTS = media::BnAudioFlingerService::TRANSACTION_queryNumberEffects,
+ QUERY_EFFECT = media::BnAudioFlingerService::TRANSACTION_queryEffect,
+ GET_EFFECT_DESCRIPTOR = media::BnAudioFlingerService::TRANSACTION_getEffectDescriptor,
+ CREATE_EFFECT = media::BnAudioFlingerService::TRANSACTION_createEffect,
+ MOVE_EFFECTS = media::BnAudioFlingerService::TRANSACTION_moveEffects,
+ LOAD_HW_MODULE = media::BnAudioFlingerService::TRANSACTION_loadHwModule,
+ GET_PRIMARY_OUTPUT_SAMPLING_RATE = media::BnAudioFlingerService::TRANSACTION_getPrimaryOutputSamplingRate,
+ GET_PRIMARY_OUTPUT_FRAME_COUNT = media::BnAudioFlingerService::TRANSACTION_getPrimaryOutputFrameCount,
+ SET_LOW_RAM_DEVICE = media::BnAudioFlingerService::TRANSACTION_setLowRamDevice,
+ GET_AUDIO_PORT = media::BnAudioFlingerService::TRANSACTION_getAudioPort,
+ CREATE_AUDIO_PATCH = media::BnAudioFlingerService::TRANSACTION_createAudioPatch,
+ RELEASE_AUDIO_PATCH = media::BnAudioFlingerService::TRANSACTION_releaseAudioPatch,
+ LIST_AUDIO_PATCHES = media::BnAudioFlingerService::TRANSACTION_listAudioPatches,
+ SET_AUDIO_PORT_CONFIG = media::BnAudioFlingerService::TRANSACTION_setAudioPortConfig,
+ GET_AUDIO_HW_SYNC_FOR_SESSION = media::BnAudioFlingerService::TRANSACTION_getAudioHwSyncForSession,
+ SYSTEM_READY = media::BnAudioFlingerService::TRANSACTION_systemReady,
+ FRAME_COUNT_HAL = media::BnAudioFlingerService::TRANSACTION_frameCountHAL,
+ GET_MICROPHONES = media::BnAudioFlingerService::TRANSACTION_getMicrophones,
+ SET_MASTER_BALANCE = media::BnAudioFlingerService::TRANSACTION_setMasterBalance,
+ GET_MASTER_BALANCE = media::BnAudioFlingerService::TRANSACTION_getMasterBalance,
+ SET_EFFECT_SUSPENDED = media::BnAudioFlingerService::TRANSACTION_setEffectSuspended,
+ SET_AUDIO_HAL_PIDS = media::BnAudioFlingerService::TRANSACTION_setAudioHalPids,
+ SET_VIBRATOR_INFOS = media::BnAudioFlingerService::TRANSACTION_setVibratorInfos,
+ };
+
+ /**
+ * And optional hook, called on every transaction, allowing additional operations to be
+ * performed before/after the unparceling ofthe data and dispatching to the respective
+ * method. Useful for bulk operations, such as logging or permission checks.
+ * The implementer is responsible to invoke the provided delegate function, which is the
+ * actual onTransact(), unless an error occurs.
+ * By default, this is just a pass-through to the delegate.
+ */
+ virtual status_t onTransactWrapper(TransactionCode code,
+ const Parcel& data,
+ uint32_t flags,
+ const std::function<status_t()>& delegate) {
+ (void) code;
+ (void) data;
+ (void) flags;
+ return delegate();
+ }
+
+ /**
+ * An optional hook for implementing diagnostics dumping.
+ */
+ virtual status_t dump(int fd, const Vector<String16>& args) {
+ (void) fd;
+ (void) args;
+ return OK;
+ }
+ };
+
+ explicit AudioFlingerServerAdapter(
+ const sp<AudioFlingerServerAdapter::Delegate>& delegate);
+
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override;
+ status_t dump(int fd, const Vector<String16>& args) override;
+
+ Status createTrack(const media::CreateTrackRequest& request,
+ media::CreateTrackResponse* _aidl_return) override;
+ Status createRecord(const media::CreateRecordRequest& request,
+ media::CreateRecordResponse* _aidl_return) override;
+ Status sampleRate(int32_t ioHandle, int32_t* _aidl_return) override;
+ Status format(int32_t output, media::audio::common::AudioFormat* _aidl_return) override;
+ Status frameCount(int32_t ioHandle, int64_t* _aidl_return) override;
+ Status latency(int32_t output, int32_t* _aidl_return) override;
+ Status setMasterVolume(float value) override;
+ Status setMasterMute(bool muted) override;
+ Status masterVolume(float* _aidl_return) override;
+ Status masterMute(bool* _aidl_return) override;
+ Status setMasterBalance(float balance) override;
+ Status getMasterBalance(float* _aidl_return) override;
+ Status setStreamVolume(media::AudioStreamType stream, float value, int32_t output) override;
+ Status setStreamMute(media::AudioStreamType stream, bool muted) override;
+ Status
+ streamVolume(media::AudioStreamType stream, int32_t output, float* _aidl_return) override;
+ Status streamMute(media::AudioStreamType stream, bool* _aidl_return) override;
+ Status setMode(media::AudioMode mode) override;
+ Status setMicMute(bool state) override;
+ Status getMicMute(bool* _aidl_return) override;
+ Status setRecordSilenced(int32_t portId, bool silenced) override;
+ Status setParameters(int32_t ioHandle, const std::string& keyValuePairs) override;
+ Status
+ getParameters(int32_t ioHandle, const std::string& keys, std::string* _aidl_return) override;
+ Status registerClient(const sp<media::IAudioFlingerClient>& client) override;
+ Status getInputBufferSize(int32_t sampleRate, media::audio::common::AudioFormat format,
+ int32_t channelMask, int64_t* _aidl_return) override;
+ Status openOutput(const media::OpenOutputRequest& request,
+ media::OpenOutputResponse* _aidl_return) override;
+ Status openDuplicateOutput(int32_t output1, int32_t output2, int32_t* _aidl_return) override;
+ Status closeOutput(int32_t output) override;
+ Status suspendOutput(int32_t output) override;
+ Status restoreOutput(int32_t output) override;
+ Status openInput(const media::OpenInputRequest& request,
+ media::OpenInputResponse* _aidl_return) override;
+ Status closeInput(int32_t input) override;
+ Status invalidateStream(media::AudioStreamType stream) override;
+ Status setVoiceVolume(float volume) override;
+ Status getRenderPosition(int32_t output, media::RenderPosition* _aidl_return) override;
+ Status getInputFramesLost(int32_t ioHandle, int32_t* _aidl_return) override;
+ Status newAudioUniqueId(media::AudioUniqueIdUse use, int32_t* _aidl_return) override;
+ Status acquireAudioSessionId(int32_t audioSession, int32_t pid, int32_t uid) override;
+ Status releaseAudioSessionId(int32_t audioSession, int32_t pid) override;
+ Status queryNumberEffects(int32_t* _aidl_return) override;
+ Status queryEffect(int32_t index, media::EffectDescriptor* _aidl_return) override;
+ Status getEffectDescriptor(const media::AudioUuid& effectUUID, const media::AudioUuid& typeUUID,
+ int32_t preferredTypeFlag,
+ media::EffectDescriptor* _aidl_return) override;
+ Status createEffect(const media::CreateEffectRequest& request,
+ media::CreateEffectResponse* _aidl_return) override;
+ Status moveEffects(int32_t session, int32_t srcOutput, int32_t dstOutput) override;
+ Status setEffectSuspended(int32_t effectId, int32_t sessionId, bool suspended) override;
+ Status loadHwModule(const std::string& name, int32_t* _aidl_return) override;
+ Status getPrimaryOutputSamplingRate(int32_t* _aidl_return) override;
+ Status getPrimaryOutputFrameCount(int64_t* _aidl_return) override;
+ Status setLowRamDevice(bool isLowRamDevice, int64_t totalMemory) override;
+ Status getAudioPort(const media::AudioPort& port, media::AudioPort* _aidl_return) override;
+ Status createAudioPatch(const media::AudioPatch& patch, int32_t* _aidl_return) override;
+ Status releaseAudioPatch(int32_t handle) override;
+ Status listAudioPatches(int32_t maxCount,
+ std::vector<media::AudioPatch>* _aidl_return) override;
+ Status setAudioPortConfig(const media::AudioPortConfig& config) override;
+ Status getAudioHwSyncForSession(int32_t sessionId, int32_t* _aidl_return) override;
+ Status systemReady() override;
+ Status frameCountHAL(int32_t ioHandle, int64_t* _aidl_return) override;
+ Status getMicrophones(std::vector<media::MicrophoneInfoData>* _aidl_return) override;
+ Status setAudioHalPids(const std::vector<int32_t>& pids) override;
+ Status setVibratorInfos(const std::vector<media::AudioVibratorInfo>& vibratorInfos) override;
+
+private:
+ const sp<AudioFlingerServerAdapter::Delegate> mDelegate;
+};
}; // namespace android
diff --git a/media/libaudioclient/include/media/IAudioFlingerClient.h b/media/libaudioclient/include/media/IAudioFlingerClient.h
deleted file mode 100644
index 0080bc9..0000000
--- a/media/libaudioclient/include/media/IAudioFlingerClient.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_IAUDIOFLINGERCLIENT_H
-#define ANDROID_IAUDIOFLINGERCLIENT_H
-
-
-#include <utils/RefBase.h>
-#include <binder/IInterface.h>
-#include <utils/KeyedVector.h>
-#include <system/audio.h>
-#include <media/AudioIoDescriptor.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-class IAudioFlingerClient : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(AudioFlingerClient);
-
- // Notifies a change of audio input/output configuration.
- virtual void ioConfigChanged(audio_io_config_event event,
- const sp<AudioIoDescriptor>& ioDesc) = 0;
-
-};
-
-
-// ----------------------------------------------------------------------------
-
-class BnAudioFlingerClient : public BnInterface<IAudioFlingerClient>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IAUDIOFLINGERCLIENT_H
diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h
deleted file mode 100644
index 837375d..0000000
--- a/media/libaudioclient/include/media/IAudioPolicyService.h
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_IAUDIOPOLICYSERVICE_H
-#define ANDROID_IAUDIOPOLICYSERVICE_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <utils/RefBase.h>
-#include <utils/Errors.h>
-#include <binder/IInterface.h>
-#include <media/AudioDeviceTypeAddr.h>
-#include <media/AudioSystem.h>
-#include <media/AudioPolicy.h>
-#include <media/IAudioPolicyServiceClient.h>
-#include <system/audio_policy.h>
-#include <vector>
-
-namespace android {
-namespace media {
-// Must be pre-declared, or else there isn't a good way to generate a header
-// library.
-class ICaptureStateListener;
-}
-
-// ----------------------------------------------------------------------------
-
-class IAudioPolicyService : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(AudioPolicyService);
-
- //
- // IAudioPolicyService interface (see AudioPolicyInterface for method descriptions)
- //
- virtual void onNewAudioModulesAvailable() = 0;
- virtual status_t setDeviceConnectionState(audio_devices_t device,
- audio_policy_dev_state_t state,
- const char *device_address,
- const char *device_name,
- audio_format_t encodedFormat) = 0;
- virtual audio_policy_dev_state_t getDeviceConnectionState(audio_devices_t device,
- const char *device_address) = 0;
- virtual status_t handleDeviceConfigChange(audio_devices_t device,
- const char *device_address,
- const char *device_name,
- audio_format_t encodedFormat) = 0;
- virtual status_t setPhoneState(audio_mode_t state, uid_t uid) = 0;
- virtual status_t setForceUse(audio_policy_force_use_t usage,
- audio_policy_forced_cfg_t config) = 0;
- virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage) = 0;
- virtual audio_io_handle_t getOutput(audio_stream_type_t stream) = 0;
- virtual status_t getOutputForAttr(audio_attributes_t *attr,
- audio_io_handle_t *output,
- audio_session_t session,
- audio_stream_type_t *stream,
- pid_t pid,
- uid_t uid,
- const audio_config_t *config,
- audio_output_flags_t flags,
- audio_port_handle_t *selectedDeviceId,
- audio_port_handle_t *portId,
- std::vector<audio_io_handle_t> *secondaryOutputs) = 0;
- virtual status_t startOutput(audio_port_handle_t portId) = 0;
- virtual status_t stopOutput(audio_port_handle_t portId) = 0;
- virtual void releaseOutput(audio_port_handle_t portId) = 0;
- virtual status_t getInputForAttr(const audio_attributes_t *attr,
- audio_io_handle_t *input,
- audio_unique_id_t riid,
- audio_session_t session,
- pid_t pid,
- uid_t uid,
- const String16& opPackageName,
- const audio_config_base_t *config,
- audio_input_flags_t flags,
- audio_port_handle_t *selectedDeviceId,
- audio_port_handle_t *portId) = 0;
- virtual status_t startInput(audio_port_handle_t portId) = 0;
- virtual status_t stopInput(audio_port_handle_t portId) = 0;
- virtual void releaseInput(audio_port_handle_t portId) = 0;
- virtual status_t initStreamVolume(audio_stream_type_t stream,
- int indexMin,
- int indexMax) = 0;
- virtual status_t setStreamVolumeIndex(audio_stream_type_t stream,
- int index,
- audio_devices_t device) = 0;
- virtual status_t getStreamVolumeIndex(audio_stream_type_t stream,
- int *index,
- audio_devices_t device) = 0;
-
- virtual status_t setVolumeIndexForAttributes(const audio_attributes_t &attr,
- int index,
- audio_devices_t device) = 0;
- virtual status_t getVolumeIndexForAttributes(const audio_attributes_t &attr,
- int &index,
- audio_devices_t device) = 0;
- virtual status_t getMaxVolumeIndexForAttributes(const audio_attributes_t &attr, int &index) = 0;
-
- virtual status_t getMinVolumeIndexForAttributes(const audio_attributes_t &attr, int &index) = 0;
-
- 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,
- uint32_t strategy,
- audio_session_t session,
- int id) = 0;
- virtual status_t unregisterEffect(int id) = 0;
- virtual status_t setEffectEnabled(int id, bool enabled) = 0;
- virtual status_t moveEffectsToIo(const std::vector<int>& ids, audio_io_handle_t io) = 0;
- virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const = 0;
- virtual bool isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs = 0)
- const = 0;
- virtual bool isSourceActive(audio_source_t source) const = 0;
- virtual status_t queryDefaultPreProcessing(audio_session_t audioSession,
- effect_descriptor_t *descriptors,
- uint32_t *count) = 0;
- virtual status_t addSourceDefaultEffect(const effect_uuid_t *type,
- const String16& opPackageName,
- const effect_uuid_t *uuid,
- int32_t priority,
- audio_source_t source,
- audio_unique_id_t* id) = 0;
- virtual status_t addStreamDefaultEffect(const effect_uuid_t *type,
- const String16& opPackageName,
- const effect_uuid_t *uuid,
- int32_t priority,
- audio_usage_t usage,
- audio_unique_id_t* id) = 0;
- virtual status_t removeSourceDefaultEffect(audio_unique_id_t id) = 0;
- virtual status_t removeStreamDefaultEffect(audio_unique_id_t id) = 0;
- virtual status_t setSupportedSystemUsages(const std::vector<audio_usage_t>& systemUsages) = 0;
- virtual status_t setAllowedCapturePolicy(uid_t uid, audio_flags_mask_t flags) = 0;
- // Check if offload is possible for given format, stream type, sample rate,
- // bit rate, duration, video and streaming or offload property is enabled
- virtual bool isOffloadSupported(const audio_offload_info_t& info) = 0;
-
- // Check if direct playback is possible for given format, sample rate, channel mask and flags.
- virtual bool isDirectOutputSupported(const audio_config_base_t& config,
- const audio_attributes_t& attributes) = 0;
-
- /* List available audio ports and their attributes */
- virtual status_t listAudioPorts(audio_port_role_t role,
- audio_port_type_t type,
- unsigned int *num_ports,
- struct audio_port *ports,
- unsigned int *generation) = 0;
-
- /* Get attributes for a given audio port */
- virtual status_t getAudioPort(struct audio_port *port) = 0;
-
- /* Create an audio patch between several source and sink ports */
- virtual status_t createAudioPatch(const struct audio_patch *patch,
- audio_patch_handle_t *handle) = 0;
-
- /* Release an audio patch */
- virtual status_t releaseAudioPatch(audio_patch_handle_t handle) = 0;
-
- /* List existing audio patches */
- virtual status_t listAudioPatches(unsigned int *num_patches,
- struct audio_patch *patches,
- unsigned int *generation) = 0;
- /* Set audio port configuration */
- virtual status_t setAudioPortConfig(const struct audio_port_config *config) = 0;
-
- virtual void registerClient(const sp<IAudioPolicyServiceClient>& client) = 0;
-
- virtual void setAudioPortCallbacksEnabled(bool enabled) = 0;
-
- virtual void setAudioVolumeGroupCallbacksEnabled(bool enabled) = 0;
-
- virtual status_t acquireSoundTriggerSession(audio_session_t *session,
- audio_io_handle_t *ioHandle,
- audio_devices_t *device) = 0;
-
- virtual status_t releaseSoundTriggerSession(audio_session_t session) = 0;
-
- virtual audio_mode_t getPhoneState() = 0;
-
- virtual status_t registerPolicyMixes(const Vector<AudioMix>& mixes, bool registration) = 0;
-
- virtual status_t setUidDeviceAffinities(uid_t uid, const AudioDeviceTypeAddrVector& devices)
- = 0;
-
- virtual status_t removeUidDeviceAffinities(uid_t uid) = 0;
-
- virtual status_t setUserIdDeviceAffinities(int userId,
- const AudioDeviceTypeAddrVector& devices) = 0;
-
- virtual status_t removeUserIdDeviceAffinities(int userId) = 0;
-
- virtual status_t startAudioSource(const struct audio_port_config *source,
- const audio_attributes_t *attributes,
- audio_port_handle_t *portId) = 0;
- virtual status_t stopAudioSource(audio_port_handle_t portId) = 0;
-
- virtual status_t setMasterMono(bool mono) = 0;
- virtual status_t getMasterMono(bool *mono) = 0;
- virtual float getStreamVolumeDB(
- audio_stream_type_t stream, int index, audio_devices_t device) = 0;
-
- virtual status_t getSurroundFormats(unsigned int *numSurroundFormats,
- audio_format_t *surroundFormats,
- bool *surroundFormatsEnabled,
- bool reported) = 0;
- virtual status_t getHwOffloadEncodingFormatsSupportedForA2DP(
- std::vector<audio_format_t> *formats) = 0;
- virtual status_t setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled) = 0;
-
- virtual status_t setAssistantUid(uid_t uid) = 0;
- virtual status_t setA11yServicesUids(const std::vector<uid_t>& uids) = 0;
- virtual status_t setCurrentImeUid(uid_t uid) = 0;
-
- virtual bool isHapticPlaybackSupported() = 0;
- virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) = 0;
- virtual status_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa,
- product_strategy_t &productStrategy) = 0;
-
- virtual status_t listAudioVolumeGroups(AudioVolumeGroupVector &groups) = 0;
- virtual status_t getVolumeGroupFromAudioAttributes(const AudioAttributes &aa,
- volume_group_t &volumeGroup) = 0;
-
- virtual status_t setRttEnabled(bool enabled) = 0;
-
- virtual bool isCallScreenModeSupported() = 0;
-
- virtual status_t setDevicesRoleForStrategy(product_strategy_t strategy,
- device_role_t role,
- const AudioDeviceTypeAddrVector &devices) = 0;
-
- virtual status_t removeDevicesRoleForStrategy(product_strategy_t strategy,
- device_role_t role) = 0;
-
- virtual status_t getDevicesForRoleAndStrategy(product_strategy_t strategy,
- device_role_t role,
- AudioDeviceTypeAddrVector &devices) = 0;
-
- virtual status_t setDevicesRoleForCapturePreset(audio_source_t audioSource,
- device_role_t role,
- const AudioDeviceTypeAddrVector &devices) = 0;
-
- virtual status_t addDevicesRoleForCapturePreset(audio_source_t audioSource,
- device_role_t role,
- const AudioDeviceTypeAddrVector &devices) = 0;
-
- virtual status_t removeDevicesRoleForCapturePreset(
- audio_source_t audioSource, device_role_t role,
- const AudioDeviceTypeAddrVector& devices) = 0;
-
- virtual status_t clearDevicesRoleForCapturePreset(audio_source_t audioSource,
- device_role_t role) = 0;
-
- virtual status_t getDevicesForRoleAndCapturePreset(audio_source_t audioSource,
- device_role_t role,
- AudioDeviceTypeAddrVector &devices) = 0;
-
- // The return code here is only intended to represent transport errors. The
- // actual server implementation should always return NO_ERROR.
- virtual status_t registerSoundTriggerCaptureStateListener(
- const sp<media::ICaptureStateListener>& listener,
- bool* result) = 0;
-};
-
-
-// ----------------------------------------------------------------------------
-
-class BnAudioPolicyService : public BnInterface<IAudioPolicyService>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IAUDIOPOLICYSERVICE_H
diff --git a/media/libaudioclient/include/media/IAudioPolicyServiceClient.h b/media/libaudioclient/include/media/IAudioPolicyServiceClient.h
deleted file mode 100644
index 47b31ee..0000000
--- a/media/libaudioclient/include/media/IAudioPolicyServiceClient.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_IAUDIOPOLICYSERVICECLIENT_H
-#define ANDROID_IAUDIOPOLICYSERVICECLIENT_H
-
-#include <vector>
-
-#include <utils/RefBase.h>
-#include <binder/IInterface.h>
-#include <system/audio.h>
-#include <system/audio_effect.h>
-#include <media/AudioPolicy.h>
-#include <media/AudioVolumeGroup.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-struct record_client_info {
- audio_unique_id_t riid;
- uid_t uid;
- audio_session_t session;
- audio_source_t source;
- audio_port_handle_t port_id;
- bool silenced;
-};
-
-typedef struct record_client_info record_client_info_t;
-
-// ----------------------------------------------------------------------------
-
-class IAudioPolicyServiceClient : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(AudioPolicyServiceClient);
-
- // Notifies a change of volume group
- virtual void onAudioVolumeGroupChanged(volume_group_t group, int flags) = 0;
- // Notifies a change of audio port configuration.
- virtual void onAudioPortListUpdate() = 0;
- // Notifies a change of audio patch configuration.
- virtual void onAudioPatchListUpdate() = 0;
- // Notifies a change in the mixing state of a specific mix in a dynamic audio policy
- virtual void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state) = 0;
- // Notifies a change of audio recording configuration
- virtual void onRecordingConfigurationUpdate(int event,
- const record_client_info_t *clientInfo,
- const audio_config_base_t *clientConfig,
- std::vector<effect_descriptor_t> clientEffects,
- const audio_config_base_t *deviceConfig,
- std::vector<effect_descriptor_t> effects,
- audio_patch_handle_t patchHandle,
- audio_source_t source) = 0;
-};
-
-
-// ----------------------------------------------------------------------------
-
-class BnAudioPolicyServiceClient : public BnInterface<IAudioPolicyServiceClient>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IAUDIOPOLICYSERVICECLIENT_H
diff --git a/media/libaudioclient/include/media/IAudioTrack.h b/media/libaudioclient/include/media/IAudioTrack.h
deleted file mode 100644
index dbbbf35..0000000
--- a/media/libaudioclient/include/media/IAudioTrack.h
+++ /dev/null
@@ -1,125 +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.
- */
-
-#ifndef ANDROID_IAUDIOTRACK_H
-#define ANDROID_IAUDIOTRACK_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/RefBase.h>
-#include <utils/Errors.h>
-#include <binder/IInterface.h>
-#include <binder/IMemory.h>
-#include <utils/String8.h>
-#include <media/AudioTimestamp.h>
-#include <media/VolumeShaper.h>
-#include <system/audio.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-class IAudioTrack : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(AudioTrack);
-
- /* Get this track's control block */
- virtual sp<IMemory> getCblk() const = 0;
-
- /* After it's created the track is not active. Call start() to
- * make it active.
- */
- virtual status_t start() = 0;
-
- /* Stop a track. If set, the callback will cease being called and
- * obtainBuffer will return an error. Buffers that are already released
- * will continue to be processed, unless/until flush() is called.
- */
- virtual void stop() = 0;
-
- /* Flush a stopped or paused track. All pending/released buffers are discarded.
- * This function has no effect if the track is not stopped or paused.
- */
- virtual void flush() = 0;
-
- /* Pause a track. If set, the callback will cease being called and
- * obtainBuffer will return an error. Buffers that are already released
- * will continue to be processed, unless/until flush() is called.
- */
- virtual void pause() = 0;
-
- /* Attach track auxiliary output to specified effect. Use effectId = 0
- * to detach track from effect.
- */
- virtual status_t attachAuxEffect(int effectId) = 0;
-
- /* Send parameters to the audio hardware */
- virtual status_t setParameters(const String8& keyValuePairs) = 0;
-
- /* Selects the presentation (if available) */
- virtual status_t selectPresentation(int presentationId, int programId) = 0;
-
- /* Return NO_ERROR if timestamp is valid. timestamp is undefined otherwise. */
- virtual status_t getTimestamp(AudioTimestamp& timestamp) = 0;
-
- /* Signal the playback thread for a change in control block */
- virtual void signal() = 0;
-
- /* Sets the volume shaper */
- virtual media::VolumeShaper::Status applyVolumeShaper(
- const sp<media::VolumeShaper::Configuration>& configuration,
- const sp<media::VolumeShaper::Operation>& operation) = 0;
-
- /* gets the volume shaper state */
- virtual sp<media::VolumeShaper::State> getVolumeShaperState(int id) = 0;
-
- /* Returns the Dual Mono mode presentation setting. */
- virtual status_t getDualMonoMode(audio_dual_mono_mode_t* mode) = 0;
-
- /* Sets the Dual Mono mode presentation on the output device. */
- virtual status_t setDualMonoMode(audio_dual_mono_mode_t mode) = 0;
-
- /* Returns the Audio Description Mix level in dB. */
- virtual status_t getAudioDescriptionMixLevel(float* leveldB) = 0;
-
- /* Sets the Audio Description Mix level in dB. */
- virtual status_t setAudioDescriptionMixLevel(float leveldB) = 0;
-
- /* Retrieves current playback rate parameters. */
- virtual status_t getPlaybackRateParameters(audio_playback_rate_t* playbackRate) = 0;
-
- /* Sets the playback rate parameters that control playback behavior. */
- virtual status_t setPlaybackRateParameters(const audio_playback_rate_t& playbackRate) = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-class BnAudioTrack : public BnInterface<IAudioTrack>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IAUDIOTRACK_H
diff --git a/media/libaudioclient/include/media/IEffect.h b/media/libaudioclient/include/media/IEffect.h
deleted file mode 100644
index ff04869..0000000
--- a/media/libaudioclient/include/media/IEffect.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_IEFFECT_H
-#define ANDROID_IEFFECT_H
-
-#include <utils/RefBase.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-#include <binder/IMemory.h>
-
-namespace android {
-
-class IEffect: public IInterface
-{
-public:
- DECLARE_META_INTERFACE(Effect);
-
- virtual status_t enable() = 0;
-
- virtual status_t disable() = 0;
-
- virtual status_t command(uint32_t cmdCode,
- uint32_t cmdSize,
- void *pCmdData,
- uint32_t *pReplySize,
- void *pReplyData) = 0;
-
- virtual void disconnect() = 0;
-
- virtual sp<IMemory> getCblk() const = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-class BnEffect: public BnInterface<IEffect>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-}; // namespace android
-
-#endif // ANDROID_IEFFECT_H
diff --git a/media/libaudioclient/include/media/IEffectClient.h b/media/libaudioclient/include/media/IEffectClient.h
deleted file mode 100644
index 2f78c98..0000000
--- a/media/libaudioclient/include/media/IEffectClient.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_IEFFECTCLIENT_H
-#define ANDROID_IEFFECTCLIENT_H
-
-#include <utils/RefBase.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-#include <binder/IMemory.h>
-
-namespace android {
-
-class IEffectClient: public IInterface
-{
-public:
- DECLARE_META_INTERFACE(EffectClient);
-
- virtual void controlStatusChanged(bool controlGranted) = 0;
- virtual void enableStatusChanged(bool enabled) = 0;
- virtual void commandExecuted(uint32_t cmdCode,
- uint32_t cmdSize,
- void *pCmdData,
- uint32_t replySize,
- void *pReplyData) = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-class BnEffectClient: public BnInterface<IEffectClient>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-}; // namespace android
-
-#endif // ANDROID_IEFFECTCLIENT_H
diff --git a/media/libaudioclient/include/media/PlayerBase.h b/media/libaudioclient/include/media/PlayerBase.h
index e7a8abc..23b6bfd 100644
--- a/media/libaudioclient/include/media/PlayerBase.h
+++ b/media/libaudioclient/include/media/PlayerBase.h
@@ -19,6 +19,7 @@
#include <audiomanager/AudioManager.h>
#include <audiomanager/IAudioManager.h>
+#include <utils/Mutex.h>
#include "android/media/BnPlayer.h"
@@ -40,19 +41,21 @@
virtual binder::Status setPan(float pan) override;
virtual binder::Status setStartDelayMs(int32_t delayMs) override;
virtual binder::Status applyVolumeShaper(
- const media::VolumeShaper::Configuration& configuration,
- const media::VolumeShaper::Operation& operation) override;
+ const media::VolumeShaperConfiguration& configuration,
+ const media::VolumeShaperOperation& operation) override;
- status_t startWithStatus();
+ status_t startWithStatus(audio_port_handle_t deviceId);
status_t pauseWithStatus();
status_t stopWithStatus();
//FIXME temporary method while some player state is outside of this class
- void reportEvent(player_state_t event);
+ void reportEvent(player_state_t event, audio_port_handle_t deviceId);
+
+ void baseUpdateDeviceId(audio_port_handle_t deviceId);
protected:
- void init(player_type_t playerType, audio_usage_t usage);
+ void init(player_type_t playerType, audio_usage_t usage, audio_session_t sessionId);
void baseDestroy();
//IPlayer methods handlers for derived classes
@@ -68,20 +71,24 @@
float mPanMultiplierL, mPanMultiplierR;
float mVolumeMultiplierL, mVolumeMultiplierR;
+ // player interface ID, uniquely identifies the player in the system
+ // effectively const after PlayerBase::init().
+ audio_unique_id_t mPIId;
+
private:
// report events to AudioService
- void servicePlayerEvent(player_state_t event);
+ void servicePlayerEvent(player_state_t event, audio_port_handle_t deviceId);
void serviceReleasePlayer();
// native interface to AudioService
android::sp<android::IAudioManager> mAudioManager;
- // player interface ID, uniquely identifies the player in the system
- audio_unique_id_t mPIId;
-
// Mutex for state reporting
Mutex mPlayerStateLock;
player_state_t mLastReportedEvent;
+
+ Mutex mDeviceIdLock;
+ audio_port_handle_t mLastReportedDeviceId;
};
} // namespace android
diff --git a/media/libaudioclient/include/media/PolicyAidlConversion.h b/media/libaudioclient/include/media/PolicyAidlConversion.h
new file mode 100644
index 0000000..873f27a
--- /dev/null
+++ b/media/libaudioclient/include/media/PolicyAidlConversion.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <limits>
+#include <type_traits>
+
+#include <system/audio.h>
+
+#include <android/media/AudioMix.h>
+#include <android/media/AudioMixCallbackFlag.h>
+#include <android/media/AudioMixLatencyClass.h>
+#include <android/media/AudioMixRouteFlag.h>
+#include <android/media/AudioMixType.h>
+#include <android/media/AudioMode.h>
+#include <android/media/AudioOffloadMode.h>
+#include <android/media/AudioPolicyForceUse.h>
+#include <android/media/AudioPolicyForcedConfig.h>
+#include <android/media/DeviceRole.h>
+
+#include <media/AidlConversionUtil.h>
+#include <media/AudioCommonTypes.h>
+#include <media/AudioPolicy.h>
+#include <android/media/AudioPolicyDeviceState.h>
+
+namespace android {
+
+ConversionResult<volume_group_t>
+aidl2legacy_int32_t_volume_group_t(int32_t aidl);
+ConversionResult<int32_t>
+legacy2aidl_volume_group_t_int32_t(volume_group_t legacy);
+
+ConversionResult<product_strategy_t>
+aidl2legacy_int32_t_product_strategy_t(int32_t aidl);
+ConversionResult<int32_t>
+legacy2aidl_product_strategy_t_int32_t(product_strategy_t legacy);
+
+ConversionResult<uint32_t>
+aidl2legacy_AudioMixType_uint32_t(media::AudioMixType aidl);
+ConversionResult<media::AudioMixType>
+legacy2aidl_uint32_t_AudioMixType(uint32_t legacy);
+
+ConversionResult<uint32_t>
+aidl2legacy_AudioMixCallbackFlag_uint32_t(media::AudioMixCallbackFlag aidl);
+ConversionResult<media::AudioMixCallbackFlag>
+legacy2aidl_uint32_t_AudioMixCallbackFlag(uint32_t legacy);
+
+ConversionResult<uint32_t>
+aidl2legacy_AudioMixCallbackFlag_uint32_t_mask(int32_t aidl);
+ConversionResult<int32_t>
+legacy2aidl_uint32_t_AudioMixCallbackFlag_mask(uint32_t legacy);
+
+ConversionResult<uint32_t>
+aidl2legacy_AudioMixRouteFlag_uint32_t(media::AudioMixRouteFlag aidl);
+ConversionResult<media::AudioMixRouteFlag>
+legacy2aidl_uint32_t_AudioMixRouteFlag(uint32_t legacy);
+
+ConversionResult<uint32_t>
+aidl2legacy_AudioMixRouteFlag_uint32_t_mask(int32_t aidl);
+ConversionResult<int32_t>
+legacy2aidl_uint32_t_AudioMixRouteFlag_mask(uint32_t legacy);
+
+ConversionResult<AudioMixMatchCriterion>
+aidl2legacy_AudioMixMatchCriterion(const media::AudioMixMatchCriterion& aidl);
+ConversionResult<media::AudioMixMatchCriterion>
+legacy2aidl_AudioMixMatchCriterion(const AudioMixMatchCriterion& legacy);
+
+ConversionResult<AudioMix>
+aidl2legacy_AudioMix(const media::AudioMix& aidl);
+ConversionResult<media::AudioMix>
+legacy2aidl_AudioMix(const AudioMix& legacy);
+
+ConversionResult<audio_policy_dev_state_t>
+aidl2legacy_AudioPolicyDeviceState_audio_policy_dev_state_t(media::AudioPolicyDeviceState aidl);
+ConversionResult<media::AudioPolicyDeviceState>
+legacy2aidl_audio_policy_dev_state_t_AudioPolicyDeviceState(audio_policy_dev_state_t legacy);
+
+ConversionResult<audio_policy_force_use_t>
+aidl2legacy_AudioPolicyForceUse_audio_policy_force_use_t(media::AudioPolicyForceUse aidl);
+ConversionResult<media::AudioPolicyForceUse>
+legacy2aidl_audio_policy_force_use_t_AudioPolicyForceUse(audio_policy_force_use_t legacy);
+
+ConversionResult<audio_policy_forced_cfg_t>
+aidl2legacy_AudioPolicyForcedConfig_audio_policy_forced_cfg_t(media::AudioPolicyForcedConfig aidl);
+ConversionResult<media::AudioPolicyForcedConfig>
+legacy2aidl_audio_policy_forced_cfg_t_AudioPolicyForcedConfig(audio_policy_forced_cfg_t legacy);
+
+ConversionResult<device_role_t>
+aidl2legacy_DeviceRole_device_role_t(media::DeviceRole aidl);
+ConversionResult<media::DeviceRole>
+legacy2aidl_device_role_t_DeviceRole(device_role_t legacy);
+
+ConversionResult<audio_offload_mode_t>
+aidl2legacy_AudioOffloadMode_audio_offload_mode_t(media::AudioOffloadMode aidl);
+ConversionResult<media::AudioOffloadMode>
+legacy2aidl_audio_offload_mode_t_AudioOffloadMode(audio_offload_mode_t legacy);
+
+} // namespace android
diff --git a/media/libaudioclient/include/media/ToneGenerator.h b/media/libaudioclient/include/media/ToneGenerator.h
index 04357a8..a575616 100644
--- a/media/libaudioclient/include/media/ToneGenerator.h
+++ b/media/libaudioclient/include/media/ToneGenerator.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_TONEGENERATOR_H_
#define ANDROID_TONEGENERATOR_H_
+#include <string>
+
#include <media/AudioSystem.h>
#include <media/AudioTrack.h>
#include <utils/Compat.h>
@@ -152,7 +154,8 @@
NUM_SUP_TONES = LAST_SUP_TONE-FIRST_SUP_TONE+1
};
- ToneGenerator(audio_stream_type_t streamType, float volume, bool threadCanCallJava = false);
+ ToneGenerator(audio_stream_type_t streamType, float volume, bool threadCanCallJava = false,
+ std::string opPackageName = {});
~ToneGenerator();
bool startTone(tone_type toneType, int durationMs = -1);
@@ -193,6 +196,7 @@
TONE_JAPAN_DIAL, // Dial tone: 400Hz, continuous
TONE_JAPAN_BUSY, // Busy tone: 400Hz, 500ms ON, 500ms OFF...
TONE_JAPAN_RADIO_ACK, // Radio path acknowlegment: 400Hz, 1s ON, 2s OFF...
+ TONE_JAPAN_RINGTONE, // Ring Tone: 400 Hz repeated in a 1 s on, 2 s off pattern.
// GB Supervisory tones
TONE_GB_BUSY, // Busy tone: 400 Hz, 375ms ON, 375ms OFF...
TONE_GB_CONGESTION, // Congestion Tone: 400 Hz, 400ms ON, 350ms OFF, 225ms ON, 525ms OFF...
@@ -343,6 +347,8 @@
};
KeyedVector<uint16_t, WaveGenerator *> mWaveGens; // list of active wave generators.
+
+ std::string mOpPackageName;
};
}
diff --git a/media/libaudioclient/include/media/TrackPlayerBase.h b/media/libaudioclient/include/media/TrackPlayerBase.h
index 66e9b3b..80124b8 100644
--- a/media/libaudioclient/include/media/TrackPlayerBase.h
+++ b/media/libaudioclient/include/media/TrackPlayerBase.h
@@ -28,13 +28,14 @@
explicit TrackPlayerBase();
virtual ~TrackPlayerBase();
- void init(AudioTrack* pat, player_type_t playerType, audio_usage_t usage);
+ void init(AudioTrack* pat, player_type_t playerType, audio_usage_t usage,
+ audio_session_t sessionId);
virtual void destroy();
//IPlayer implementation
virtual binder::Status applyVolumeShaper(
- const media::VolumeShaper::Configuration& configuration,
- const media::VolumeShaper::Operation& operation);
+ const media::VolumeShaperConfiguration& configuration,
+ const media::VolumeShaperOperation& operation);
//FIXME move to protected field, so far made public to minimize changes to AudioTrack logic
sp<AudioTrack> mAudioTrack;
@@ -53,8 +54,20 @@
void doDestroy();
status_t doSetVolume();
+ class SelfAudioDeviceCallback : public AudioSystem::AudioDeviceCallback {
+ public:
+ SelfAudioDeviceCallback(PlayerBase& self);
+ virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
+ audio_port_handle_t deviceId);
+ private:
+ virtual ~SelfAudioDeviceCallback();
+ PlayerBase& mSelf;
+ };
+
// volume coming from the player volume API
float mPlayerVolumeL, mPlayerVolumeR;
+
+ sp<SelfAudioDeviceCallback> mSelfAudioDeviceCallback;
};
} // namespace android
diff --git a/media/libaudioclient/tests/Android.bp b/media/libaudioclient/tests/Android.bp
index 92c6ec0..3c1da4d 100644
--- a/media/libaudioclient/tests/Android.bp
+++ b/media/libaudioclient/tests/Android.bp
@@ -16,6 +16,18 @@
}
cc_test {
+ name: "audio_aidl_status_tests",
+ defaults: ["libaudioclient_tests_defaults"],
+ srcs: ["audio_aidl_status_tests.cpp"],
+ shared_libs: [
+ "libaudioclient_aidl_conversion",
+ "libbinder",
+ "libcutils",
+ "libutils",
+ ],
+}
+
+cc_test {
name: "test_create_audiotrack",
defaults: ["libaudioclient_tests_defaults"],
srcs: ["test_create_audiotrack.cpp",
diff --git a/media/libaudioclient/tests/audio_aidl_status_tests.cpp b/media/libaudioclient/tests/audio_aidl_status_tests.cpp
new file mode 100644
index 0000000..5517091
--- /dev/null
+++ b/media/libaudioclient/tests/audio_aidl_status_tests.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <media/AidlConversionUtil.h>
+#include <utils/Errors.h>
+
+using namespace android;
+using namespace android::aidl_utils;
+using android::binder::Status;
+
+// Tests for statusTFromBinderStatus() and binderStatusFromStatusT().
+
+// STATUS_T_SMALL_VALUE_LIMIT is an arbitrary limit where we exhaustively check status_t errors.
+// It is known that this limit doesn't cover UNKNOWN_ERROR ~ INT32_MIN.
+constexpr status_t STATUS_T_SMALL_VALUE_LIMIT = -1000;
+
+// Small status values are preserved on round trip
+TEST(audio_aidl_status_tests, statusRoundTripSmallValues) {
+ for (status_t status = 0; status > STATUS_T_SMALL_VALUE_LIMIT; --status) {
+ ASSERT_EQ(status, statusTFromBinderStatus(binderStatusFromStatusT(status)));
+ }
+}
+
+// Special status values are preserved on round trip.
+TEST(audio_aidl_status_tests, statusRoundTripSpecialValues) {
+ for (status_t status : {
+ OK,
+ UNKNOWN_ERROR,
+ NO_MEMORY,
+ INVALID_OPERATION,
+ BAD_VALUE,
+ BAD_TYPE,
+ NAME_NOT_FOUND,
+ PERMISSION_DENIED,
+ NO_INIT,
+ ALREADY_EXISTS,
+ DEAD_OBJECT,
+ FAILED_TRANSACTION,
+ BAD_INDEX,
+ NOT_ENOUGH_DATA,
+ WOULD_BLOCK,
+ TIMED_OUT,
+ UNKNOWN_TRANSACTION,
+ FDS_NOT_ALLOWED}) {
+ ASSERT_EQ(status, statusTFromBinderStatus(binderStatusFromStatusT(status)));
+ }
+}
+
+// Binder exceptions show as an error (not fixed at this time); these come fromExceptionCode().
+TEST(audio_aidl_status_tests, binderStatusExceptions) {
+ for (int exceptionCode : {
+ //Status::EX_NONE,
+ Status::EX_SECURITY,
+ Status::EX_BAD_PARCELABLE,
+ Status::EX_ILLEGAL_ARGUMENT,
+ Status::EX_NULL_POINTER,
+ Status::EX_ILLEGAL_STATE,
+ Status::EX_NETWORK_MAIN_THREAD,
+ Status::EX_UNSUPPORTED_OPERATION,
+ //Status::EX_SERVICE_SPECIFIC, -- tested fromServiceSpecificError()
+ Status::EX_PARCELABLE,
+ // This is special and Java specific; see Parcel.java.
+ Status::EX_HAS_REPLY_HEADER,
+ // This is special, and indicates to C++ binder proxies that the
+ // transaction has failed at a low level.
+ //Status::EX_TRANSACTION_FAILED, -- tested fromStatusT().
+ }) {
+ ASSERT_NE(OK, statusTFromBinderStatus(Status::fromExceptionCode(exceptionCode)));
+ }
+}
+
+// Binder transaction errors show exactly in status_t; these come fromStatusT().
+TEST(audio_aidl_status_tests, binderStatusTransactionError) {
+ for (status_t status : {
+ OK, // Note: fromStatusT does check if this is 0, so this is no error.
+ UNKNOWN_ERROR,
+ NO_MEMORY,
+ INVALID_OPERATION,
+ BAD_VALUE,
+ BAD_TYPE,
+ NAME_NOT_FOUND,
+ PERMISSION_DENIED,
+ NO_INIT,
+ ALREADY_EXISTS,
+ DEAD_OBJECT,
+ FAILED_TRANSACTION,
+ BAD_INDEX,
+ NOT_ENOUGH_DATA,
+ WOULD_BLOCK,
+ TIMED_OUT,
+ UNKNOWN_TRANSACTION,
+ FDS_NOT_ALLOWED}) {
+ ASSERT_EQ(status, statusTFromBinderStatus(Status::fromStatusT(status)));
+ }
+}
+
+// Binder service specific errors show in status_t; these come fromServiceSpecificError().
+TEST(audio_aidl_status_tests, binderStatusServiceSpecificError) {
+ // fromServiceSpecificError() still stores exception code if status is 0.
+ for (status_t status = -1; status > STATUS_T_SMALL_VALUE_LIMIT; --status) {
+ ASSERT_EQ(status, statusTFromBinderStatus(Status::fromServiceSpecificError(status)));
+ }
+}
+
+// Binder status with message.
+TEST(audio_aidl_status_tests, binderStatusMessage) {
+ const String8 message("abcd");
+ for (status_t status = -1; status > STATUS_T_SMALL_VALUE_LIMIT; --status) {
+ const Status binderStatus = binderStatusFromStatusT(status, message.c_str());
+ ASSERT_EQ(status, statusTFromBinderStatus(binderStatus));
+ ASSERT_EQ(message, binderStatus.exceptionMessage());
+ }
+}
diff --git a/media/libaudioclient/tests/test_create_audiorecord.cpp b/media/libaudioclient/tests/test_create_audiorecord.cpp
index cf6a734..57676c1 100644
--- a/media/libaudioclient/tests/test_create_audiorecord.cpp
+++ b/media/libaudioclient/tests/test_create_audiorecord.cpp
@@ -19,6 +19,7 @@
#include <string.h>
#include <unistd.h>
+#include <android/media/permission/Identity.h>
#include <binder/MemoryBase.h>
#include <binder/MemoryDealer.h>
#include <binder/MemoryHeapBase.h>
@@ -32,19 +33,24 @@
namespace android {
+using media::permission::Identity;
+
int testRecord(FILE *inputFile, int outputFileFd)
{
char line[MAX_INPUT_FILE_LINE_LENGTH];
uint32_t testCount = 0;
Vector<String16> args;
int ret = 0;
+ // TODO b/182392769: use identity util
+ Identity identity;
+ identity.packageName = PACKAGE_NAME;
if (inputFile == nullptr) {
sp<AudioRecord> record = new AudioRecord(AUDIO_SOURCE_DEFAULT,
0 /* sampleRate */,
AUDIO_FORMAT_DEFAULT,
AUDIO_CHANNEL_IN_MONO,
- String16(PACKAGE_NAME));
+ identity);
if (record == 0 || record->initCheck() != NO_ERROR) {
write(outputFileFd, "Error creating AudioRecord\n",
sizeof("Error creating AudioRecord\n"));
@@ -90,7 +96,7 @@
memset(&attributes, 0, sizeof(attributes));
attributes.source = inputSource;
- sp<AudioRecord> record = new AudioRecord(String16(PACKAGE_NAME));
+ sp<AudioRecord> record = new AudioRecord(identity);
record->set(AUDIO_SOURCE_DEFAULT,
sampleRate,
diff --git a/media/libaudiofoundation/Android.bp b/media/libaudiofoundation/Android.bp
index b2fa007..3bef55b 100644
--- a/media/libaudiofoundation/Android.bp
+++ b/media/libaudiofoundation/Android.bp
@@ -14,13 +14,21 @@
export_include_dirs: ["include"],
header_libs: [
+ "libaudioclient_aidl_conversion_util",
"libaudio_system_headers",
"libmedia_helper_headers",
],
export_header_lib_headers: [
+ "libaudioclient_aidl_conversion_util",
"libaudio_system_headers",
"libmedia_helper_headers",
],
+ static_libs: [
+ "audioclient-types-aidl-cpp",
+ ],
+ export_static_lib_headers: [
+ "audioclient-types-aidl-cpp",
+ ],
host_supported: true,
target: {
darwin: {
@@ -44,6 +52,8 @@
],
shared_libs: [
+ "audioclient-types-aidl-cpp",
+ "libaudioclient_aidl_conversion",
"libaudioutils",
"libbase",
"libbinder",
@@ -52,6 +62,11 @@
"libutils",
],
+ export_shared_lib_headers: [
+ "audioclient-types-aidl-cpp",
+ "libaudioclient_aidl_conversion",
+ ],
+
header_libs: [
"libaudiofoundation_headers",
],
diff --git a/media/libaudiofoundation/AudioDeviceTypeAddr.cpp b/media/libaudiofoundation/AudioDeviceTypeAddr.cpp
index a47337b..c5d7da8 100644
--- a/media/libaudiofoundation/AudioDeviceTypeAddr.cpp
+++ b/media/libaudiofoundation/AudioDeviceTypeAddr.cpp
@@ -15,13 +15,14 @@
*/
#include <media/AudioDeviceTypeAddr.h>
-
#include <arpa/inet.h>
#include <iostream>
#include <regex>
#include <set>
#include <sstream>
+#include <media/AidlConversion.h>
+
namespace android {
namespace {
@@ -155,4 +156,18 @@
return stream.str();
}
+ConversionResult<AudioDeviceTypeAddr>
+aidl2legacy_AudioDeviceTypeAddress(const media::AudioDevice& aidl) {
+ audio_devices_t type = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_devices_t(aidl.type));
+ return AudioDeviceTypeAddr(type, aidl.address);
+}
+
+ConversionResult<media::AudioDevice>
+legacy2aidl_AudioDeviceTypeAddress(const AudioDeviceTypeAddr& legacy) {
+ media::AudioDevice aidl;
+ aidl.type = VALUE_OR_RETURN(legacy2aidl_audio_devices_t_int32_t(legacy.mType));
+ aidl.address = legacy.getAddress();
+ return aidl;
+}
+
} // namespace android
diff --git a/media/libaudiofoundation/AudioGain.cpp b/media/libaudiofoundation/AudioGain.cpp
index 759140e..1dee938 100644
--- a/media/libaudiofoundation/AudioGain.cpp
+++ b/media/libaudiofoundation/AudioGain.cpp
@@ -129,42 +129,51 @@
mGain.max_ramp_ms == other->mGain.max_ramp_ms;
}
-status_t AudioGain::writeToParcel(android::Parcel *parcel) const
-{
- status_t status = NO_ERROR;
- if ((status = parcel->writeInt32(mIndex)) != NO_ERROR) return status;
- if ((status = parcel->writeBool(mUseInChannelMask)) != NO_ERROR) return status;
- if ((status = parcel->writeBool(mUseForVolume)) != NO_ERROR) return status;
- if ((status = parcel->writeUint32(mGain.mode)) != NO_ERROR) return status;
- if ((status = parcel->writeUint32(mGain.channel_mask)) != NO_ERROR) return status;
- if ((status = parcel->writeInt32(mGain.min_value)) != NO_ERROR) return status;
- if ((status = parcel->writeInt32(mGain.max_value)) != NO_ERROR) return status;
- if ((status = parcel->writeInt32(mGain.default_value)) != NO_ERROR) return status;
- if ((status = parcel->writeUint32(mGain.step_value)) != NO_ERROR) return status;
- if ((status = parcel->writeUint32(mGain.min_ramp_ms)) != NO_ERROR) return status;
- status = parcel->writeUint32(mGain.max_ramp_ms);
- return status;
+status_t AudioGain::writeToParcel(android::Parcel *parcel) const {
+ media::AudioGain parcelable;
+ return writeToParcelable(&parcelable)
+ ?: parcelable.writeToParcel(parcel);
}
-status_t AudioGain::readFromParcel(const android::Parcel *parcel)
-{
- status_t status = NO_ERROR;
- if ((status = parcel->readInt32(&mIndex)) != NO_ERROR) return status;
- if ((status = parcel->readBool(&mUseInChannelMask)) != NO_ERROR) return status;
- if ((status = parcel->readBool(&mUseForVolume)) != NO_ERROR) return status;
- uint32_t rawGainMode;
- if ((status = parcel->readUint32(&rawGainMode)) != NO_ERROR) return status;
- mGain.mode = static_cast<audio_gain_mode_t>(rawGainMode);
- uint32_t rawChannelMask;
- if ((status = parcel->readUint32(&rawChannelMask)) != NO_ERROR) return status;
- mGain.channel_mask = static_cast<audio_channel_mask_t>(rawChannelMask);
- if ((status = parcel->readInt32(&mGain.min_value)) != NO_ERROR) return status;
- if ((status = parcel->readInt32(&mGain.max_value)) != NO_ERROR) return status;
- if ((status = parcel->readInt32(&mGain.default_value)) != NO_ERROR) return status;
- if ((status = parcel->readUint32(&mGain.step_value)) != NO_ERROR) return status;
- if ((status = parcel->readUint32(&mGain.min_ramp_ms)) != NO_ERROR) return status;
- status = parcel->readUint32(&mGain.max_ramp_ms);
- return status;
+status_t AudioGain::writeToParcelable(media::AudioGain* parcelable) const {
+ parcelable->index = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(mIndex));
+ parcelable->useInChannelMask = mUseInChannelMask;
+ parcelable->useForVolume = mUseForVolume;
+ parcelable->mode = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_gain_mode_t_int32_t_mask(mGain.mode));
+ parcelable->channelMask = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_channel_mask_t_int32_t(mGain.channel_mask));
+ parcelable->minValue = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(mGain.min_value));
+ parcelable->maxValue = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(mGain.max_value));
+ parcelable->defaultValue = VALUE_OR_RETURN_STATUS(
+ convertIntegral<int32_t>(mGain.default_value));
+ parcelable->stepValue = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(mGain.step_value));
+ parcelable->minRampMs = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(mGain.min_ramp_ms));
+ parcelable->maxRampMs = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(mGain.max_ramp_ms));
+ return OK;
+}
+
+status_t AudioGain::readFromParcel(const android::Parcel *parcel) {
+ media::AudioGain parcelable;
+ return parcelable.readFromParcel(parcel)
+ ?: readFromParcelable(parcelable);
+}
+
+status_t AudioGain::readFromParcelable(const media::AudioGain& parcelable) {
+ mIndex = VALUE_OR_RETURN_STATUS(convertIntegral<int>(parcelable.index));
+ mUseInChannelMask = parcelable.useInChannelMask;
+ mUseForVolume = parcelable.useForVolume;
+ mGain.mode = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_int32_t_audio_gain_mode_t_mask(parcelable.mode));
+ mGain.channel_mask = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_int32_t_audio_channel_mask_t(parcelable.channelMask));
+ mGain.min_value = VALUE_OR_RETURN_STATUS(convertIntegral<int>(parcelable.minValue));
+ mGain.max_value = VALUE_OR_RETURN_STATUS(convertIntegral<int>(parcelable.maxValue));
+ mGain.default_value = VALUE_OR_RETURN_STATUS(convertIntegral<int>(parcelable.defaultValue));
+ mGain.step_value = VALUE_OR_RETURN_STATUS(convertIntegral<unsigned int>(parcelable.stepValue));
+ mGain.min_ramp_ms = VALUE_OR_RETURN_STATUS(convertIntegral<unsigned int>(parcelable.minRampMs));
+ mGain.max_ramp_ms = VALUE_OR_RETURN_STATUS(convertIntegral<unsigned int>(parcelable.maxRampMs));
+ return OK;
}
bool AudioGains::equals(const AudioGains &other) const
@@ -200,4 +209,34 @@
return status;
}
+ConversionResult<sp<AudioGain>>
+aidl2legacy_AudioGain(const media::AudioGain& aidl) {
+ sp<AudioGain> legacy = new AudioGain(0, false);
+ status_t status = legacy->readFromParcelable(aidl);
+ if (status != OK) {
+ return base::unexpected(status);
+ }
+ return legacy;
+}
+
+ConversionResult<media::AudioGain>
+legacy2aidl_AudioGain(const sp<AudioGain>& legacy) {
+ media::AudioGain aidl;
+ status_t status = legacy->writeToParcelable(&aidl);
+ if (status != OK) {
+ return base::unexpected(status);
+ }
+ return aidl;
+}
+
+ConversionResult<AudioGains>
+aidl2legacy_AudioGains(const std::vector<media::AudioGain>& aidl) {
+ return convertContainer<AudioGains>(aidl, aidl2legacy_AudioGain);
+}
+
+ConversionResult<std::vector<media::AudioGain>>
+legacy2aidl_AudioGains(const AudioGains& legacy) {
+ return convertContainer<std::vector<media::AudioGain>>(legacy, legacy2aidl_AudioGain);
+}
+
} // namespace android
diff --git a/media/libaudiofoundation/AudioPort.cpp b/media/libaudiofoundation/AudioPort.cpp
index 1846a6b..fafabd9 100644
--- a/media/libaudiofoundation/AudioPort.cpp
+++ b/media/libaudiofoundation/AudioPort.cpp
@@ -16,7 +16,9 @@
#define LOG_TAG "AudioPort"
#include <algorithm>
+#include <utility>
+#include <android/media/ExtraAudioDescriptor.h>
#include <android-base/stringprintf.h>
#include <media/AudioPort.h>
#include <utils/Log.h>
@@ -38,6 +40,36 @@
}
}
+void AudioPort::importAudioPort(const audio_port_v7 &port) {
+ for (size_t i = 0; i < port.num_audio_profiles; ++i) {
+ sp<AudioProfile> profile = new AudioProfile(port.audio_profiles[i].format,
+ ChannelMaskSet(port.audio_profiles[i].channel_masks,
+ port.audio_profiles[i].channel_masks +
+ port.audio_profiles->num_channel_masks),
+ SampleRateSet(port.audio_profiles[i].sample_rates,
+ port.audio_profiles[i].sample_rates +
+ port.audio_profiles[i].num_sample_rates),
+ port.audio_profiles[i].encapsulation_type);
+ if (!mProfiles.contains(profile)) {
+ addAudioProfile(profile);
+ }
+ }
+
+ for (size_t i = 0; i < port.num_extra_audio_descriptors; ++i) {
+ auto convertedResult = legacy2aidl_audio_extra_audio_descriptor_ExtraAudioDescriptor(
+ port.extra_audio_descriptors[i]);
+ if (!convertedResult.ok()) {
+ ALOGE("%s, failed to convert extra audio descriptor", __func__);
+ continue;
+ }
+ if (std::find(mExtraAudioDescriptors.begin(),
+ mExtraAudioDescriptors.end(),
+ convertedResult.value()) == mExtraAudioDescriptors.end()) {
+ mExtraAudioDescriptors.push_back(std::move(convertedResult.value()));
+ }
+ }
+}
+
void AudioPort::toAudioPort(struct audio_port *port) const {
// TODO: update this function once audio_port structure reflects the new profile definition.
// For compatibility reason: flatening the AudioProfile into audio_port structure.
@@ -62,21 +94,56 @@
}
}
}
- port->role = mRole;
- port->type = mType;
- strlcpy(port->name, mName.c_str(), AUDIO_PORT_MAX_NAME_LEN);
+ toAudioPortBase(port);
port->num_sample_rates = flatenedRates.size();
port->num_channel_masks = flatenedChannels.size();
port->num_formats = flatenedFormats.size();
std::copy(flatenedRates.begin(), flatenedRates.end(), port->sample_rates);
std::copy(flatenedChannels.begin(), flatenedChannels.end(), port->channel_masks);
std::copy(flatenedFormats.begin(), flatenedFormats.end(), port->formats);
+}
- ALOGV("AudioPort::toAudioPort() num gains %zu", mGains.size());
+void AudioPort::toAudioPort(struct audio_port_v7 *port) const {
+ toAudioPortBase(port);
+ port->num_audio_profiles = 0;
+ for (const auto& profile : mProfiles) {
+ if (profile->isValid()) {
+ const SampleRateSet &sampleRates = profile->getSampleRates();
+ const ChannelMaskSet &channelMasks = profile->getChannels();
- port->num_gains = std::min(mGains.size(), (size_t) AUDIO_PORT_MAX_GAINS);
- for (size_t i = 0; i < port->num_gains; i++) {
- port->gains[i] = mGains[i]->getGain();
+ if (sampleRates.size() > AUDIO_PORT_MAX_SAMPLING_RATES ||
+ channelMasks.size() > AUDIO_PORT_MAX_CHANNEL_MASKS ||
+ port->num_audio_profiles >= AUDIO_PORT_MAX_AUDIO_PROFILES) {
+ ALOGE("%s: bailing out: cannot export profiles to port config", __func__);
+ break;
+ }
+
+ auto& dstProfile = port->audio_profiles[port->num_audio_profiles++];
+ dstProfile.format = profile->getFormat();
+ dstProfile.num_sample_rates = sampleRates.size();
+ std::copy(sampleRates.begin(), sampleRates.end(),
+ std::begin(dstProfile.sample_rates));
+ dstProfile.num_channel_masks = channelMasks.size();
+ std::copy(channelMasks.begin(), channelMasks.end(),
+ std::begin(dstProfile.channel_masks));
+ dstProfile.encapsulation_type = profile->getEncapsulationType();
+ }
+ }
+
+ port->num_extra_audio_descriptors = 0;
+ for (const auto& desc : mExtraAudioDescriptors) {
+ if (port->num_extra_audio_descriptors >= AUDIO_PORT_MAX_EXTRA_AUDIO_DESCRIPTORS) {
+ ALOGE("%s: bailing out: cannot export extra audio descriptor to port config", __func__);
+ return;
+ }
+
+ auto convertedResult = aidl2legacy_ExtraAudioDescriptor_audio_extra_audio_descriptor(desc);
+ if (!convertedResult.ok()) {
+ ALOGE("%s: failed to convert extra audio descriptor", __func__);
+ continue;
+ }
+ port->extra_audio_descriptors[port->num_extra_audio_descriptors++] =
+ std::move(convertedResult.value());
}
}
@@ -88,6 +155,22 @@
std::string profilesStr;
mProfiles.dump(&profilesStr, spaces);
dst->append(profilesStr);
+ if (!mExtraAudioDescriptors.empty()) {
+ dst->append(base::StringPrintf("%*s- extra audio descriptors: \n", spaces, ""));
+ const int eadSpaces = spaces + 4;
+ const int descSpaces = eadSpaces + 4;
+ for (size_t i = 0; i < mExtraAudioDescriptors.size(); i++) {
+ dst->append(
+ base::StringPrintf("%*s extra audio descriptor %zu:\n", eadSpaces, "", i));
+ dst->append(base::StringPrintf(
+ "%*s- standard: %u\n", descSpaces, "", mExtraAudioDescriptors[i].standard));
+ dst->append(base::StringPrintf("%*s- descriptor:", descSpaces, ""));
+ for (auto v : mExtraAudioDescriptors[i].audioDescriptor) {
+ dst->append(base::StringPrintf(" %02x", v));
+ }
+ dst->append("\n");
+ }
+ }
if (mGains.size() != 0) {
dst->append(base::StringPrintf("%*s- gains:\n", spaces, ""));
@@ -112,37 +195,41 @@
mName.compare(other->getName()) == 0 &&
mType == other->getType() &&
mRole == other->getRole() &&
- mProfiles.equals(other->getAudioProfiles());
+ mProfiles.equals(other->getAudioProfiles()) &&
+ mExtraAudioDescriptors == other->getExtraAudioDescriptors();
}
status_t AudioPort::writeToParcel(Parcel *parcel) const
{
- status_t status = NO_ERROR;
- if ((status = parcel->writeUtf8AsUtf16(mName)) != NO_ERROR) return status;
- if ((status = parcel->writeUint32(mType)) != NO_ERROR) return status;
- if ((status = parcel->writeUint32(mRole)) != NO_ERROR) return status;
- if ((status = parcel->writeParcelable(mProfiles)) != NO_ERROR) return status;
- if ((status = parcel->writeParcelable(mGains)) != NO_ERROR) return status;
- return status;
+ media::AudioPort parcelable;
+ return writeToParcelable(&parcelable)
+ ?: parcelable.writeToParcel(parcel);
}
-status_t AudioPort::readFromParcel(const Parcel *parcel)
-{
- status_t status = NO_ERROR;
- if ((status = parcel->readUtf8FromUtf16(&mName)) != NO_ERROR) return status;
- static_assert(sizeof(mType) == sizeof(uint32_t));
- if ((status = parcel->readUint32(reinterpret_cast<uint32_t*>(&mType))) != NO_ERROR) {
- return status;
- }
- static_assert(sizeof(mRole) == sizeof(uint32_t));
- if ((status = parcel->readUint32(reinterpret_cast<uint32_t*>(&mRole))) != NO_ERROR) {
- return status;
- }
- mProfiles.clear();
- if ((status = parcel->readParcelable(&mProfiles)) != NO_ERROR) return status;
- mGains.clear();
- if ((status = parcel->readParcelable(&mGains)) != NO_ERROR) return status;
- return status;
+status_t AudioPort::writeToParcelable(media::AudioPort* parcelable) const {
+ parcelable->name = mName;
+ parcelable->type = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_port_type_t_AudioPortType(mType));
+ parcelable->role = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_port_role_t_AudioPortRole(mRole));
+ parcelable->profiles = VALUE_OR_RETURN_STATUS(legacy2aidl_AudioProfileVector(mProfiles));
+ parcelable->extraAudioDescriptors = mExtraAudioDescriptors;
+ parcelable->gains = VALUE_OR_RETURN_STATUS(legacy2aidl_AudioGains(mGains));
+ return OK;
+}
+
+status_t AudioPort::readFromParcel(const Parcel *parcel) {
+ media::AudioPort parcelable;
+ return parcelable.readFromParcel(parcel)
+ ?: readFromParcelable(parcelable);
+}
+
+status_t AudioPort::readFromParcelable(const media::AudioPort& parcelable) {
+ mName = parcelable.name;
+ mType = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioPortType_audio_port_type_t(parcelable.type));
+ mRole = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioPortRole_audio_port_role_t(parcelable.role));
+ mProfiles = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioProfileVector(parcelable.profiles));
+ mExtraAudioDescriptors = parcelable.extraAudioDescriptors;
+ mGains = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioGains(parcelable.gains));
+ return OK;
}
// --- AudioPortConfig class implementation
@@ -243,50 +330,56 @@
mGain.ramp_duration_ms == other->mGain.ramp_duration_ms;
}
-status_t AudioPortConfig::writeToParcel(Parcel *parcel) const
-{
- status_t status = NO_ERROR;
- if ((status = parcel->writeUint32(mSamplingRate)) != NO_ERROR) return status;
- if ((status = parcel->writeUint32(mFormat)) != NO_ERROR) return status;
- if ((status = parcel->writeUint32(mChannelMask)) != NO_ERROR) return status;
- if ((status = parcel->writeInt32(mId)) != NO_ERROR) return status;
- // Write mGain to parcel.
- if ((status = parcel->writeInt32(mGain.index)) != NO_ERROR) return status;
- if ((status = parcel->writeUint32(mGain.mode)) != NO_ERROR) return status;
- if ((status = parcel->writeUint32(mGain.channel_mask)) != NO_ERROR) return status;
- if ((status = parcel->writeUint32(mGain.ramp_duration_ms)) != NO_ERROR) return status;
- std::vector<int> values(std::begin(mGain.values), std::end(mGain.values));
- if ((status = parcel->writeInt32Vector(values)) != NO_ERROR) return status;
- return status;
+status_t AudioPortConfig::writeToParcel(Parcel *parcel) const {
+ media::AudioPortConfig parcelable;
+ return writeToParcelable(&parcelable)
+ ?: parcelable.writeToParcel(parcel);
}
-status_t AudioPortConfig::readFromParcel(const Parcel *parcel)
-{
- status_t status = NO_ERROR;
- if ((status = parcel->readUint32(&mSamplingRate)) != NO_ERROR) return status;
- static_assert(sizeof(mFormat) == sizeof(uint32_t));
- if ((status = parcel->readUint32(reinterpret_cast<uint32_t*>(&mFormat))) != NO_ERROR) {
- return status;
- }
- uint32_t rawChannelMask;
- if ((status = parcel->readUint32(&rawChannelMask)) != NO_ERROR) return status;
- mChannelMask = static_cast<audio_channel_mask_t>(rawChannelMask);
- if ((status = parcel->readInt32(&mId)) != NO_ERROR) return status;
- // Read mGain from parcel.
- if ((status = parcel->readInt32(&mGain.index)) != NO_ERROR) return status;
- uint32_t rawGainMode;
- if ((status = parcel->readUint32(&rawGainMode)) != NO_ERROR) return status;
- mGain.mode = static_cast<audio_gain_mode_t>(rawGainMode);
- if ((status = parcel->readUint32(&rawChannelMask)) != NO_ERROR) return status;
- mGain.channel_mask = static_cast<audio_channel_mask_t>(rawChannelMask);
- if ((status = parcel->readUint32(&mGain.ramp_duration_ms)) != NO_ERROR) return status;
- std::vector<int> values;
- if ((status = parcel->readInt32Vector(&values)) != NO_ERROR) return status;
- if (values.size() != std::size(mGain.values)) {
+status_t AudioPortConfig::writeToParcelable(media::AudioPortConfig* parcelable) const {
+ parcelable->sampleRate = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(mSamplingRate));
+ parcelable->format = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_format_t_AudioFormat(mFormat));
+ parcelable->channelMask = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_channel_mask_t_int32_t(mChannelMask));
+ parcelable->id = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_port_handle_t_int32_t(mId));
+ parcelable->gain.index = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(mGain.index));
+ parcelable->gain.mode = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_gain_mode_t_int32_t_mask(mGain.mode));
+ parcelable->gain.channelMask = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_channel_mask_t_int32_t(mGain.channel_mask));
+ parcelable->gain.rampDurationMs = VALUE_OR_RETURN_STATUS(
+ convertIntegral<int32_t>(mGain.ramp_duration_ms));
+ parcelable->gain.values = VALUE_OR_RETURN_STATUS(convertContainer<std::vector<int32_t>>(
+ mGain.values, convertIntegral<int32_t, int>));
+ return OK;
+}
+
+status_t AudioPortConfig::readFromParcel(const Parcel *parcel) {
+ media::AudioPortConfig parcelable;
+ return parcelable.readFromParcel(parcel)
+ ?: readFromParcelable(parcelable);
+}
+
+status_t AudioPortConfig::readFromParcelable(const media::AudioPortConfig& parcelable) {
+ mSamplingRate = VALUE_OR_RETURN_STATUS(convertIntegral<unsigned int>(parcelable.sampleRate));
+ mFormat = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioFormat_audio_format_t(parcelable.format));
+ mChannelMask = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_int32_t_audio_channel_mask_t(parcelable.channelMask));
+ mId = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_port_handle_t(parcelable.id));
+ mGain.index = VALUE_OR_RETURN_STATUS(convertIntegral<int>(parcelable.gain.index));
+ mGain.mode = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_int32_t_audio_gain_mode_t_mask(parcelable.gain.mode));
+ mGain.channel_mask = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_int32_t_audio_channel_mask_t(parcelable.gain.channelMask));
+ mGain.ramp_duration_ms = VALUE_OR_RETURN_STATUS(
+ convertIntegral<unsigned int>(parcelable.gain.rampDurationMs));
+ if (parcelable.gain.values.size() > std::size(mGain.values)) {
return BAD_VALUE;
}
- std::copy(values.begin(), values.end(), mGain.values);
- return status;
+ for (size_t i = 0; i < parcelable.gain.values.size(); ++i) {
+ mGain.values[i] = VALUE_OR_RETURN_STATUS(convertIntegral<int>(parcelable.gain.values[i]));
+ }
+ return OK;
}
} // namespace android
diff --git a/media/libaudiofoundation/AudioProfile.cpp b/media/libaudiofoundation/AudioProfile.cpp
index 67b600e..8ac3f73 100644
--- a/media/libaudiofoundation/AudioProfile.cpp
+++ b/media/libaudiofoundation/AudioProfile.cpp
@@ -58,10 +58,18 @@
AudioProfile::AudioProfile(audio_format_t format,
const ChannelMaskSet &channelMasks,
const SampleRateSet &samplingRateCollection) :
+ AudioProfile(format, channelMasks, samplingRateCollection,
+ AUDIO_ENCAPSULATION_TYPE_NONE) {}
+
+AudioProfile::AudioProfile(audio_format_t format,
+ const ChannelMaskSet &channelMasks,
+ const SampleRateSet &samplingRateCollection,
+ audio_encapsulation_type_t encapsulationType) :
mName(""),
mFormat(format),
mChannelMasks(channelMasks),
- mSamplingRates(samplingRateCollection) {}
+ mSamplingRates(samplingRateCollection),
+ mEncapsulationType(encapsulationType) {}
void AudioProfile::setChannels(const ChannelMaskSet &channelMasks)
{
@@ -116,6 +124,9 @@
}
dst->append("\n");
}
+
+ dst->append(base::StringPrintf(
+ "%*s- encapsulation type: %#x\n", spaces, "", mEncapsulationType));
}
bool AudioProfile::equals(const sp<AudioProfile>& other) const
@@ -127,47 +138,83 @@
mSamplingRates == other->getSampleRates() &&
mIsDynamicFormat == other->isDynamicFormat() &&
mIsDynamicChannels == other->isDynamicChannels() &&
- mIsDynamicRate == other->isDynamicRate();
+ mIsDynamicRate == other->isDynamicRate() &&
+ mEncapsulationType == other->getEncapsulationType();
}
-status_t AudioProfile::writeToParcel(Parcel *parcel) const
-{
- status_t status = NO_ERROR;
- if ((status = parcel->writeUtf8AsUtf16(mName)) != NO_ERROR) return status;
- if ((status = parcel->writeUint32(mFormat)) != NO_ERROR) return status;
- std::vector<int> values(mChannelMasks.begin(), mChannelMasks.end());
- if ((status = parcel->writeInt32Vector(values)) != NO_ERROR) return status;
- values.clear();
- values.assign(mSamplingRates.begin(), mSamplingRates.end());
- if ((status = parcel->writeInt32Vector(values)) != NO_ERROR) return status;
- if ((status = parcel->writeBool(mIsDynamicFormat)) != NO_ERROR) return status;
- if ((status = parcel->writeBool(mIsDynamicChannels)) != NO_ERROR) return status;
- if ((status = parcel->writeBool(mIsDynamicRate)) != NO_ERROR) return status;
- return status;
+AudioProfile& AudioProfile::operator=(const AudioProfile& other) {
+ mName = other.mName;
+ mFormat = other.mFormat;
+ mChannelMasks = other.mChannelMasks;
+ mSamplingRates = other.mSamplingRates;
+ mEncapsulationType = other.mEncapsulationType;
+ mIsDynamicFormat = other.mIsDynamicFormat;
+ mIsDynamicChannels = other.mIsDynamicChannels;
+ mIsDynamicRate = other.mIsDynamicRate;
+ return *this;
}
-status_t AudioProfile::readFromParcel(const Parcel *parcel)
-{
- status_t status = NO_ERROR;
- if ((status = parcel->readUtf8FromUtf16(&mName)) != NO_ERROR) return status;
- static_assert(sizeof(mFormat) == sizeof(uint32_t));
- if ((status = parcel->readUint32(reinterpret_cast<uint32_t*>(&mFormat))) != NO_ERROR) {
+status_t AudioProfile::writeToParcel(Parcel *parcel) const {
+ media::AudioProfile parcelable = VALUE_OR_RETURN_STATUS(toParcelable());
+ return parcelable.writeToParcel(parcel);
+ }
+
+ConversionResult<media::AudioProfile>
+AudioProfile::toParcelable() const {
+ media::AudioProfile parcelable;
+ parcelable.name = mName;
+ parcelable.format = VALUE_OR_RETURN(legacy2aidl_audio_format_t_AudioFormat(mFormat));
+ parcelable.channelMasks = VALUE_OR_RETURN(
+ convertContainer<std::vector<int32_t>>(mChannelMasks,
+ legacy2aidl_audio_channel_mask_t_int32_t));
+ parcelable.samplingRates = VALUE_OR_RETURN(
+ convertContainer<std::vector<int32_t>>(mSamplingRates,
+ convertIntegral<int32_t, uint32_t>));
+ parcelable.isDynamicFormat = mIsDynamicFormat;
+ parcelable.isDynamicChannels = mIsDynamicChannels;
+ parcelable.isDynamicRate = mIsDynamicRate;
+ parcelable.encapsulationType = VALUE_OR_RETURN(
+ legacy2aidl_audio_encapsulation_type_t_AudioEncapsulationType(mEncapsulationType));
+ return parcelable;
+}
+
+status_t AudioProfile::readFromParcel(const Parcel *parcel) {
+ media::AudioProfile parcelable;
+ if (status_t status = parcelable.readFromParcel(parcel); status != OK) {
return status;
}
- std::vector<int> values;
- if ((status = parcel->readInt32Vector(&values)) != NO_ERROR) return status;
- mChannelMasks.clear();
- for (auto raw : values) {
- mChannelMasks.insert(static_cast<audio_channel_mask_t>(raw));
- }
- values.clear();
- if ((status = parcel->readInt32Vector(&values)) != NO_ERROR) return status;
- mSamplingRates.clear();
- mSamplingRates.insert(values.begin(), values.end());
- if ((status = parcel->readBool(&mIsDynamicFormat)) != NO_ERROR) return status;
- if ((status = parcel->readBool(&mIsDynamicChannels)) != NO_ERROR) return status;
- if ((status = parcel->readBool(&mIsDynamicRate)) != NO_ERROR) return status;
- return status;
+ *this = *VALUE_OR_RETURN_STATUS(fromParcelable(parcelable));
+ return OK;
+}
+
+ConversionResult<sp<AudioProfile>>
+AudioProfile::fromParcelable(const media::AudioProfile& parcelable) {
+ sp<AudioProfile> legacy = new AudioProfile();
+ legacy->mName = parcelable.name;
+ legacy->mFormat = VALUE_OR_RETURN(aidl2legacy_AudioFormat_audio_format_t(parcelable.format));
+ legacy->mChannelMasks = VALUE_OR_RETURN(
+ convertContainer<ChannelMaskSet>(parcelable.channelMasks,
+ aidl2legacy_int32_t_audio_channel_mask_t));
+ legacy->mSamplingRates = VALUE_OR_RETURN(
+ convertContainer<SampleRateSet>(parcelable.samplingRates,
+ convertIntegral<uint32_t, int32_t>));
+ legacy->mIsDynamicFormat = parcelable.isDynamicFormat;
+ legacy->mIsDynamicChannels = parcelable.isDynamicChannels;
+ legacy->mIsDynamicRate = parcelable.isDynamicRate;
+ legacy->mEncapsulationType = VALUE_OR_RETURN(
+ aidl2legacy_AudioEncapsulationType_audio_encapsulation_type_t(
+ parcelable.encapsulationType));
+ return legacy;
+}
+
+ConversionResult<sp<AudioProfile>>
+aidl2legacy_AudioProfile(const media::AudioProfile& aidl) {
+ return AudioProfile::fromParcelable(aidl);
+}
+
+ConversionResult<media::AudioProfile>
+legacy2aidl_AudioProfile(const sp<AudioProfile>& legacy) {
+ return legacy->toParcelable();
}
ssize_t AudioProfileVector::add(const sp<AudioProfile> &profile)
@@ -260,6 +307,16 @@
return false;
}
+bool AudioProfileVector::contains(const sp<AudioProfile>& profile) const
+{
+ for (const auto& audioProfile : *this) {
+ if (audioProfile->equals(profile)) {
+ return true;
+ }
+ }
+ return false;
+}
+
void AudioProfileVector::dump(std::string *dst, int spaces) const
{
dst->append(base::StringPrintf("%*s- Profiles:\n", spaces, ""));
@@ -306,4 +363,41 @@
});
}
+ConversionResult<AudioProfileVector>
+aidl2legacy_AudioProfileVector(const std::vector<media::AudioProfile>& aidl) {
+ return convertContainer<AudioProfileVector>(aidl, aidl2legacy_AudioProfile);
+}
+
+ConversionResult<std::vector<media::AudioProfile>>
+legacy2aidl_AudioProfileVector(const AudioProfileVector& legacy) {
+ return convertContainer<std::vector<media::AudioProfile>>(legacy, legacy2aidl_AudioProfile);
+}
+
+AudioProfileVector intersectAudioProfiles(const AudioProfileVector& profiles1,
+ const AudioProfileVector& profiles2)
+{
+ std::map<audio_format_t, std::pair<ChannelMaskSet, SampleRateSet>> infos2;
+ for (const auto& profile : profiles2) {
+ infos2.emplace(profile->getFormat(),
+ std::make_pair(profile->getChannels(), profile->getSampleRates()));
+ }
+ AudioProfileVector profiles;
+ for (const auto& profile : profiles1) {
+ const auto it = infos2.find(profile->getFormat());
+ if (it == infos2.end()) {
+ continue;
+ }
+ ChannelMaskSet channelMasks = SetIntersection(profile->getChannels(), it->second.first);
+ if (channelMasks.empty()) {
+ continue;
+ }
+ SampleRateSet sampleRates = SetIntersection(profile->getSampleRates(), it->second.second);
+ if (sampleRates.empty()) {
+ continue;
+ }
+ profiles.push_back(new AudioProfile(profile->getFormat(), channelMasks, sampleRates));
+ }
+ return profiles;
+}
+
} // namespace android
diff --git a/media/libaudiofoundation/DeviceDescriptorBase.cpp b/media/libaudiofoundation/DeviceDescriptorBase.cpp
index 16cf71a..5cfea81 100644
--- a/media/libaudiofoundation/DeviceDescriptorBase.cpp
+++ b/media/libaudiofoundation/DeviceDescriptorBase.cpp
@@ -19,6 +19,7 @@
#include <android-base/stringprintf.h>
#include <audio_utils/string.h>
+#include <media/AidlConversion.h>
#include <media/DeviceDescriptorBase.h>
#include <media/TypeConverter.h>
@@ -80,13 +81,12 @@
void DeviceDescriptorBase::toAudioPort(struct audio_port *port) const
{
ALOGV("DeviceDescriptorBase::toAudioPort() handle %d type %08x", mId, mDeviceTypeAddr.mType);
- AudioPort::toAudioPort(port);
- toAudioPortConfig(&port->active_config);
- port->id = mId;
- port->ext.device.type = mDeviceTypeAddr.mType;
- port->ext.device.encapsulation_modes = mEncapsulationModes;
- port->ext.device.encapsulation_metadata_types = mEncapsulationMetadataTypes;
- (void)audio_utils_strlcpy_zerofill(port->ext.device.address, mDeviceTypeAddr.getAddress());
+ toAudioPortInternal(port);
+}
+
+void DeviceDescriptorBase::toAudioPort(struct audio_port_v7 *port) const {
+ ALOGV("DeviceDescriptorBase::toAudioPort() v7 handle %d type %08x", mId, mDeviceTypeAddr.mType);
+ toAudioPortInternal(port);
}
status_t DeviceDescriptorBase::setEncapsulationModes(uint32_t encapsulationModes) {
@@ -122,9 +122,9 @@
spaces, "", ::android::toString(mDeviceTypeAddr.mType).c_str()));
dst->append(base::StringPrintf(
- "%*s- supported encapsulation modes: %u", spaces, "", mEncapsulationModes));
+ "%*s- supported encapsulation modes: %u\n", spaces, "", mEncapsulationModes));
dst->append(base::StringPrintf(
- "%*s- supported encapsulation metadata types: %u",
+ "%*s- supported encapsulation metadata types: %u\n",
spaces, "", mEncapsulationMetadataTypes));
if (mDeviceTypeAddr.address().size() != 0) {
@@ -156,26 +156,53 @@
mDeviceTypeAddr.equals(other->mDeviceTypeAddr);
}
+
status_t DeviceDescriptorBase::writeToParcel(Parcel *parcel) const
{
- status_t status = NO_ERROR;
- if ((status = AudioPort::writeToParcel(parcel)) != NO_ERROR) return status;
- if ((status = AudioPortConfig::writeToParcel(parcel)) != NO_ERROR) return status;
- if ((status = parcel->writeParcelable(mDeviceTypeAddr)) != NO_ERROR) return status;
- if ((status = parcel->writeUint32(mEncapsulationModes)) != NO_ERROR) return status;
- if ((status = parcel->writeUint32(mEncapsulationMetadataTypes)) != NO_ERROR) return status;
- return status;
+ media::AudioPort parcelable;
+ return writeToParcelable(&parcelable)
+ ?: parcelable.writeToParcel(parcel);
}
-status_t DeviceDescriptorBase::readFromParcel(const Parcel *parcel)
-{
- status_t status = NO_ERROR;
- if ((status = AudioPort::readFromParcel(parcel)) != NO_ERROR) return status;
- if ((status = AudioPortConfig::readFromParcel(parcel)) != NO_ERROR) return status;
- if ((status = parcel->readParcelable(&mDeviceTypeAddr)) != NO_ERROR) return status;
- if ((status = parcel->readUint32(&mEncapsulationModes)) != NO_ERROR) return status;
- if ((status = parcel->readUint32(&mEncapsulationMetadataTypes)) != NO_ERROR) return status;
- return status;
+status_t DeviceDescriptorBase::writeToParcelable(media::AudioPort* parcelable) const {
+ AudioPort::writeToParcelable(parcelable);
+ AudioPortConfig::writeToParcelable(&parcelable->activeConfig);
+ parcelable->id = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_port_handle_t_int32_t(mId));
+
+ media::AudioPortDeviceExt ext;
+ ext.device = VALUE_OR_RETURN_STATUS(legacy2aidl_AudioDeviceTypeAddress(mDeviceTypeAddr));
+ ext.encapsulationModes = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_AudioEncapsulationMode_mask(mEncapsulationModes));
+ ext.encapsulationMetadataTypes = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_AudioEncapsulationMetadataType_mask(mEncapsulationMetadataTypes));
+ UNION_SET(parcelable->ext, device, std::move(ext));
+ return OK;
+}
+
+status_t DeviceDescriptorBase::readFromParcel(const Parcel *parcel) {
+ media::AudioPort parcelable;
+ return parcelable.readFromParcel(parcel)
+ ?: readFromParcelable(parcelable);
+}
+
+status_t DeviceDescriptorBase::readFromParcelable(const media::AudioPort& parcelable) {
+ if (parcelable.type != media::AudioPortType::DEVICE) {
+ return BAD_VALUE;
+ }
+ status_t status = AudioPort::readFromParcelable(parcelable)
+ ?: AudioPortConfig::readFromParcelable(parcelable.activeConfig);
+ if (status != OK) {
+ return status;
+ }
+
+ media::AudioPortDeviceExt ext = VALUE_OR_RETURN_STATUS(UNION_GET(parcelable.ext, device));
+ mDeviceTypeAddr = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_AudioDeviceTypeAddress(ext.device));
+ mEncapsulationModes = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_AudioEncapsulationMode_mask(ext.encapsulationModes));
+ mEncapsulationMetadataTypes = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_AudioEncapsulationMetadataType_mask(ext.encapsulationMetadataTypes));
+ return OK;
}
std::string toString(const DeviceDescriptorBaseVector& devices)
@@ -199,4 +226,24 @@
return deviceTypeAddrs;
}
+ConversionResult<sp<DeviceDescriptorBase>>
+aidl2legacy_DeviceDescriptorBase(const media::AudioPort& aidl) {
+ sp<DeviceDescriptorBase> result = new DeviceDescriptorBase(AUDIO_DEVICE_NONE);
+ status_t status = result->readFromParcelable(aidl);
+ if (status != OK) {
+ return base::unexpected(status);
+ }
+ return result;
+}
+
+ConversionResult<media::AudioPort>
+legacy2aidl_DeviceDescriptorBase(const sp<DeviceDescriptorBase>& legacy) {
+ media::AudioPort aidl;
+ status_t status = legacy->writeToParcelable(&aidl);
+ if (status != OK) {
+ return base::unexpected(status);
+ }
+ return aidl;
+}
+
} // namespace android
diff --git a/media/libaudiofoundation/include/media/AudioContainers.h b/media/libaudiofoundation/include/media/AudioContainers.h
index aa7ca69..204b365 100644
--- a/media/libaudiofoundation/include/media/AudioContainers.h
+++ b/media/libaudiofoundation/include/media/AudioContainers.h
@@ -50,6 +50,15 @@
return intersection;
}
+template<typename T>
+static std::set<T> SetIntersection(const std::set<T>& a, const std::set<T> b) {
+ std::set<T> intersection;
+ std::set_intersection(a.begin(), a.end(),
+ b.begin(), b.end(),
+ std::inserter(intersection, intersection.begin()));
+ return intersection;
+}
+
static inline ChannelMaskSet asInMask(const ChannelMaskSet& channelMasks) {
ChannelMaskSet inMaskSet;
for (const auto &channel : channelMasks) {
diff --git a/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h b/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h
index 7497faf..8edcc58 100644
--- a/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h
+++ b/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h
@@ -19,9 +19,11 @@
#include <string>
#include <vector>
+#include <android/media/AudioDevice.h>
#include <binder/Parcelable.h>
#include <binder/Parcel.h>
#include <media/AudioContainers.h>
+#include <media/AidlConversionUtil.h>
#include <system/audio.h>
#include <utils/Errors.h>
@@ -84,4 +86,10 @@
std::string dumpAudioDeviceTypeAddrVector(const AudioDeviceTypeAddrVector& deviceTypeAddrs,
bool includeSensitiveInfo=false);
+// Conversion routines, according to AidlConversion.h conventions.
+ConversionResult<AudioDeviceTypeAddr>
+aidl2legacy_AudioDeviceTypeAddress(const media::AudioDevice& aidl);
+ConversionResult<media::AudioDevice>
+legacy2aidl_AudioDeviceTypeAddress(const AudioDeviceTypeAddr& legacy);
+
} // namespace android
diff --git a/media/libaudiofoundation/include/media/AudioGain.h b/media/libaudiofoundation/include/media/AudioGain.h
index 859f1e7..a06b686 100644
--- a/media/libaudiofoundation/include/media/AudioGain.h
+++ b/media/libaudiofoundation/include/media/AudioGain.h
@@ -16,8 +16,10 @@
#pragma once
+#include <android/media/AudioGain.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
+#include <media/AidlConversion.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <system/audio.h>
@@ -72,6 +74,9 @@
status_t writeToParcel(Parcel* parcel) const override;
status_t readFromParcel(const Parcel* parcel) override;
+ status_t writeToParcelable(media::AudioGain* parcelable) const;
+ status_t readFromParcelable(const media::AudioGain& parcelable);
+
private:
int mIndex;
struct audio_gain mGain;
@@ -79,6 +84,12 @@
bool mUseForVolume = false;
};
+// Conversion routines, according to AidlConversion.h conventions.
+ConversionResult<sp<AudioGain>>
+aidl2legacy_AudioGain(const media::AudioGain& aidl);
+ConversionResult<media::AudioGain>
+legacy2aidl_AudioGain(const sp<AudioGain>& legacy);
+
class AudioGains : public std::vector<sp<AudioGain> >, public Parcelable
{
public:
@@ -104,4 +115,10 @@
status_t readFromParcel(const Parcel* parcel) override;
};
+// Conversion routines, according to AidlConversion.h conventions.
+ConversionResult<AudioGains>
+aidl2legacy_AudioGains(const std::vector<media::AudioGain>& aidl);
+ConversionResult<std::vector<media::AudioGain>>
+legacy2aidl_AudioGains(const AudioGains& legacy);
+
} // namespace android
diff --git a/media/libaudiofoundation/include/media/AudioPort.h b/media/libaudiofoundation/include/media/AudioPort.h
index 3c013cb..1cee1c9 100644
--- a/media/libaudiofoundation/include/media/AudioPort.h
+++ b/media/libaudiofoundation/include/media/AudioPort.h
@@ -17,7 +17,11 @@
#pragma once
#include <string>
+#include <type_traits>
+#include <android/media/AudioPort.h>
+#include <android/media/AudioPortConfig.h>
+#include <android/media/ExtraAudioDescriptor.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
#include <media/AudioGain.h>
@@ -48,6 +52,8 @@
virtual void toAudioPort(struct audio_port *port) const;
+ virtual void toAudioPort(struct audio_port_v7 *port) const;
+
virtual void addAudioProfile(const sp<AudioProfile> &profile) {
mProfiles.add(profile);
}
@@ -62,8 +68,18 @@
void setAudioProfiles(const AudioProfileVector &profiles) { mProfiles = profiles; }
AudioProfileVector &getAudioProfiles() { return mProfiles; }
+ void setExtraAudioDescriptors(
+ const std::vector<media::ExtraAudioDescriptor> extraAudioDescriptors) {
+ mExtraAudioDescriptors = extraAudioDescriptors;
+ }
+ std::vector<media::ExtraAudioDescriptor> &getExtraAudioDescriptors() {
+ return mExtraAudioDescriptors;
+ }
+
virtual void importAudioPort(const sp<AudioPort>& port, bool force = false);
+ virtual void importAudioPort(const audio_port_v7& port);
+
status_t checkGain(const struct audio_gain_config *gainConfig, int index) const {
if (index < 0 || (size_t)index >= mGains.size()) {
return BAD_VALUE;
@@ -86,12 +102,31 @@
status_t writeToParcel(Parcel* parcel) const override;
status_t readFromParcel(const Parcel* parcel) override;
+ status_t writeToParcelable(media::AudioPort* parcelable) const;
+ status_t readFromParcelable(const media::AudioPort& parcelable);
+
AudioGains mGains; // gain controllers
protected:
std::string mName;
audio_port_type_t mType;
audio_port_role_t mRole;
AudioProfileVector mProfiles; // AudioProfiles supported by this port (format, Rates, Channels)
+
+ // Audio capabilities that are defined by hardware descriptors when the format is unrecognized
+ // by the platform, e.g. short audio descriptor in EDID for HDMI.
+ std::vector<media::ExtraAudioDescriptor> mExtraAudioDescriptors;
+private:
+ template <typename T, std::enable_if_t<std::is_same<T, struct audio_port>::value
+ || std::is_same<T, struct audio_port_v7>::value, int> = 0>
+ void toAudioPortBase(T* port) const {
+ port->role = mRole;
+ port->type = mType;
+ strlcpy(port->name, mName.c_str(), AUDIO_PORT_MAX_NAME_LEN);
+ port->num_gains = std::min(mGains.size(), (size_t) AUDIO_PORT_MAX_GAINS);
+ for (size_t i = 0; i < port->num_gains; i++) {
+ port->gains[i] = mGains[i]->getGain();
+ }
+ }
};
@@ -119,6 +154,8 @@
status_t writeToParcel(Parcel* parcel) const override;
status_t readFromParcel(const Parcel* parcel) override;
+ status_t writeToParcelable(media::AudioPortConfig* parcelable) const;
+ status_t readFromParcelable(const media::AudioPortConfig& parcelable);
protected:
unsigned int mSamplingRate = 0u;
diff --git a/media/libaudiofoundation/include/media/AudioProfile.h b/media/libaudiofoundation/include/media/AudioProfile.h
index 730138a..6a36e78 100644
--- a/media/libaudiofoundation/include/media/AudioProfile.h
+++ b/media/libaudiofoundation/include/media/AudioProfile.h
@@ -19,8 +19,10 @@
#include <string>
#include <vector>
+#include <android/media/AudioProfile.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
+#include <media/AidlConversion.h>
#include <media/AudioContainers.h>
#include <system/audio.h>
#include <utils/RefBase.h>
@@ -36,6 +38,10 @@
AudioProfile(audio_format_t format,
const ChannelMaskSet &channelMasks,
const SampleRateSet &samplingRateCollection);
+ AudioProfile(audio_format_t format,
+ const ChannelMaskSet &channelMasks,
+ const SampleRateSet &samplingRateCollection,
+ audio_encapsulation_type_t encapsulationType);
audio_format_t getFormat() const { return mFormat; }
const ChannelMaskSet &getChannels() const { return mChannelMasks; }
@@ -66,6 +72,11 @@
bool isDynamic() { return mIsDynamicFormat || mIsDynamicChannels || mIsDynamicRate; }
+ audio_encapsulation_type_t getEncapsulationType() const { return mEncapsulationType; }
+ void setEncapsulationType(audio_encapsulation_type_t encapsulationType) {
+ mEncapsulationType = encapsulationType;
+ }
+
void dump(std::string *dst, int spaces) const;
bool equals(const sp<AudioProfile>& other) const;
@@ -73,7 +84,11 @@
status_t writeToParcel(Parcel* parcel) const override;
status_t readFromParcel(const Parcel* parcel) override;
+ ConversionResult<media::AudioProfile> toParcelable() const;
+ static ConversionResult<sp<AudioProfile>> fromParcelable(const media::AudioProfile& parcelable);
+
private:
+
std::string mName;
audio_format_t mFormat; // The format for an audio profile should only be set when initialized.
ChannelMaskSet mChannelMasks;
@@ -82,8 +97,19 @@
bool mIsDynamicFormat = false;
bool mIsDynamicChannels = false;
bool mIsDynamicRate = false;
+
+ audio_encapsulation_type_t mEncapsulationType = AUDIO_ENCAPSULATION_TYPE_NONE;
+
+ AudioProfile() = default;
+ AudioProfile& operator=(const AudioProfile& other);
};
+// Conversion routines, according to AidlConversion.h conventions.
+ConversionResult<sp<AudioProfile>>
+aidl2legacy_AudioProfile(const media::AudioProfile& aidl);
+ConversionResult<media::AudioProfile>
+legacy2aidl_AudioProfile(const sp<AudioProfile>& legacy);
+
class AudioProfileVector : public std::vector<sp<AudioProfile>>, public Parcelable
{
public:
@@ -105,6 +131,8 @@
bool hasDynamicProfile() const;
bool hasDynamicRateFor(audio_format_t format) const;
+ bool contains(const sp<AudioProfile>& profile) const;
+
virtual void dump(std::string *dst, int spaces) const;
bool equals(const AudioProfileVector& other) const;
@@ -115,4 +143,13 @@
bool operator == (const AudioProfile &left, const AudioProfile &right);
+// Conversion routines, according to AidlConversion.h conventions.
+ConversionResult<AudioProfileVector>
+aidl2legacy_AudioProfileVector(const std::vector<media::AudioProfile>& aidl);
+ConversionResult<std::vector<media::AudioProfile>>
+legacy2aidl_AudioProfileVector(const AudioProfileVector& legacy);
+
+AudioProfileVector intersectAudioProfiles(const AudioProfileVector& profiles1,
+ const AudioProfileVector& profiles2);
+
} // namespace android
diff --git a/media/libaudiofoundation/include/media/DeviceDescriptorBase.h b/media/libaudiofoundation/include/media/DeviceDescriptorBase.h
index 0cbd1de..140ce36 100644
--- a/media/libaudiofoundation/include/media/DeviceDescriptorBase.h
+++ b/media/libaudiofoundation/include/media/DeviceDescriptorBase.h
@@ -18,6 +18,7 @@
#include <vector>
+#include <android/media/AudioPort.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
#include <media/AudioContainers.h>
@@ -54,6 +55,7 @@
// AudioPort
virtual void toAudioPort(struct audio_port *port) const;
+ virtual void toAudioPort(struct audio_port_v7 *port) const;
status_t setEncapsulationModes(uint32_t encapsulationModes);
status_t setEncapsulationMetadataTypes(uint32_t encapsulationMetadataTypes);
@@ -75,10 +77,25 @@
status_t writeToParcel(Parcel* parcel) const override;
status_t readFromParcel(const Parcel* parcel) override;
+ status_t writeToParcelable(media::AudioPort* parcelable) const;
+ status_t readFromParcelable(const media::AudioPort& parcelable);
+
protected:
AudioDeviceTypeAddr mDeviceTypeAddr;
uint32_t mEncapsulationModes = 0;
uint32_t mEncapsulationMetadataTypes = 0;
+private:
+ template <typename T, std::enable_if_t<std::is_same<T, struct audio_port>::value
+ || std::is_same<T, struct audio_port_v7>::value, int> = 0>
+ void toAudioPortInternal(T* port) const {
+ AudioPort::toAudioPort(port);
+ toAudioPortConfig(&port->active_config);
+ port->id = mId;
+ port->ext.device.type = mDeviceTypeAddr.mType;
+ port->ext.device.encapsulation_modes = mEncapsulationModes;
+ port->ext.device.encapsulation_metadata_types = mEncapsulationMetadataTypes;
+ (void)audio_utils_strlcpy_zerofill(port->ext.device.address, mDeviceTypeAddr.getAddress());
+ }
};
using DeviceDescriptorBaseVector = std::vector<sp<DeviceDescriptorBase>>;
@@ -94,4 +111,10 @@
*/
AudioDeviceTypeAddrVector deviceTypeAddrsFromDescriptors(const DeviceDescriptorBaseVector& devices);
+// Conversion routines, according to AidlConversion.h conventions.
+ConversionResult<sp<DeviceDescriptorBase>>
+aidl2legacy_DeviceDescriptorBase(const media::AudioPort& aidl);
+ConversionResult<media::AudioPort>
+legacy2aidl_DeviceDescriptorBase(const sp<DeviceDescriptorBase>& legacy);
+
} // namespace android
diff --git a/media/libaudiohal/Android.bp b/media/libaudiohal/Android.bp
index dbe342a..bd24c84 100644
--- a/media/libaudiohal/Android.bp
+++ b/media/libaudiohal/Android.bp
@@ -39,6 +39,7 @@
header_libs: [
"libaudiohal_headers",
"libbase_headers",
+ "libmediautils_headers",
]
}
@@ -71,8 +72,6 @@
export_include_dirs: ["include"],
// This is needed because the stream interface includes media/MicrophoneInfo.h
- // which is not in any library but has a dependency on headers from libbinder.
- header_libs: ["libbinder_headers"],
-
- export_header_lib_headers: ["libbinder_headers"],
+ header_libs: ["av-headers"],
+ export_header_lib_headers: ["av-headers"],
}
diff --git a/media/libaudiohal/impl/Android.bp b/media/libaudiohal/impl/Android.bp
index 91a7a17..a2c6e8a 100644
--- a/media/libaudiohal/impl/Android.bp
+++ b/media/libaudiohal/impl/Android.bp
@@ -35,6 +35,7 @@
"android.hardware.audio.common-util",
"android.hidl.allocator@1.0",
"android.hidl.memory@1.0",
+ "av-types-aidl-cpp",
"libaudiofoundation",
"libaudiohal_deathhandler",
"libaudioutils",
diff --git a/media/libaudiohal/impl/ConversionHelperHidl.cpp b/media/libaudiohal/impl/ConversionHelperHidl.cpp
index b6fee6d..32eaa31 100644
--- a/media/libaudiohal/impl/ConversionHelperHidl.cpp
+++ b/media/libaudiohal/impl/ConversionHelperHidl.cpp
@@ -51,9 +51,9 @@
value) == NO_ERROR;
const bool keepDelayValue =
- halKeys.get(String8(AUDIO_PARAMETER_DEVICE_ADDITIONAL_OUTPUT_DELAY),
+ halKeys.get(String8(AudioParameter::keyAdditionalOutputDeviceDelay),
value) == NO_ERROR ||
- halKeys.get(String8(AUDIO_PARAMETER_DEVICE_MAX_ADDITIONAL_OUTPUT_DELAY),
+ halKeys.get(String8(AudioParameter::keyMaxAdditionalOutputDeviceDelay),
value) == NO_ERROR;
for (size_t i = 0; i < halKeys.size(); ++i) {
@@ -62,8 +62,8 @@
if (status != OK) return status;
if ((keepFormatValue && key == AudioParameter::keyFormat) ||
(keepRoutingValue && key == AudioParameter::keyRouting) ||
- (keepDelayValue && key == AUDIO_PARAMETER_DEVICE_ADDITIONAL_OUTPUT_DELAY) ||
- (keepDelayValue && key == AUDIO_PARAMETER_DEVICE_MAX_ADDITIONAL_OUTPUT_DELAY)) {
+ (keepDelayValue && key == AudioParameter::keyAdditionalOutputDeviceDelay) ||
+ (keepDelayValue && key == AudioParameter::keyMaxAdditionalOutputDeviceDelay)) {
AudioParameter keepValueParam;
halKeys.getAt(i, key, value);
keepValueParam.add(key, value);
diff --git a/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp b/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp
index 8d15592..9c4363c 100644
--- a/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp
+++ b/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp
@@ -40,7 +40,7 @@
EffectsFactoryHalHidl::EffectsFactoryHalHidl(sp<IEffectsFactory> effectsFactory)
: ConversionHelperHidl("EffectsFactory") {
- ALOG_ASSERT(effectsFactory != nullptr, "Provided IDevicesFactory service is NULL");
+ ALOG_ASSERT(effectsFactory != nullptr, "Provided IEffectsFactory service is NULL");
mEffectsFactory = effectsFactory;
}
diff --git a/media/libaudiohal/impl/EffectsFactoryHalHidl.h b/media/libaudiohal/impl/EffectsFactoryHalHidl.h
index dece1bb..5fa85e7 100644
--- a/media/libaudiohal/impl/EffectsFactoryHalHidl.h
+++ b/media/libaudiohal/impl/EffectsFactoryHalHidl.h
@@ -54,6 +54,8 @@
virtual status_t dumpEffects(int fd);
+ virtual float getHalVersion() { return MAJOR_VERSION + (float)MINOR_VERSION / 10; }
+
status_t allocateBuffer(size_t size, sp<EffectBufferHalInterface>* buffer) override;
status_t mirrorBuffer(void* external, size_t size,
sp<EffectBufferHalInterface>* buffer) override;
diff --git a/media/libaudiohal/impl/StreamHalHidl.cpp b/media/libaudiohal/impl/StreamHalHidl.cpp
index f4a4fe1..539a149 100644
--- a/media/libaudiohal/impl/StreamHalHidl.cpp
+++ b/media/libaudiohal/impl/StreamHalHidl.cpp
@@ -63,7 +63,9 @@
}
StreamHalHidl::~StreamHalHidl() {
- mStream = nullptr;
+ // The last step is to flush all binder commands so that the deletion
+ // of IStreamIn / IStreamOut (mStream) is issued with less delay. See b/35394629.
+ hardware::IPCThreadState::self()->flushCommands();
}
status_t StreamHalHidl::getBufferSize(size_t *size) {
@@ -307,7 +309,7 @@
}
private:
- wp<StreamOutHalHidl> mStream;
+ const wp<StreamOutHalHidl> mStream;
};
} // namespace
@@ -318,21 +320,19 @@
StreamOutHalHidl::~StreamOutHalHidl() {
if (mStream != 0) {
- if (mCallback.unsafe_get()) {
+ if (mCallback.load().unsafe_get()) {
processReturn("clearCallback", mStream->clearCallback());
}
#if MAJOR_VERSION >= 6
- if (mEventCallback.unsafe_get() != nullptr) {
+ if (mEventCallback.load().unsafe_get() != nullptr) {
processReturn("setEventCallback",
mStream->setEventCallback(nullptr));
}
#endif
processReturn("close", mStream->close());
- mStream.clear();
}
- mCallback.clear();
- mEventCallback.clear();
- hardware::IPCThreadState::self()->flushCommands();
+ mCallback = nullptr;
+ mEventCallback = nullptr;
if (mEfGroup) {
EventFlag::deleteEventFlag(&mEfGroup);
}
@@ -385,7 +385,7 @@
if (bytes == 0 && !mDataMQ) {
// Can't determine the size for the MQ buffer. Wait for a non-empty write request.
- ALOGW_IF(mCallback.unsafe_get(), "First call to async write with 0 bytes");
+ ALOGW_IF(mCallback.load().unsafe_get(), "First call to async write with 0 bytes");
return OK;
}
@@ -778,28 +778,28 @@
#endif
void StreamOutHalHidl::onWriteReady() {
- sp<StreamOutHalInterfaceCallback> callback = mCallback.promote();
+ sp<StreamOutHalInterfaceCallback> callback = mCallback.load().promote();
if (callback == 0) return;
ALOGV("asyncCallback onWriteReady");
callback->onWriteReady();
}
void StreamOutHalHidl::onDrainReady() {
- sp<StreamOutHalInterfaceCallback> callback = mCallback.promote();
+ sp<StreamOutHalInterfaceCallback> callback = mCallback.load().promote();
if (callback == 0) return;
ALOGV("asyncCallback onDrainReady");
callback->onDrainReady();
}
void StreamOutHalHidl::onError() {
- sp<StreamOutHalInterfaceCallback> callback = mCallback.promote();
+ sp<StreamOutHalInterfaceCallback> callback = mCallback.load().promote();
if (callback == 0) return;
ALOGV("asyncCallback onError");
callback->onError();
}
void StreamOutHalHidl::onCodecFormatChanged(const std::basic_string<uint8_t>& metadataBs) {
- sp<StreamOutHalInterfaceEventCallback> callback = mEventCallback.promote();
+ sp<StreamOutHalInterfaceEventCallback> callback = mEventCallback.load().promote();
if (callback == nullptr) return;
ALOGV("asyncCodecFormatCallback %s", __func__);
callback->onCodecFormatChanged(metadataBs);
@@ -813,8 +813,6 @@
StreamInHalHidl::~StreamInHalHidl() {
if (mStream != 0) {
processReturn("close", mStream->close());
- mStream.clear();
- hardware::IPCThreadState::self()->flushCommands();
}
if (mEfGroup) {
EventFlag::deleteEventFlag(&mEfGroup);
diff --git a/media/libaudiohal/impl/StreamHalHidl.h b/media/libaudiohal/impl/StreamHalHidl.h
index d40fa7c..970903b 100644
--- a/media/libaudiohal/impl/StreamHalHidl.h
+++ b/media/libaudiohal/impl/StreamHalHidl.h
@@ -25,6 +25,7 @@
#include <fmq/EventFlag.h>
#include <fmq/MessageQueue.h>
#include <media/audiohal/StreamHalInterface.h>
+#include <mediautils/Synchronization.h>
#include "ConversionHelperHidl.h"
#include "StreamPowerLog.h"
@@ -93,8 +94,7 @@
// Subclasses can not be constructed directly by clients.
explicit StreamHalHidl(IStream *stream);
- // The destructor automatically closes the stream.
- virtual ~StreamHalHidl();
+ ~StreamHalHidl() override;
status_t getCachedBufferSize(size_t *size);
@@ -107,7 +107,7 @@
private:
const int HAL_THREAD_PRIORITY_DEFAULT = -1;
- IStream *mStream;
+ IStream * const mStream;
int mHalThreadPriority;
size_t mCachedBufferSize;
};
@@ -197,9 +197,9 @@
typedef MessageQueue<uint8_t, hardware::kSynchronizedReadWrite> DataMQ;
typedef MessageQueue<WriteStatus, hardware::kSynchronizedReadWrite> StatusMQ;
- wp<StreamOutHalInterfaceCallback> mCallback;
- wp<StreamOutHalInterfaceEventCallback> mEventCallback;
- sp<IStreamOut> mStream;
+ mediautils::atomic_wp<StreamOutHalInterfaceCallback> mCallback;
+ mediautils::atomic_wp<StreamOutHalInterfaceEventCallback> mEventCallback;
+ const sp<IStreamOut> mStream;
std::unique_ptr<CommandMQ> mCommandMQ;
std::unique_ptr<DataMQ> mDataMQ;
std::unique_ptr<StatusMQ> mStatusMQ;
@@ -255,7 +255,7 @@
typedef MessageQueue<uint8_t, hardware::kSynchronizedReadWrite> DataMQ;
typedef MessageQueue<ReadStatus, hardware::kSynchronizedReadWrite> StatusMQ;
- sp<IStreamIn> mStream;
+ const sp<IStreamIn> mStream;
std::unique_ptr<CommandMQ> mCommandMQ;
std::unique_ptr<DataMQ> mDataMQ;
std::unique_ptr<StatusMQ> mStatusMQ;
diff --git a/media/libaudiohal/include/media/audiohal/EffectsFactoryHalInterface.h b/media/libaudiohal/include/media/audiohal/EffectsFactoryHalInterface.h
index 3a76f9f..9fb56ae 100644
--- a/media/libaudiohal/include/media/audiohal/EffectsFactoryHalInterface.h
+++ b/media/libaudiohal/include/media/audiohal/EffectsFactoryHalInterface.h
@@ -46,6 +46,8 @@
virtual status_t dumpEffects(int fd) = 0;
+ virtual float getHalVersion() = 0;
+
static sp<EffectsFactoryHalInterface> create();
virtual status_t allocateBuffer(size_t size, sp<EffectBufferHalInterface>* buffer) = 0;
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index 00baea2..d85e2e9 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -429,7 +429,7 @@
}
} break;
case HAPTIC_INTENSITY: {
- const haptic_intensity_t hapticIntensity = static_cast<haptic_intensity_t>(valueInt);
+ const os::HapticScale hapticIntensity = static_cast<os::HapticScale>(valueInt);
if (track->mHapticIntensity != hapticIntensity) {
track->mHapticIntensity = hapticIntensity;
}
@@ -552,7 +552,7 @@
t->mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
// haptic
t->mHapticPlaybackEnabled = false;
- t->mHapticIntensity = HAPTIC_SCALE_NONE;
+ t->mHapticIntensity = os::HapticScale::NONE;
t->mMixerHapticChannelMask = AUDIO_CHANNEL_NONE;
t->mMixerHapticChannelCount = 0;
t->mAdjustInChannelCount = t->channelCount + t->mHapticChannelCount;
@@ -597,19 +597,12 @@
const std::shared_ptr<Track> &t = getTrack(name);
if (t->mHapticPlaybackEnabled) {
size_t sampleCount = mFrameCount * t->mMixerHapticChannelCount;
- float gamma = t->getHapticScaleGamma();
- float maxAmplitudeRatio = t->getHapticMaxAmplitudeRatio();
uint8_t* buffer = (uint8_t*)pair.first + mFrameCount * audio_bytes_per_frame(
t->mMixerChannelCount, t->mMixerFormat);
switch (t->mMixerFormat) {
// Mixer format should be AUDIO_FORMAT_PCM_FLOAT.
case AUDIO_FORMAT_PCM_FLOAT: {
- float* fout = (float*) buffer;
- for (size_t i = 0; i < sampleCount; i++) {
- float mul = fout[i] >= 0 ? 1.0 : -1.0;
- fout[i] = powf(fabsf(fout[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma)
- * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * mul;
- }
+ os::scaleHapticData((float*) buffer, sampleCount, t->mHapticIntensity);
} break;
default:
LOG_ALWAYS_FATAL("bad mMixerFormat: %#x", t->mMixerFormat);
diff --git a/media/libaudioprocessing/include/media/AudioMixer.h b/media/libaudioprocessing/include/media/AudioMixer.h
index 3f7cd48..70eafe3 100644
--- a/media/libaudioprocessing/include/media/AudioMixer.h
+++ b/media/libaudioprocessing/include/media/AudioMixer.h
@@ -22,10 +22,10 @@
#include <stdint.h>
#include <sys/types.h>
-#include <android/os/IExternalVibratorService.h>
#include <media/AudioMixerBase.h>
#include <media/BufferProviders.h>
#include <utils/threads.h>
+#include <vibrator/ExternalVibrationUtils.h>
// FIXME This is actually unity gain, which might not be max in future, expressed in U.12
#define MAX_GAIN_INT AudioMixerBase::UNITY_GAIN_INT
@@ -55,32 +55,6 @@
// parameter 'value' is a pointer to the new playback rate.
};
- typedef enum { // Haptic intensity, should keep consistent with VibratorService
- HAPTIC_SCALE_MUTE = os::IExternalVibratorService::SCALE_MUTE,
- HAPTIC_SCALE_VERY_LOW = os::IExternalVibratorService::SCALE_VERY_LOW,
- HAPTIC_SCALE_LOW = os::IExternalVibratorService::SCALE_LOW,
- HAPTIC_SCALE_NONE = os::IExternalVibratorService::SCALE_NONE,
- HAPTIC_SCALE_HIGH = os::IExternalVibratorService::SCALE_HIGH,
- HAPTIC_SCALE_VERY_HIGH = os::IExternalVibratorService::SCALE_VERY_HIGH,
- } haptic_intensity_t;
- static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2.0f / 3.0f;
- static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f;
- static const constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f;
-
- static inline bool isValidHapticIntensity(haptic_intensity_t hapticIntensity) {
- switch (hapticIntensity) {
- case HAPTIC_SCALE_MUTE:
- case HAPTIC_SCALE_VERY_LOW:
- case HAPTIC_SCALE_LOW:
- case HAPTIC_SCALE_NONE:
- case HAPTIC_SCALE_HIGH:
- case HAPTIC_SCALE_VERY_HIGH:
- return true;
- default:
- return false;
- }
- }
-
AudioMixer(size_t frameCount, uint32_t sampleRate)
: AudioMixerBase(frameCount, sampleRate) {
pthread_once(&sOnceControl, &sInitRoutine);
@@ -170,7 +144,7 @@
// Haptic
bool mHapticPlaybackEnabled;
- haptic_intensity_t mHapticIntensity;
+ os::HapticScale mHapticIntensity;
audio_channel_mask_t mHapticChannelMask;
uint32_t mHapticChannelCount;
audio_channel_mask_t mMixerHapticChannelMask;
@@ -180,38 +154,6 @@
uint32_t mAdjustNonDestructiveInChannelCount;
uint32_t mAdjustNonDestructiveOutChannelCount;
bool mKeepContractedChannels;
-
- float getHapticScaleGamma() const {
- // Need to keep consistent with the value in VibratorService.
- switch (mHapticIntensity) {
- case HAPTIC_SCALE_VERY_LOW:
- return 2.0f;
- case HAPTIC_SCALE_LOW:
- return 1.5f;
- case HAPTIC_SCALE_HIGH:
- return 0.5f;
- case HAPTIC_SCALE_VERY_HIGH:
- return 0.25f;
- default:
- return 1.0f;
- }
- }
-
- float getHapticMaxAmplitudeRatio() const {
- // Need to keep consistent with the value in VibratorService.
- switch (mHapticIntensity) {
- case HAPTIC_SCALE_VERY_LOW:
- return HAPTIC_SCALE_VERY_LOW_RATIO;
- case HAPTIC_SCALE_LOW:
- return HAPTIC_SCALE_LOW_RATIO;
- case HAPTIC_SCALE_NONE:
- case HAPTIC_SCALE_HIGH:
- case HAPTIC_SCALE_VERY_HIGH:
- return 1.0f;
- default:
- return 0.0f;
- }
- }
};
inline std::shared_ptr<Track> getTrack(int name) {
diff --git a/media/libeffects/data/audio_effects.xml b/media/libeffects/data/audio_effects.xml
index 2e5f529..93a2181 100644
--- a/media/libeffects/data/audio_effects.xml
+++ b/media/libeffects/data/audio_effects.xml
@@ -21,6 +21,7 @@
<library name="downmix" path="libdownmix.so"/>
<library name="loudness_enhancer" path="libldnhncr.so"/>
<library name="dynamics_processing" path="libdynproc.so"/>
+ <library name="haptic_generator" path="libhapticgenerator.so"/>
</libraries>
<!-- list of effects to load.
@@ -58,6 +59,7 @@
<effect name="downmix" library="downmix" uuid="93f04452-e4fe-41cc-91f9-e475b6d1d69f"/>
<effect name="loudness_enhancer" library="loudness_enhancer" uuid="fa415329-2034-4bea-b5dc-5b381c8d1e2c"/>
<effect name="dynamics_processing" library="dynamics_processing" uuid="e0e6539b-1781-7261-676f-6d7573696340"/>
+ <effect name="haptic_generator" library="haptic_generator" uuid="97c4acd1-8b82-4f2f-832e-c2fe5d7a9931"/>
</effects>
<!-- Audio pre processor configurations.
diff --git a/media/libeffects/hapticgenerator/Android.bp b/media/libeffects/hapticgenerator/Android.bp
new file mode 100644
index 0000000..a660957
--- /dev/null
+++ b/media/libeffects/hapticgenerator/Android.bp
@@ -0,0 +1,59 @@
+// 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.
+
+// HapticGenerator library
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_library_shared {
+ name: "libhapticgenerator",
+
+ vendor: true,
+
+ srcs: [
+ "EffectHapticGenerator.cpp",
+ "Processors.cpp",
+ ],
+
+ cflags: [
+ "-O2", // Turning on the optimization in order to reduce effect processing time.
+ // The latency is around 1/5 less than without the optimization.
+ "-Wall",
+ "-Werror",
+ "-ffast-math", // This is needed for the non-zero coefficients optimization for
+ // BiquadFilter. Try the biquad_filter_benchmark test in audio_utils
+ // with/without `-ffast-math` for more context.
+ "-fvisibility=hidden",
+ ],
+
+ shared_libs: [
+ "libaudioutils",
+ "libbinder",
+ "liblog",
+ "libutils",
+ "libvibrator",
+ ],
+
+ relative_install_path: "soundfx",
+
+ header_libs: [
+ "libaudioeffects",
+ ],
+}
diff --git a/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp b/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
new file mode 100644
index 0000000..65a20a7
--- /dev/null
+++ b/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "EffectHG"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "EffectHapticGenerator.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include <errno.h>
+#include <inttypes.h>
+#include <math.h>
+
+#include <audio_effects/effect_hapticgenerator.h>
+#include <audio_utils/format.h>
+#include <system/audio.h>
+
+static constexpr float DEFAULT_RESONANT_FREQUENCY = 150.0f;
+static constexpr float DEFAULT_BSF_ZERO_Q = 8.0f;
+static constexpr float DEFAULT_BSF_POLE_Q = 4.0f;
+
+// This is the only symbol that needs to be exported
+__attribute__ ((visibility ("default")))
+audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
+ .tag = AUDIO_EFFECT_LIBRARY_TAG,
+ .version = EFFECT_LIBRARY_API_VERSION,
+ .name = "HapticGenerator Library",
+ .implementor = "The Android Open Source Project",
+ .create_effect = android::audio_effect::haptic_generator::HapticGeneratorLib_Create,
+ .release_effect = android::audio_effect::haptic_generator::HapticGeneratorLib_Release,
+ .get_descriptor = android::audio_effect::haptic_generator::HapticGeneratorLib_GetDescriptor,
+};
+
+namespace android::audio_effect::haptic_generator {
+
+// effect_handle_t interface implementation for haptic generator effect
+const struct effect_interface_s gHapticGeneratorInterface = {
+ HapticGenerator_Process,
+ HapticGenerator_Command,
+ HapticGenerator_GetDescriptor,
+ nullptr /* no process_reverse function, no reference stream needed */
+};
+
+//-----------------------------------------------------------------------------
+// Effect Descriptor
+//-----------------------------------------------------------------------------
+
+// UUIDs for effect types have been generated from http://www.itu.int/ITU-T/asn1/uuid.html
+// Haptic Generator
+static const effect_descriptor_t gHgDescriptor = {
+ FX_IID_HAPTICGENERATOR_, // type
+ {0x97c4acd1, 0x8b82, 0x4f2f, 0x832e, {0xc2, 0xfe, 0x5d, 0x7a, 0x99, 0x31}}, // uuid
+ EFFECT_CONTROL_API_VERSION,
+ EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
+ 0, // FIXME what value should be reported? // cpu load
+ 0, // FIXME what value should be reported? // memory usage
+ "Haptic Generator",
+ "The Android Open Source Project"
+};
+
+//-----------------------------------------------------------------------------
+// Internal functions
+//-----------------------------------------------------------------------------
+
+namespace {
+
+int HapticGenerator_Init(struct HapticGeneratorContext *context) {
+ context->itfe = &gHapticGeneratorInterface;
+
+ context->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
+ context->config.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+ context->config.inputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
+ context->config.inputCfg.samplingRate = 0;
+ context->config.inputCfg.bufferProvider.getBuffer = nullptr;
+ context->config.inputCfg.bufferProvider.releaseBuffer = nullptr;
+ context->config.inputCfg.bufferProvider.cookie = nullptr;
+ context->config.inputCfg.mask = EFFECT_CONFIG_ALL;
+ context->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
+ context->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+ context->config.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
+ context->config.outputCfg.samplingRate = 0;
+ context->config.outputCfg.bufferProvider.getBuffer = nullptr;
+ context->config.outputCfg.bufferProvider.releaseBuffer = nullptr;
+ context->config.outputCfg.bufferProvider.cookie = nullptr;
+ context->config.outputCfg.mask = EFFECT_CONFIG_ALL;
+
+ memset(context->param.hapticChannelSource, 0, sizeof(context->param.hapticChannelSource));
+ context->param.hapticChannelCount = 0;
+ context->param.audioChannelCount = 0;
+ context->param.maxHapticIntensity = os::HapticScale::MUTE;
+
+ context->param.resonantFrequency = DEFAULT_RESONANT_FREQUENCY;
+ context->param.bpfQ = 1.0f;
+ context->param.slowEnvNormalizationPower = -0.8f;
+ context->param.bsfZeroQ = DEFAULT_BSF_ZERO_Q;
+ context->param.bsfPoleQ = DEFAULT_BSF_POLE_Q;
+ context->param.distortionCornerFrequency = 300.0f;
+ context->param.distortionInputGain = 0.3f;
+ context->param.distortionCubeThreshold = 0.1f;
+ context->param.distortionOutputGain = 1.5f;
+
+ context->state = HAPTICGENERATOR_STATE_INITIALIZED;
+ return 0;
+}
+
+void addBiquadFilter(
+ std::vector<std::function<void(float *, const float *, size_t)>> &processingChain,
+ struct HapticGeneratorProcessorsRecord &processorsRecord,
+ std::shared_ptr<HapticBiquadFilter> filter) {
+ // The process chain captures the shared pointer of the filter in lambda.
+ // The process record will keep a shared pointer to the filter so that it is possible to access
+ // the filter outside of the process chain.
+ processorsRecord.filters.push_back(filter);
+ processingChain.push_back([filter](float *out, const float *in, size_t frameCount) {
+ filter->process(out, in, frameCount);
+ });
+}
+
+/**
+ * \brief build haptic generator processing chain.
+ *
+ * \param processingChain
+ * \param processorsRecord a structure to cache all the shared pointers for processors
+ * \param sampleRate the audio sampling rate. Use a float here as it may be used to create filters
+ * \param channelCount haptic channel count
+ */
+void HapticGenerator_buildProcessingChain(
+ std::vector<std::function<void(float*, const float*, size_t)>>& processingChain,
+ struct HapticGeneratorProcessorsRecord& processorsRecord, float sampleRate,
+ const struct HapticGeneratorParam* param) {
+ const size_t channelCount = param->hapticChannelCount;
+ float highPassCornerFrequency = 50.0f;
+ auto hpf = createHPF2(highPassCornerFrequency, sampleRate, channelCount);
+ addBiquadFilter(processingChain, processorsRecord, hpf);
+ float lowPassCornerFrequency = 9000.0f;
+ auto lpf = createLPF2(lowPassCornerFrequency, sampleRate, channelCount);
+ addBiquadFilter(processingChain, processorsRecord, lpf);
+
+ auto ramp = std::make_shared<Ramp>(channelCount); // ramp = half-wave rectifier.
+ // The process chain captures the shared pointer of the ramp in lambda. It will be the only
+ // reference to the ramp.
+ // The process record will keep a weak pointer to the ramp so that it is possible to access
+ // the ramp outside of the process chain.
+ processorsRecord.ramps.push_back(ramp);
+ processingChain.push_back([ramp](float *out, const float *in, size_t frameCount) {
+ ramp->process(out, in, frameCount);
+ });
+
+ highPassCornerFrequency = 60.0f;
+ hpf = createHPF2(highPassCornerFrequency, sampleRate, channelCount);
+ addBiquadFilter(processingChain, processorsRecord, hpf);
+ lowPassCornerFrequency = 700.0f;
+ lpf = createLPF2(lowPassCornerFrequency, sampleRate, channelCount);
+ addBiquadFilter(processingChain, processorsRecord, lpf);
+
+ lowPassCornerFrequency = 400.0f;
+ lpf = createLPF2(lowPassCornerFrequency, sampleRate, channelCount);
+ addBiquadFilter(processingChain, processorsRecord, lpf);
+ lowPassCornerFrequency = 500.0f;
+ lpf = createLPF2(lowPassCornerFrequency, sampleRate, channelCount);
+ addBiquadFilter(processingChain, processorsRecord, lpf);
+
+ auto bpf = createBPF(param->resonantFrequency, param->bpfQ, sampleRate, channelCount);
+ processorsRecord.bpf = bpf;
+ addBiquadFilter(processingChain, processorsRecord, bpf);
+
+ float normalizationPower = param->slowEnvNormalizationPower;
+ // The process chain captures the shared pointer of the slow envelope in lambda. It will
+ // be the only reference to the slow envelope.
+ // The process record will keep a weak pointer to the slow envelope so that it is possible
+ // to access the slow envelope outside of the process chain.
+ auto slowEnv = std::make_shared<SlowEnvelope>( // SlowEnvelope = partial normalizer, or AGC.
+ 5.0f /*envCornerFrequency*/, sampleRate, normalizationPower,
+ 0.01f /*envOffset*/, channelCount);
+ processorsRecord.slowEnvs.push_back(slowEnv);
+ processingChain.push_back([slowEnv](float *out, const float *in, size_t frameCount) {
+ slowEnv->process(out, in, frameCount);
+ });
+
+
+ auto bsf = createBSF(
+ param->resonantFrequency, param->bsfZeroQ, param->bsfPoleQ, sampleRate, channelCount);
+ processorsRecord.bsf = bsf;
+ addBiquadFilter(processingChain, processorsRecord, bsf);
+
+ // The process chain captures the shared pointer of the Distortion in lambda. It will
+ // be the only reference to the Distortion.
+ // The process record will keep a weak pointer to the Distortion so that it is possible
+ // to access the Distortion outside of the process chain.
+ auto distortion = std::make_shared<Distortion>(
+ param->distortionCornerFrequency, sampleRate, param->distortionInputGain,
+ param->distortionCubeThreshold, param->distortionOutputGain, channelCount);
+ processorsRecord.distortions.push_back(distortion);
+ processingChain.push_back([distortion](float *out, const float *in, size_t frameCount) {
+ distortion->process(out, in, frameCount);
+ });
+}
+
+int HapticGenerator_Configure(struct HapticGeneratorContext *context, effect_config_t *config) {
+ if (config->inputCfg.samplingRate != config->outputCfg.samplingRate ||
+ config->inputCfg.format != config->outputCfg.format ||
+ config->inputCfg.format != AUDIO_FORMAT_PCM_FLOAT ||
+ config->inputCfg.channels != config->outputCfg.channels ||
+ config->inputCfg.buffer.frameCount != config->outputCfg.buffer.frameCount) {
+ return -EINVAL;
+ }
+ if (&context->config != config) {
+ context->processingChain.clear();
+ context->processorsRecord.filters.clear();
+ context->processorsRecord.ramps.clear();
+ context->processorsRecord.slowEnvs.clear();
+ context->processorsRecord.distortions.clear();
+ memcpy(&context->config, config, sizeof(effect_config_t));
+ context->param.audioChannelCount = audio_channel_count_from_out_mask(
+ ((audio_channel_mask_t) config->inputCfg.channels) & ~AUDIO_CHANNEL_HAPTIC_ALL);
+ context->param.hapticChannelCount = audio_channel_count_from_out_mask(
+ ((audio_channel_mask_t) config->outputCfg.channels) & AUDIO_CHANNEL_HAPTIC_ALL);
+ ALOG_ASSERT(context->param.hapticChannelCount <= 2,
+ "haptic channel count(%zu) is too large",
+ context->param.hapticChannelCount);
+ context->audioDataBytesPerFrame = audio_bytes_per_frame(
+ context->param.audioChannelCount, (audio_format_t) config->inputCfg.format);
+ for (size_t i = 0; i < context->param.hapticChannelCount; ++i) {
+ // By default, use the first audio channel to generate haptic channels.
+ context->param.hapticChannelSource[i] = 0;
+ }
+
+ HapticGenerator_buildProcessingChain(context->processingChain,
+ context->processorsRecord,
+ config->inputCfg.samplingRate,
+ &context->param);
+ }
+ return 0;
+}
+
+int HapticGenerator_Reset(struct HapticGeneratorContext *context) {
+ for (auto& filter : context->processorsRecord.filters) {
+ filter->clear();
+ }
+ for (auto& slowEnv : context->processorsRecord.slowEnvs) {
+ slowEnv->clear();
+ }
+ for (auto& distortion : context->processorsRecord.distortions) {
+ distortion->clear();
+ }
+ return 0;
+}
+
+int HapticGenerator_SetParameter(struct HapticGeneratorContext *context,
+ int32_t param,
+ uint32_t size,
+ void *value) {
+ switch (param) {
+ case HG_PARAM_HAPTIC_INTENSITY: {
+ if (value == nullptr || size != (uint32_t) (2 * sizeof(int))) {
+ return -EINVAL;
+ }
+ int id = *(int *) value;
+ os::HapticScale hapticIntensity = static_cast<os::HapticScale>(*((int *) value + 1));
+ if (hapticIntensity == os::HapticScale::MUTE) {
+ context->param.id2Intensity.erase(id);
+ } else {
+ context->param.id2Intensity.emplace(id, hapticIntensity);
+ }
+ context->param.maxHapticIntensity = hapticIntensity;
+ for (const auto&[id, intensity] : context->param.id2Intensity) {
+ context->param.maxHapticIntensity = std::max(
+ context->param.maxHapticIntensity, intensity);
+ }
+ break;
+ }
+ case HG_PARAM_VIBRATOR_INFO: {
+ if (value == nullptr || size != 2 * sizeof(float)) {
+ return -EINVAL;
+ }
+ const float resonantFrequency = *(float*) value;
+ const float qFactor = *((float *) value + 1);
+ context->param.resonantFrequency =
+ isnan(resonantFrequency) ? DEFAULT_RESONANT_FREQUENCY : resonantFrequency;
+ context->param.bsfZeroQ = isnan(qFactor) ? DEFAULT_BSF_POLE_Q : qFactor;
+ context->param.bsfPoleQ = context->param.bsfZeroQ / 2.0f;
+
+ if (context->processorsRecord.bpf != nullptr) {
+ context->processorsRecord.bpf->setCoefficients(
+ bpfCoefs(context->param.resonantFrequency,
+ context->param.bpfQ,
+ context->config.inputCfg.samplingRate));
+ }
+ if (context->processorsRecord.bsf != nullptr) {
+ context->processorsRecord.bsf->setCoefficients(
+ bsfCoefs(context->param.resonantFrequency,
+ context->param.bsfZeroQ,
+ context->param.bsfPoleQ,
+ context->config.inputCfg.samplingRate));
+ }
+ HapticGenerator_Reset(context);
+ } break;
+ default:
+ ALOGW("Unknown param: %d", param);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief run the processing chain to generate haptic data from audio data
+ *
+ * \param processingChain the processing chain for generating haptic data
+ * \param buf1 a buffer contains raw audio data
+ * \param buf2 a buffer that is large enough to keep all the data
+ * \param frameCount frame count of the data
+ * \return a pointer to the output buffer
+ */
+float* HapticGenerator_runProcessingChain(
+ const std::vector<std::function<void(float*, const float*, size_t)>>& processingChain,
+ float* buf1, float* buf2, size_t frameCount) {
+ float *in = buf1;
+ float *out = buf2;
+ for (const auto processingFunc : processingChain) {
+ processingFunc(out, in, frameCount);
+ std::swap(in, out);
+ }
+ return in;
+}
+
+} // namespace (anonymous)
+
+//-----------------------------------------------------------------------------
+// Effect API Implementation
+//-----------------------------------------------------------------------------
+
+/*--- Effect Library Interface Implementation ---*/
+
+int32_t HapticGeneratorLib_Create(const effect_uuid_t *uuid,
+ int32_t sessionId __unused,
+ int32_t ioId __unused,
+ effect_handle_t *handle) {
+ if (handle == nullptr || uuid == nullptr) {
+ return -EINVAL;
+ }
+
+ if (memcmp(uuid, &gHgDescriptor.uuid, sizeof(*uuid)) != 0) {
+ return -EINVAL;
+ }
+
+ HapticGeneratorContext *context = new HapticGeneratorContext;
+ HapticGenerator_Init(context);
+
+ *handle = (effect_handle_t) context;
+ ALOGV("%s context is %p", __func__, context);
+ return 0;
+}
+
+int32_t HapticGeneratorLib_Release(effect_handle_t handle) {
+ HapticGeneratorContext *context = (HapticGeneratorContext *) handle;
+ delete context;
+ return 0;
+}
+
+int32_t HapticGeneratorLib_GetDescriptor(const effect_uuid_t *uuid,
+ effect_descriptor_t *descriptor) {
+
+ if (descriptor == nullptr || uuid == nullptr) {
+ ALOGE("%s() called with NULL pointer", __func__);
+ return -EINVAL;
+ }
+
+ if (memcmp(uuid, &gHgDescriptor.uuid, sizeof(*uuid)) == 0) {
+ *descriptor = gHgDescriptor;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/*--- Effect Control Interface Implementation ---*/
+
+int32_t HapticGenerator_Process(effect_handle_t self,
+ audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) {
+ HapticGeneratorContext *context = (HapticGeneratorContext *) self;
+
+ if (inBuffer == nullptr || inBuffer->raw == nullptr
+ || outBuffer == nullptr || outBuffer->raw == nullptr) {
+ return 0;
+ }
+
+ // The audio data must not be modified but just written to
+ // output buffer according the access mode.
+ size_t audioBytes = context->audioDataBytesPerFrame * inBuffer->frameCount;
+ size_t audioSampleCount = inBuffer->frameCount * context->param.audioChannelCount;
+ if (inBuffer->raw != outBuffer->raw) {
+ if (context->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
+ for (size_t i = 0; i < audioSampleCount; ++i) {
+ outBuffer->f32[i] += inBuffer->f32[i];
+ }
+ } else {
+ memcpy(outBuffer->raw, inBuffer->raw, audioBytes);
+ }
+ }
+
+ if (context->state != HAPTICGENERATOR_STATE_ACTIVE) {
+ ALOGE("State(%d) is not HAPTICGENERATOR_STATE_ACTIVE when calling %s",
+ context->state, __func__);
+ return -ENODATA;
+ }
+
+ if (context->param.maxHapticIntensity == os::HapticScale::MUTE) {
+ // Haptic channels are muted, not need to generate haptic data.
+ return 0;
+ }
+
+ // Resize buffer if the haptic sample count is greater than buffer size.
+ size_t hapticSampleCount = inBuffer->frameCount * context->param.hapticChannelCount;
+ if (hapticSampleCount > context->inputBuffer.size()) {
+ // The context->inputBuffer and context->outputBuffer must have the same size,
+ // which must be at least the haptic sample count.
+ context->inputBuffer.resize(hapticSampleCount);
+ context->outputBuffer.resize(hapticSampleCount);
+ }
+
+ // Construct input buffer according to haptic channel source
+ for (size_t i = 0; i < inBuffer->frameCount; ++i) {
+ for (size_t j = 0; j < context->param.hapticChannelCount; ++j) {
+ context->inputBuffer[i * context->param.hapticChannelCount + j] =
+ inBuffer->f32[i * context->param.audioChannelCount
+ + context->param.hapticChannelSource[j]];
+ }
+ }
+
+ float* hapticOutBuffer = HapticGenerator_runProcessingChain(
+ context->processingChain, context->inputBuffer.data(),
+ context->outputBuffer.data(), inBuffer->frameCount);
+ os::scaleHapticData(hapticOutBuffer, hapticSampleCount, context->param.maxHapticIntensity);
+
+ // For haptic data, the haptic playback thread will copy the data from effect input buffer,
+ // which contains haptic data at the end of the buffer, directly to sink buffer.
+ // In that case, copy haptic data to input buffer instead of output buffer.
+ // Note: this may not work with rpc/binder calls
+ memcpy_by_audio_format(static_cast<char*>(inBuffer->raw) + audioBytes,
+ static_cast<audio_format_t>(context->config.outputCfg.format),
+ hapticOutBuffer,
+ AUDIO_FORMAT_PCM_FLOAT,
+ hapticSampleCount);
+
+ return 0;
+}
+
+int32_t HapticGenerator_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
+ void *cmdData, uint32_t *replySize, void *replyData) {
+ HapticGeneratorContext *context = (HapticGeneratorContext *) self;
+
+ if (context == nullptr || context->state == HAPTICGENERATOR_STATE_UNINITIALIZED) {
+ return -EINVAL;
+ }
+
+ ALOGV("HapticGenerator_Command command %u cmdSize %u", cmdCode, cmdSize);
+
+ switch (cmdCode) {
+ case EFFECT_CMD_INIT:
+ if (replyData == nullptr || replySize == nullptr || *replySize != sizeof(int)) {
+ return -EINVAL;
+ }
+ *(int *) replyData = HapticGenerator_Init(context);
+ break;
+
+ case EFFECT_CMD_SET_CONFIG:
+ if (cmdData == nullptr || cmdSize != sizeof(effect_config_t)
+ || replyData == nullptr || replySize == nullptr || *replySize != sizeof(int)) {
+ return -EINVAL;
+ }
+ *(int *) replyData = HapticGenerator_Configure(
+ context, (effect_config_t *) cmdData);
+ break;
+
+ case EFFECT_CMD_RESET:
+ HapticGenerator_Reset(context);
+ break;
+
+ case EFFECT_CMD_GET_PARAM:
+ ALOGV("HapticGenerator_Command EFFECT_CMD_GET_PARAM cmdData %p,"
+ "*replySize %u, replyData: %p",
+ cmdData, *replySize, replyData);
+ break;
+
+ case EFFECT_CMD_SET_PARAM: {
+ ALOGV("HapticGenerator_Command EFFECT_CMD_SET_PARAM cmdSize %d cmdData %p, "
+ "*replySize %u, replyData %p", cmdSize, cmdData,
+ replySize ? *replySize : 0, replyData);
+ if (cmdData == nullptr || (cmdSize < (int) (sizeof(effect_param_t) + sizeof(int32_t)))
+ || replyData == nullptr || replySize == nullptr ||
+ *replySize != (int) sizeof(int32_t)) {
+ return -EINVAL;
+ }
+ effect_param_t *cmd = (effect_param_t *) cmdData;
+ *(int *) replyData = HapticGenerator_SetParameter(
+ context, *(int32_t *) cmd->data, cmd->vsize, cmd->data + sizeof(int32_t));
+ }
+ break;
+
+ case EFFECT_CMD_ENABLE:
+ if (replyData == nullptr || replySize == nullptr || *replySize != sizeof(int)) {
+ return -EINVAL;
+ }
+ if (context->state != HAPTICGENERATOR_STATE_INITIALIZED) {
+ return -ENOSYS;
+ }
+ context->state = HAPTICGENERATOR_STATE_ACTIVE;
+ ALOGV("EFFECT_CMD_ENABLE() OK");
+ *(int *) replyData = 0;
+ break;
+
+ case EFFECT_CMD_DISABLE:
+ if (replyData == nullptr || replySize == nullptr || *replySize != sizeof(int)) {
+ return -EINVAL;
+ }
+ if (context->state != HAPTICGENERATOR_STATE_ACTIVE) {
+ return -ENOSYS;
+ }
+ context->state = HAPTICGENERATOR_STATE_INITIALIZED;
+ ALOGV("EFFECT_CMD_DISABLE() OK");
+ *(int *) replyData = 0;
+ break;
+
+ case EFFECT_CMD_SET_VOLUME:
+ case EFFECT_CMD_SET_DEVICE:
+ case EFFECT_CMD_SET_AUDIO_MODE:
+ break;
+
+ default:
+ ALOGW("HapticGenerator_Command invalid command %u", cmdCode);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int32_t HapticGenerator_GetDescriptor(effect_handle_t self, effect_descriptor_t *descriptor) {
+ HapticGeneratorContext *context = (HapticGeneratorContext *) self;
+
+ if (context == nullptr ||
+ context->state == HAPTICGENERATOR_STATE_UNINITIALIZED) {
+ return -EINVAL;
+ }
+
+ memcpy(descriptor, &gHgDescriptor, sizeof(effect_descriptor_t));
+
+ return 0;
+}
+
+} // namespace android::audio_effect::haptic_generator
diff --git a/media/libeffects/hapticgenerator/EffectHapticGenerator.h b/media/libeffects/hapticgenerator/EffectHapticGenerator.h
new file mode 100644
index 0000000..96b744a
--- /dev/null
+++ b/media/libeffects/hapticgenerator/EffectHapticGenerator.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_EFFECTHAPTICGENERATOR_H_
+#define ANDROID_EFFECTHAPTICGENERATOR_H_
+
+#include <functional>
+#include <vector>
+#include <map>
+
+#include <hardware/audio_effect.h>
+#include <system/audio_effect.h>
+#include <vibrator/ExternalVibrationUtils.h>
+
+#include "Processors.h"
+
+namespace android::audio_effect::haptic_generator {
+
+//-----------------------------------------------------------------------------
+// Definition
+//-----------------------------------------------------------------------------
+
+enum hapticgenerator_state_t {
+ HAPTICGENERATOR_STATE_UNINITIALIZED,
+ HAPTICGENERATOR_STATE_INITIALIZED,
+ HAPTICGENERATOR_STATE_ACTIVE,
+};
+
+// parameters for each haptic generator
+struct HapticGeneratorParam {
+ uint32_t hapticChannelSource[2]; // The audio channels used to generate haptic channels.
+ // The first channel will be used to generate HAPTIC_A,
+ // The second channel will be used to generate HAPTIC_B
+ // The value will be offset of audio channel
+ uint32_t audioChannelCount;
+ uint32_t hapticChannelCount;
+
+ // A map from track id to haptic intensity.
+ std::map<int, os::HapticScale> id2Intensity;
+ os::HapticScale maxHapticIntensity; // max intensity will be used to scale haptic data.
+
+ float resonantFrequency;
+ float bpfQ;
+ float slowEnvNormalizationPower;
+ float bsfZeroQ;
+ float bsfPoleQ;
+ float distortionCornerFrequency;
+ float distortionInputGain;
+ float distortionCubeThreshold;
+ float distortionOutputGain;
+};
+
+// A structure to keep all shared pointers for all processors in HapticGenerator.
+struct HapticGeneratorProcessorsRecord {
+ std::vector<std::shared_ptr<HapticBiquadFilter>> filters;
+ std::vector<std::shared_ptr<Ramp>> ramps;
+ std::vector<std::shared_ptr<SlowEnvelope>> slowEnvs;
+ std::vector<std::shared_ptr<Distortion>> distortions;
+
+ // Cache band-pass filter and band-stop filter for updating parameters
+ // according to vibrator info
+ std::shared_ptr<HapticBiquadFilter> bpf;
+ std::shared_ptr<HapticBiquadFilter> bsf;
+};
+
+// A structure to keep all the context for HapticGenerator.
+struct HapticGeneratorContext {
+ const struct effect_interface_s *itfe;
+ effect_config_t config;
+ hapticgenerator_state_t state;
+ struct HapticGeneratorParam param;
+ size_t audioDataBytesPerFrame;
+
+ // A cache for all shared pointers of the HapticGenerator
+ struct HapticGeneratorProcessorsRecord processorsRecord;
+
+ // Using a vector of functions to record the processing chain for haptic-generating algorithm.
+ // The three parameters of the processing functions are pointer to output buffer, pointer to
+ // input buffer and frame count.
+ std::vector<std::function<void(float*, const float*, size_t)>> processingChain;
+
+ // inputBuffer is where to keep input buffer for the generating algorithm. It will be
+ // constructed according to HapticGeneratorParam.hapticChannelSource.
+ std::vector<float> inputBuffer;
+
+ // outputBuffer is a buffer having the same length as inputBuffer. It can be used as
+ // intermediate buffer in the generating algorithm.
+ std::vector<float> outputBuffer;
+};
+
+//-----------------------------------------------------------------------------
+// Effect API
+//-----------------------------------------------------------------------------
+
+int32_t HapticGeneratorLib_Create(const effect_uuid_t *uuid,
+ int32_t sessionId,
+ int32_t ioId,
+ effect_handle_t *handle);
+
+int32_t HapticGeneratorLib_Release(effect_handle_t handle);
+
+int32_t HapticGeneratorLib_GetDescriptor(const effect_uuid_t *uuid,
+ effect_descriptor_t *descriptor);
+
+int32_t HapticGenerator_Process(effect_handle_t self,
+ audio_buffer_t *inBuffer,
+ audio_buffer_t *outBuffer);
+
+int32_t HapticGenerator_Command(effect_handle_t self,
+ uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *cmdData,
+ uint32_t *replySize,
+ void *replyData);
+
+int32_t HapticGenerator_GetDescriptor(effect_handle_t self,
+ effect_descriptor_t *descriptor);
+
+} // namespace android::audio_effect::haptic_generator
+
+#endif // ANDROID_EFFECTHAPTICGENERATOR_H_
diff --git a/media/libeffects/hapticgenerator/MODULE_LICENSE_APACHE2 b/media/libeffects/hapticgenerator/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/media/libeffects/hapticgenerator/MODULE_LICENSE_APACHE2
diff --git a/media/libeffects/hapticgenerator/Processors.cpp b/media/libeffects/hapticgenerator/Processors.cpp
new file mode 100644
index 0000000..4fe3a75
--- /dev/null
+++ b/media/libeffects/hapticgenerator/Processors.cpp
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "EffectHG_Processors"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <assert.h>
+
+#include <cmath>
+
+#include "Processors.h"
+
+#if defined(__aarch64__) || defined(__ARM_NEON__)
+#ifndef USE_NEON
+#define USE_NEON (true)
+#endif
+#else
+#define USE_NEON (false)
+#endif
+#if USE_NEON
+#include <arm_neon.h>
+#endif
+
+namespace android::audio_effect::haptic_generator {
+
+float getRealPoleZ(float cornerFrequency, float sampleRate) {
+ // This will be a pole of a first order filter.
+ float realPoleS = -2 * M_PI * cornerFrequency;
+ return exp(realPoleS / sampleRate); // zero-pole matching
+}
+
+std::pair<float, float> getComplexPoleZ(float ringingFrequency, float q, float sampleRate) {
+ // This is the pole for 1/(s^2 + s/q + 1) in normalized frequency. The other pole is
+ // the complex conjugate of this.
+ float poleImagS = 2 * M_PI * ringingFrequency;
+ float poleRealS = -poleImagS / (2 * q);
+ float poleRadius = exp(poleRealS / sampleRate);
+ float poleImagZ = poleRadius * sin(poleImagS / sampleRate);
+ float poleRealZ = poleRadius * cos(poleImagS / sampleRate);
+ return {poleRealZ, poleImagZ};
+}
+
+// Implementation of Ramp
+
+Ramp::Ramp(size_t channelCount) : mChannelCount(channelCount) {}
+
+void Ramp::process(float *out, const float *in, size_t frameCount) {
+ size_t i = 0;
+#if USE_NEON
+ size_t sampleCount = frameCount * mChannelCount;
+ float32x2_t allZero = vdup_n_f32(0.0f);
+ while (i + 1 < sampleCount) {
+ vst1_f32(out, vmax_f32(vld1_f32(in), allZero));
+ in += 2;
+ out += 2;
+ i += 2;
+ }
+#endif // USE_NEON
+ for (; i < frameCount * mChannelCount; ++i) {
+ *out = *in >= 0.0f ? *in : 0.0f;
+ out++;
+ in++;
+ }
+}
+
+// Implementation of SlowEnvelope
+
+SlowEnvelope::SlowEnvelope(
+ float cornerFrequency,
+ float sampleRate,
+ float normalizationPower,
+ float envOffset,
+ size_t channelCount)
+ : mLpf(createLPF(cornerFrequency, sampleRate, channelCount)),
+ mNormalizationPower(normalizationPower),
+ mEnvOffset(envOffset),
+ mChannelCount(channelCount) {}
+
+void SlowEnvelope::process(float* out, const float* in, size_t frameCount) {
+ size_t sampleCount = frameCount * mChannelCount;
+ if (sampleCount > mLpfOutBuffer.size()) {
+ mLpfOutBuffer.resize(sampleCount);
+ mLpfInBuffer.resize(sampleCount);
+ }
+ for (size_t i = 0; i < sampleCount; ++i) {
+ mLpfInBuffer[i] = fabs(in[i]);
+ }
+ mLpf->process(mLpfOutBuffer.data(), mLpfInBuffer.data(), frameCount);
+ for (size_t i = 0; i < sampleCount; ++i) {
+ out[i] = in[i] * pow(mLpfOutBuffer[i] + mEnvOffset, mNormalizationPower);
+ }
+}
+
+void SlowEnvelope::setNormalizationPower(float normalizationPower) {
+ mNormalizationPower = normalizationPower;
+}
+
+void SlowEnvelope::clear() {
+ mLpf->clear();
+}
+
+// Implementation of distortion
+
+Distortion::Distortion(
+ float cornerFrequency,
+ float sampleRate,
+ float inputGain,
+ float cubeThreshold,
+ float outputGain,
+ size_t channelCount)
+ : mLpf(createLPF2(cornerFrequency, sampleRate, channelCount)),
+ mSampleRate(sampleRate),
+ mCornerFrequency(cornerFrequency),
+ mInputGain(inputGain),
+ mCubeThreshold(cubeThreshold),
+ mOutputGain(outputGain),
+ mChannelCount(channelCount) {}
+
+void Distortion::process(float *out, const float *in, size_t frameCount) {
+ size_t sampleCount = frameCount * mChannelCount;
+ if (sampleCount > mLpfInBuffer.size()) {
+ mLpfInBuffer.resize(sampleCount);
+ }
+ for (size_t i = 0; i < sampleCount; ++i) {
+ const float x = mInputGain * in[i];
+ mLpfInBuffer[i] = x * x * x / (mCubeThreshold + x * x); // "Coring" nonlinearity.
+ }
+ mLpf->process(out, mLpfInBuffer.data(), frameCount); // Reduce 3*F components.
+ for (size_t i = 0; i < sampleCount; ++i) {
+ const float x = out[i];
+ out[i] = mOutputGain * x / (1.0f + fabs(x)); // Soft limiter.
+ }
+}
+
+void Distortion::setCornerFrequency(float cornerFrequency) {
+ mCornerFrequency = cornerFrequency;
+ BiquadFilterCoefficients coefficient = lpfCoefs(cornerFrequency, mSampleRate);
+ mLpf->setCoefficients(coefficient);
+}
+
+void Distortion::setInputGain(float inputGain) {
+ mInputGain = inputGain;
+}
+
+void Distortion::setCubeThrehold(float cubeThreshold) {
+ mCubeThreshold = cubeThreshold;
+}
+
+void Distortion::setOutputGain(float outputGain) {
+ mOutputGain = outputGain;
+}
+
+void Distortion::clear() {
+ mLpf->clear();
+}
+
+
+// Implementation of helper functions
+
+BiquadFilterCoefficients cascadeFirstOrderFilters(const BiquadFilterCoefficients &coefs1,
+ const BiquadFilterCoefficients &coefs2) {
+ assert(coefs1[2] == 0.0f);
+ assert(coefs2[2] == 0.0f);
+ assert(coefs1[4] == 0.0f);
+ assert(coefs2[4] == 0.0f);
+ return {coefs1[0] * coefs2[0],
+ coefs1[0] * coefs2[1] + coefs1[1] * coefs2[0],
+ coefs1[1] * coefs2[1],
+ coefs1[3] + coefs2[3],
+ coefs1[3] * coefs2[3]};
+}
+
+BiquadFilterCoefficients lpfCoefs(const float cornerFrequency, const float sampleRate) {
+ BiquadFilterCoefficients coefficient;
+ float realPoleZ = getRealPoleZ(cornerFrequency, sampleRate);
+ // This is a zero at nyquist
+ coefficient[0] = 0.5f * (1 - realPoleZ);
+ coefficient[1] = coefficient[0];
+ coefficient[2] = 0.0f;
+ coefficient[3] = -realPoleZ; // This is traditional 1/(s+1) filter
+ coefficient[4] = 0.0f;
+ return coefficient;
+}
+
+BiquadFilterCoefficients bpfCoefs(const float ringingFrequency,
+ const float q,
+ const float sampleRate) {
+ BiquadFilterCoefficients coefficient;
+ const auto [real, img] = getComplexPoleZ(ringingFrequency, q, sampleRate);
+ // Note: this is not a standard cookbook BPF, but a low pass filter with zero at DC
+ coefficient[0] = 1.0f;
+ coefficient[1] = -1.0f;
+ coefficient[2] = 0.0f;
+ coefficient[3] = -2 * real;
+ coefficient[4] = real * real + img * img;
+ return coefficient;
+}
+
+BiquadFilterCoefficients bsfCoefs(const float ringingFrequency,
+ const float zq,
+ const float pq,
+ const float sampleRate) {
+ BiquadFilterCoefficients coefficient;
+ const auto [zeroReal, zeroImg] = getComplexPoleZ(ringingFrequency, zq, sampleRate);
+ float zeroCoeff1 = -2 * zeroReal;
+ float zeroCoeff2 = zeroReal* zeroReal + zeroImg * zeroImg;
+ const auto [poleReal, poleImg] = getComplexPoleZ(ringingFrequency, pq, sampleRate);
+ float poleCoeff1 = -2 * poleReal;
+ float poleCoeff2 = poleReal * poleReal + poleImg * poleImg;
+ const float norm = (1.0f + poleCoeff1 + poleCoeff2) / (1.0f + zeroCoeff1 + zeroCoeff2);
+ coefficient[0] = 1.0f * norm;
+ coefficient[1] = zeroCoeff1 * norm;
+ coefficient[2] = zeroCoeff2 * norm;
+ coefficient[3] = poleCoeff1;
+ coefficient[4] = poleCoeff2;
+ return coefficient;
+}
+
+std::shared_ptr<HapticBiquadFilter> createLPF(const float cornerFrequency,
+ const float sampleRate,
+ const size_t channelCount) {
+ BiquadFilterCoefficients coefficient = lpfCoefs(cornerFrequency, sampleRate);
+ return std::make_shared<HapticBiquadFilter>(channelCount, coefficient);
+}
+
+std::shared_ptr<HapticBiquadFilter> createLPF2(const float cornerFrequency,
+ const float sampleRate,
+ const size_t channelCount) {
+ BiquadFilterCoefficients coefficient = lpfCoefs(cornerFrequency, sampleRate);
+ return std::make_shared<HapticBiquadFilter>(
+ channelCount, cascadeFirstOrderFilters(coefficient, coefficient));
+}
+
+std::shared_ptr<HapticBiquadFilter> createHPF2(const float cornerFrequency,
+ const float sampleRate,
+ const size_t channelCount) {
+ BiquadFilterCoefficients coefficient;
+ // Note: this is valid only when corner frequency is less than nyquist / 2.
+ float realPoleZ = getRealPoleZ(cornerFrequency, sampleRate);
+
+ // Note: this is a zero at DC
+ coefficient[0] = 0.5f * (1 + realPoleZ);
+ coefficient[1] = -coefficient[0];
+ coefficient[2] = 0.0f;
+ coefficient[3] = -realPoleZ;
+ coefficient[4] = 0.0f;
+ return std::make_shared<HapticBiquadFilter>(
+ channelCount, cascadeFirstOrderFilters(coefficient, coefficient));
+}
+
+std::shared_ptr<HapticBiquadFilter> createBPF(const float ringingFrequency,
+ const float q,
+ const float sampleRate,
+ const size_t channelCount) {
+ BiquadFilterCoefficients coefficient = bpfCoefs(ringingFrequency, q, sampleRate);
+ return std::make_shared<HapticBiquadFilter>(channelCount, coefficient);
+}
+
+std::shared_ptr<HapticBiquadFilter> createBSF(const float ringingFrequency,
+ const float zq,
+ const float pq,
+ const float sampleRate,
+ const size_t channelCount) {
+ BiquadFilterCoefficients coefficient = bsfCoefs(ringingFrequency, zq, pq, sampleRate);
+ return std::make_shared<HapticBiquadFilter>(channelCount, coefficient);
+}
+
+} // namespace android::audio_effect::haptic_generator
diff --git a/media/libeffects/hapticgenerator/Processors.h b/media/libeffects/hapticgenerator/Processors.h
new file mode 100644
index 0000000..74ca77d
--- /dev/null
+++ b/media/libeffects/hapticgenerator/Processors.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _EFFECT_HAPTIC_GENERATOR_PROCESSORS_H_
+#define _EFFECT_HAPTIC_GENERATOR_PROCESSORS_H_
+
+#include <sys/types.h>
+
+#include <memory>
+#include <vector>
+
+#include <audio_utils/BiquadFilter.h>
+
+using HapticBiquadFilter = android::audio_utils::BiquadFilter<float>;
+using BiquadFilterCoefficients = std::array<float, android::audio_utils::kBiquadNumCoefs>;
+
+namespace android::audio_effect::haptic_generator {
+
+// A class providing a process function that makes input data non-negative.
+class Ramp {
+public:
+ explicit Ramp(size_t channelCount);
+
+ void process(float *out, const float *in, size_t frameCount);
+
+private:
+ const size_t mChannelCount;
+};
+
+
+class SlowEnvelope {
+public:
+ SlowEnvelope(float cornerFrequency, float sampleRate,
+ float normalizationPower, float envOffset,
+ size_t channelCount);
+
+ void process(float *out, const float *in, size_t frameCount);
+
+ void setNormalizationPower(float normalizationPower);
+
+ void clear();
+
+private:
+ const std::shared_ptr<HapticBiquadFilter> mLpf;
+ std::vector<float> mLpfInBuffer;
+ std::vector<float> mLpfOutBuffer;
+ float mNormalizationPower;
+ const float mEnvOffset;
+ const float mChannelCount;
+};
+
+
+// A class providing a process function that compressively distorts a waveforms
+class Distortion {
+public:
+ Distortion(float cornerFrequency, float sampleRate,
+ float inputGain, float cubeThreshold,
+ float outputGain, size_t channelCount);
+
+ void process(float *out, const float *in, size_t frameCount);
+
+ void setCornerFrequency(float cornerFrequency);
+ void setInputGain(float inputGain);
+ void setCubeThrehold(float cubeThreshold);
+ void setOutputGain(float outputGain);
+
+ void clear();
+
+private:
+ const std::shared_ptr<HapticBiquadFilter> mLpf;
+ std::vector<float> mLpfInBuffer;
+ float mSampleRate;
+ float mCornerFrequency;
+ float mInputGain;
+ float mCubeThreshold;
+ float mOutputGain;
+ const size_t mChannelCount;
+};
+
+// Helper functions
+
+BiquadFilterCoefficients cascadeFirstOrderFilters(const BiquadFilterCoefficients &coefs1,
+ const BiquadFilterCoefficients &coefs2);
+
+BiquadFilterCoefficients lpfCoefs(const float cornerFrequency, const float sampleRate);
+
+BiquadFilterCoefficients bpfCoefs(const float ringingFrequency,
+ const float q,
+ const float sampleRate);
+
+BiquadFilterCoefficients bsfCoefs(const float ringingFrequency,
+ const float zq,
+ const float pq,
+ const float sampleRate);
+
+std::shared_ptr<HapticBiquadFilter> createLPF(const float cornerFrequency,
+ const float sampleRate,
+ const size_t channelCount);
+
+// Create two cascaded LPF with same corner frequency.
+std::shared_ptr<HapticBiquadFilter> createLPF2(const float cornerFrequency,
+ const float sampleRate,
+ const size_t channelCount);
+
+// Create two cascaded HPF with same corner frequency.
+std::shared_ptr<HapticBiquadFilter> createHPF2(const float cornerFrequency,
+ const float sampleRate,
+ const size_t channelCount);
+
+std::shared_ptr<HapticBiquadFilter> createBPF(const float ringingFrequency,
+ const float q,
+ const float sampleRate,
+ const size_t channelCount);
+
+std::shared_ptr<HapticBiquadFilter> createBSF(const float ringingFrequency,
+ const float zq,
+ const float pq,
+ const float sampleRate,
+ const size_t channelCount);
+
+} // namespace android::audio_effect::haptic_generator
+
+#endif // _EFFECT_HAPTIC_GENERATOR_PROCESSORS_H_
diff --git a/media/libeffects/visualizer/Android.bp b/media/libeffects/visualizer/Android.bp
new file mode 100644
index 0000000..8dd6789
--- /dev/null
+++ b/media/libeffects/visualizer/Android.bp
@@ -0,0 +1,51 @@
+// Visualizer library
+package {
+ default_applicable_licenses: [
+ "frameworks_av_media_libeffects_visualizer_license",
+ ],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_av_media_libeffects_visualizer_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
+cc_library_shared {
+ name: "libvisualizer",
+
+ vendor: true,
+
+ srcs: [
+ "EffectVisualizer.cpp",
+ ],
+
+ cflags: [
+ "-O2",
+ "-fvisibility=hidden",
+
+ "-DBUILD_FLOAT",
+ "-DSUPPORT_MC",
+
+ "-Wall",
+ "-Werror",
+ ],
+
+ shared_libs: [
+ "liblog",
+ ],
+
+ relative_install_path: "soundfx",
+
+ header_libs: [
+ "libaudioeffects",
+ "libaudioutils_headers",
+ ],
+}
diff --git a/media/libeffects/visualizer/Android.mk b/media/libeffects/visualizer/Android.mk
deleted file mode 100644
index 68c7cf2..0000000
--- a/media/libeffects/visualizer/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# Visualizer library
-include $(CLEAR_VARS)
-
-LOCAL_VENDOR_MODULE := true
-LOCAL_SRC_FILES:= \
- EffectVisualizer.cpp
-
-LOCAL_CFLAGS+= -O2 -fvisibility=hidden
-LOCAL_CFLAGS += -Wall -Werror
-LOCAL_CFLAGS += -DBUILD_FLOAT -DSUPPORT_MC
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- liblog \
- libdl
-
-LOCAL_MODULE_RELATIVE_PATH := soundfx
-LOCAL_MODULE:= libvisualizer
-LOCAL_LICENSE_KINDS:= SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS:= notice
-LOCAL_NOTICE_FILE:= $(LOCAL_PATH)/NOTICE
-
-LOCAL_C_INCLUDES := \
- $(call include-path-for, audio-effects) \
- $(call include-path-for, audio-utils)
-
-
-LOCAL_HEADER_LIBRARIES += libhardware_headers
-include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 6214f5e..e471c7b 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -22,12 +22,14 @@
export_include_dirs: ["include"],
header_libs: [
+ "av-headers",
"libbase_headers",
"libgui_headers",
"libstagefright_headers",
"media_plugin_headers",
],
export_header_lib_headers: [
+ "av-headers",
"libgui_headers",
"libstagefright_headers",
"media_plugin_headers",
@@ -72,28 +74,6 @@
path: "aidl",
}
-filegroup {
- name: "resourcemanager_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",
- ],
- path: "aidl",
-}
-
-aidl_interface {
- name: "resourcemanager_aidl_interface",
- unstable: true,
- local_include_dir: "aidl",
- srcs: [
- ":resourcemanager_aidl",
- ],
-}
-
cc_library_shared {
name: "libmedia_omx",
vendor_available: true,
@@ -367,6 +347,8 @@
shared_libs: [
"android.hidl.token@1.0-utils",
+ "audioclient-types-aidl-cpp",
+ "av-types-aidl-cpp",
"liblog",
"libcutils",
"libprocessgroup",
@@ -383,6 +365,7 @@
"libaudioclient",
"libmedia_codeclist",
"libmedia_omx",
+ "media_permission-aidl-cpp",
],
export_shared_lib_headers: [
@@ -391,14 +374,17 @@
"libandroidicu",
//"libsonivox",
"libmedia_omx",
+ "media_permission-aidl-cpp",
],
static_libs: [
"resourcemanager_aidl_interface-ndk_platform",
+ "media_permission-aidl-cpp",
],
export_static_lib_headers: [
"resourcemanager_aidl_interface-ndk_platform",
+ "media_permission-aidl-cpp",
],
export_include_dirs: [
@@ -452,5 +438,8 @@
},
},
- apex_available: ["com.android.media"],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media"
+ ],
}
diff --git a/media/libmedia/IMediaExtractor.cpp b/media/libmedia/IMediaExtractor.cpp
index 7ed76d8..eb436d1 100644
--- a/media/libmedia/IMediaExtractor.cpp
+++ b/media/libmedia/IMediaExtractor.cpp
@@ -39,7 +39,8 @@
SETMEDIACAS,
NAME,
GETMETRICS,
- SETENTRYPOINT
+ SETENTRYPOINT,
+ SETLOGSESSIONID
};
class BpMediaExtractor : public BpInterface<IMediaExtractor> {
@@ -150,6 +151,13 @@
data.writeInt32(static_cast<int32_t>(entryPoint));
return remote()->transact(SETENTRYPOINT, data, &reply);
}
+
+ virtual status_t setLogSessionId(const String8& logSessionId) {
+ Parcel data, reply;
+ data.writeInterfaceToken(BpMediaExtractor::getInterfaceDescriptor());
+ data.writeString8(logSessionId);
+ return remote()->transact(SETLOGSESSIONID, data, &reply);
+ }
};
IMPLEMENT_META_INTERFACE(MediaExtractor, "android.media.IMediaExtractor");
@@ -250,6 +258,16 @@
}
return err;
}
+ case SETLOGSESSIONID: {
+ ALOGV("setLogSessionId");
+ CHECK_INTERFACE(IMediaExtractor, data, reply);
+ String8 logSessionId;
+ status_t status = data.readString8(&logSessionId);
+ if (status == OK) {
+ setLogSessionId(logSessionId);
+ }
+ return status;
+ }
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp
index 11005c6..0f189ee 100644
--- a/media/libmedia/IMediaPlayerService.cpp
+++ b/media/libmedia/IMediaPlayerService.cpp
@@ -35,6 +35,8 @@
namespace android {
+using media::permission::Identity;
+
enum {
CREATE = IBinder::FIRST_CALL_TRANSACTION,
CREATE_MEDIA_RECORDER,
@@ -63,22 +65,22 @@
virtual sp<IMediaPlayer> create(
const sp<IMediaPlayerClient>& client, audio_session_t audioSessionId,
- const std::string opPackageName) {
+ const Identity& identity) {
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
data.writeStrongBinder(IInterface::asBinder(client));
data.writeInt32(audioSessionId);
- data.writeCString(opPackageName.c_str());
+ data.writeParcelable(identity);
remote()->transact(CREATE, data, &reply);
return interface_cast<IMediaPlayer>(reply.readStrongBinder());
}
- virtual sp<IMediaRecorder> createMediaRecorder(const String16 &opPackageName)
+ virtual sp<IMediaRecorder> createMediaRecorder(const Identity& identity)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
- data.writeString16(opPackageName);
+ data.writeParcelable(identity);
remote()->transact(CREATE_MEDIA_RECORDER, data, &reply);
return interface_cast<IMediaRecorder>(reply.readStrongBinder());
}
@@ -129,19 +131,23 @@
sp<IMediaPlayerClient> client =
interface_cast<IMediaPlayerClient>(data.readStrongBinder());
audio_session_t audioSessionId = (audio_session_t) data.readInt32();
- const char* opPackageName = data.readCString();
- if (opPackageName == nullptr) {
- return FAILED_TRANSACTION;
+ Identity identity;
+ status_t status = data.readParcelable(&identity);
+ if (status != NO_ERROR) {
+ return status;
}
- std::string opPackageNameStr(opPackageName);
- sp<IMediaPlayer> player = create(client, audioSessionId, opPackageNameStr);
+ sp<IMediaPlayer> player = create(client, audioSessionId, identity);
reply->writeStrongBinder(IInterface::asBinder(player));
return NO_ERROR;
} break;
case CREATE_MEDIA_RECORDER: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
- const String16 opPackageName = data.readString16();
- sp<IMediaRecorder> recorder = createMediaRecorder(opPackageName);
+ Identity identity;
+ status_t status = data.readParcelable(&identity);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ sp<IMediaRecorder> recorder = createMediaRecorder(identity);
reply->writeStrongBinder(IInterface::asBinder(recorder));
return NO_ERROR;
} break;
diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp
index bd17f4d..67d33fa 100644
--- a/media/libmedia/MediaProfiles.cpp
+++ b/media/libmedia/MediaProfiles.cpp
@@ -63,7 +63,7 @@
searchDirs[1] + fileName,
searchDirs[2] + fileName,
searchDirs[3] + fileName,
- "system/etc/media_profiles_V1_0.xml" // System fallback
+ "system/etc/media_profiles.xml" // System fallback
};
}();
static std::array<char const*, 5> const cPaths = {
@@ -121,6 +121,11 @@
{"1080p", CAMCORDER_QUALITY_1080P},
{"2160p", CAMCORDER_QUALITY_2160P},
{"qvga", CAMCORDER_QUALITY_QVGA},
+ {"vga", CAMCORDER_QUALITY_VGA},
+ {"4kdci", CAMCORDER_QUALITY_4KDCI},
+ {"qhd", CAMCORDER_QUALITY_QHD},
+ {"2k", CAMCORDER_QUALITY_2K},
+ {"8kuhd", CAMCORDER_QUALITY_8KUHD},
{"timelapselow", CAMCORDER_QUALITY_TIME_LAPSE_LOW},
{"timelapsehigh", CAMCORDER_QUALITY_TIME_LAPSE_HIGH},
@@ -131,6 +136,10 @@
{"timelapse1080p", CAMCORDER_QUALITY_TIME_LAPSE_1080P},
{"timelapse2160p", CAMCORDER_QUALITY_TIME_LAPSE_2160P},
{"timelapseqvga", CAMCORDER_QUALITY_TIME_LAPSE_QVGA},
+ {"timelapsevga", CAMCORDER_QUALITY_TIME_LAPSE_VGA},
+ {"timelapse4kdci", CAMCORDER_QUALITY_TIME_LAPSE_4KDCI},
+ {"timelapseqhd", CAMCORDER_QUALITY_TIME_LAPSE_QHD},
+ {"timelapse2k", CAMCORDER_QUALITY_TIME_LAPSE_2K},
{"highspeedlow", CAMCORDER_QUALITY_HIGH_SPEED_LOW},
{"highspeedhigh", CAMCORDER_QUALITY_HIGH_SPEED_HIGH},
@@ -138,19 +147,11 @@
{"highspeed720p", CAMCORDER_QUALITY_HIGH_SPEED_720P},
{"highspeed1080p", CAMCORDER_QUALITY_HIGH_SPEED_1080P},
{"highspeed2160p", CAMCORDER_QUALITY_HIGH_SPEED_2160P},
-
- // Vendor-specific profiles
- {"vga", CAMCORDER_QUALITY_VGA},
- {"4kdci", CAMCORDER_QUALITY_4KDCI},
- {"timelapsevga", CAMCORDER_QUALITY_TIME_LAPSE_VGA},
- {"timelapse4kdci", CAMCORDER_QUALITY_TIME_LAPSE_4KDCI},
{"highspeedcif", CAMCORDER_QUALITY_HIGH_SPEED_CIF},
{"highspeedvga", CAMCORDER_QUALITY_HIGH_SPEED_VGA},
{"highspeed4kdci", CAMCORDER_QUALITY_HIGH_SPEED_4KDCI},
- {"qhd", CAMCORDER_QUALITY_QHD},
- {"2k", CAMCORDER_QUALITY_2k},
- {"timelapseqhd", CAMCORDER_QUALITY_TIME_LAPSE_QHD},
- {"timelapse2k", CAMCORDER_QUALITY_TIME_LAPSE_2k},
+
+ // Vendor-specific profiles
};
#if LOG_NDEBUG
diff --git a/media/libmedia/MediaResource.cpp b/media/libmedia/MediaResource.cpp
index fe86d27..ec52a49 100644
--- a/media/libmedia/MediaResource.cpp
+++ b/media/libmedia/MediaResource.cpp
@@ -43,11 +43,11 @@
}
//static
-MediaResource MediaResource::CodecResource(bool secure, bool video) {
+MediaResource MediaResource::CodecResource(bool secure, bool video, int64_t instanceCount) {
return MediaResource(
secure ? Type::kSecureCodec : Type::kNonSecureCodec,
video ? SubType::kVideoCodec : SubType::kAudioCodec,
- 1);
+ instanceCount);
}
//static
diff --git a/media/libmedia/TEST_MAPPING b/media/libmedia/TEST_MAPPING
new file mode 100644
index 0000000..65390ed
--- /dev/null
+++ b/media/libmedia/TEST_MAPPING
@@ -0,0 +1,6 @@
+// test_mapping for frameworks/av/media/libmedia
+{
+ "presubmit": [
+ { "name": "CodecListTest" }
+ ]
+}
diff --git a/media/libmedia/aidl/android/media/IResourceManagerService.aidl b/media/libmedia/aidl/android/media/IResourceManagerService.aidl
deleted file mode 100644
index 621bd84..0000000
--- a/media/libmedia/aidl/android/media/IResourceManagerService.aidl
+++ /dev/null
@@ -1,112 +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.
- */
-
-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);
-
- /**
- * Override the pid of original calling process with the pid of the process
- * who actually use the requested resources.
- *
- * @param originalPid pid of the original calling process.
- * @param newPid pid of the actual process who use the resources.
- * remove existing override on originalPid if newPid is -1.
- */
- void overridePid(int originalPid, int newPid);
-
- /**
- * Mark a client for pending removal
- *
- * @param pid pid from which the client's resources will be removed.
- * @param clientId clientId within the pid that will be removed.
- */
- void markClientForPendingRemoval(int pid, long clientId);
-
- /**
- * Reclaim resources from clients pending removal, if any.
- *
- * @param pid pid from which resources will be reclaimed.
- */
- void reclaimResourcesFromClientsPendingRemoval(int pid);
-}
diff --git a/media/libmedia/include/android/IMediaExtractor.h b/media/libmedia/include/android/IMediaExtractor.h
index f9cafde..bb1eb99 100644
--- a/media/libmedia/include/android/IMediaExtractor.h
+++ b/media/libmedia/include/android/IMediaExtractor.h
@@ -72,6 +72,8 @@
};
virtual status_t setEntryPoint(EntryPoint entryPoint) = 0;
+
+ virtual status_t setLogSessionId(const String8& logSessionId) = 0;
};
diff --git a/media/libmedia/include/media/IMediaPlayerService.h b/media/libmedia/include/media/IMediaPlayerService.h
index a4207eb..243e9c7 100644
--- a/media/libmedia/include/media/IMediaPlayerService.h
+++ b/media/libmedia/include/media/IMediaPlayerService.h
@@ -27,6 +27,7 @@
#include <media/IMediaPlayerClient.h>
#include <media/IMediaMetadataRetriever.h>
+#include <android/media/permission/Identity.h>
#include <string>
@@ -46,11 +47,13 @@
public:
DECLARE_META_INTERFACE(MediaPlayerService);
- virtual sp<IMediaRecorder> createMediaRecorder(const String16 &opPackageName) = 0;
+ virtual sp<IMediaRecorder> createMediaRecorder(
+ const android::media::permission::Identity &identity) = 0;
virtual sp<IMediaMetadataRetriever> createMetadataRetriever() = 0;
virtual sp<IMediaPlayer> create(const sp<IMediaPlayerClient>& client,
audio_session_t audioSessionId = AUDIO_SESSION_ALLOCATE,
- const std::string opPackage = "") = 0;
+ const android::media::permission::Identity& identity =
+ android::media::permission::Identity()) = 0;
virtual sp<IMediaCodecList> getCodecList() const = 0;
// Connects to a remote display.
diff --git a/media/libmedia/include/media/MediaProfiles.h b/media/libmedia/include/media/MediaProfiles.h
index 3f4fd19..4a898e2 100644
--- a/media/libmedia/include/media/MediaProfiles.h
+++ b/media/libmedia/include/media/MediaProfiles.h
@@ -39,8 +39,9 @@
CAMCORDER_QUALITY_VGA = 9,
CAMCORDER_QUALITY_4KDCI = 10,
CAMCORDER_QUALITY_QHD = 11,
- CAMCORDER_QUALITY_2k = 12,
- CAMCORDER_QUALITY_LIST_END = 12,
+ CAMCORDER_QUALITY_2K = 12,
+ CAMCORDER_QUALITY_8KUHD = 13,
+ CAMCORDER_QUALITY_LIST_END = 13,
CAMCORDER_QUALITY_TIME_LAPSE_LIST_START = 1000,
CAMCORDER_QUALITY_TIME_LAPSE_LOW = 1000,
@@ -55,8 +56,9 @@
CAMCORDER_QUALITY_TIME_LAPSE_VGA = 1009,
CAMCORDER_QUALITY_TIME_LAPSE_4KDCI = 1010,
CAMCORDER_QUALITY_TIME_LAPSE_QHD = 1011,
- CAMCORDER_QUALITY_TIME_LAPSE_2k = 1012,
- CAMCORDER_QUALITY_TIME_LAPSE_LIST_END = 1012,
+ CAMCORDER_QUALITY_TIME_LAPSE_2K = 1012,
+ CAMCORDER_QUALITY_TIME_LAPSE_8KUHD = 1013,
+ CAMCORDER_QUALITY_TIME_LAPSE_LIST_END = 1013,
CAMCORDER_QUALITY_HIGH_SPEED_LIST_START = 2000,
CAMCORDER_QUALITY_HIGH_SPEED_LOW = 2000,
diff --git a/media/libmedia/include/media/MediaRecorderBase.h b/media/libmedia/include/media/MediaRecorderBase.h
index d9a7efb..b5325ce 100644
--- a/media/libmedia/include/media/MediaRecorderBase.h
+++ b/media/libmedia/include/media/MediaRecorderBase.h
@@ -21,6 +21,7 @@
#include <media/AudioSystem.h>
#include <media/MicrophoneInfo.h>
#include <media/mediarecorder.h>
+#include <android/media/permission/Identity.h>
#include <system/audio.h>
@@ -33,8 +34,8 @@
struct PersistentSurface;
struct MediaRecorderBase {
- MediaRecorderBase(const String16 &opPackageName)
- : mOpPackageName(opPackageName) {}
+ explicit MediaRecorderBase(const media::permission::Identity &client)
+ : mClient(client) {}
virtual ~MediaRecorderBase() {}
virtual status_t init() = 0;
@@ -83,7 +84,7 @@
protected:
- String16 mOpPackageName;
+ media::permission::Identity mClient;
private:
MediaRecorderBase(const MediaRecorderBase &);
diff --git a/media/libmedia/include/media/MediaResource.h b/media/libmedia/include/media/MediaResource.h
index 4927d28..4712528 100644
--- a/media/libmedia/include/media/MediaResource.h
+++ b/media/libmedia/include/media/MediaResource.h
@@ -37,7 +37,7 @@
MediaResource(Type type, SubType subType, int64_t value);
MediaResource(Type type, const std::vector<uint8_t> &id, int64_t value);
- static MediaResource CodecResource(bool secure, bool video);
+ static MediaResource CodecResource(bool secure, bool video, int64_t instanceCount = 1);
static MediaResource GraphicMemoryResource(int64_t value);
static MediaResource CpuBoostResource();
static MediaResource VideoBatteryResource();
diff --git a/media/libmedia/include/media/mediametadataretriever.h b/media/libmedia/include/media/mediametadataretriever.h
index 138a014..fba1a30 100644
--- a/media/libmedia/include/media/mediametadataretriever.h
+++ b/media/libmedia/include/media/mediametadataretriever.h
@@ -73,6 +73,9 @@
METADATA_KEY_COLOR_RANGE = 37,
METADATA_KEY_SAMPLERATE = 38,
METADATA_KEY_BITS_PER_SAMPLE = 39,
+ METADATA_KEY_VIDEO_CODEC_MIME_TYPE = 40,
+ METADATA_KEY_XMP_OFFSET = 41,
+ METADATA_KEY_XMP_LENGTH = 42,
// Add more here...
};
diff --git a/media/libmedia/include/media/mediaplayer.h b/media/libmedia/include/media/mediaplayer.h
index 71c0bc5..fbba398 100644
--- a/media/libmedia/include/media/mediaplayer.h
+++ b/media/libmedia/include/media/mediaplayer.h
@@ -29,6 +29,7 @@
#include <media/IMediaPlayer.h>
#include <media/IMediaDeathNotifier.h>
#include <media/IStreamSource.h>
+#include <android/media/permission/Identity.h>
#include <utils/KeyedVector.h>
#include <utils/String8.h>
@@ -211,7 +212,8 @@
public virtual IMediaDeathNotifier
{
public:
- MediaPlayer(const std::string opPackageName = "");
+ explicit MediaPlayer(const android::media::permission::Identity& mIdentity =
+ android::media::permission::Identity());
~MediaPlayer();
void died();
void disconnect();
@@ -315,7 +317,7 @@
float mSendLevel;
struct sockaddr_in mRetransmitEndpoint;
bool mRetransmitEndpointValid;
- const std::string mOpPackageName;
+ const android::media::permission::Identity mIdentity;
};
}; // namespace android
diff --git a/media/libmedia/include/media/mediarecorder.h b/media/libmedia/include/media/mediarecorder.h
index 84c92f6..96a3293 100644
--- a/media/libmedia/include/media/mediarecorder.h
+++ b/media/libmedia/include/media/mediarecorder.h
@@ -25,6 +25,7 @@
#include <media/IMediaRecorderClient.h>
#include <media/IMediaDeathNotifier.h>
#include <media/MicrophoneInfo.h>
+#include <android/media/permission/Identity.h>
namespace android {
@@ -226,7 +227,7 @@
public virtual IMediaDeathNotifier
{
public:
- MediaRecorder(const String16& opPackageName);
+ explicit MediaRecorder(const media::permission::Identity& identity);
~MediaRecorder();
void died();
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 1554920..7504787 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -40,8 +40,9 @@
namespace android {
using media::VolumeShaper;
+using media::permission::Identity;
-MediaPlayer::MediaPlayer(const std::string opPackageName) : mOpPackageName(opPackageName)
+MediaPlayer::MediaPlayer(const Identity& identity) : mIdentity(identity)
{
ALOGV("constructor");
mListener = NULL;
@@ -152,7 +153,7 @@
if (url != NULL) {
const sp<IMediaPlayerService> service(getMediaPlayerService());
if (service != 0) {
- sp<IMediaPlayer> player(service->create(this, mAudioSessionId, mOpPackageName));
+ sp<IMediaPlayer> player(service->create(this, mAudioSessionId, mIdentity));
if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
(NO_ERROR != player->setDataSource(httpService, url, headers))) {
player.clear();
@@ -169,7 +170,7 @@
status_t err = UNKNOWN_ERROR;
const sp<IMediaPlayerService> service(getMediaPlayerService());
if (service != 0) {
- sp<IMediaPlayer> player(service->create(this, mAudioSessionId, mOpPackageName));
+ sp<IMediaPlayer> player(service->create(this, mAudioSessionId, mIdentity));
if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
(NO_ERROR != player->setDataSource(fd, offset, length))) {
player.clear();
@@ -185,7 +186,7 @@
status_t err = UNKNOWN_ERROR;
const sp<IMediaPlayerService> service(getMediaPlayerService());
if (service != 0) {
- sp<IMediaPlayer> player(service->create(this, mAudioSessionId, mOpPackageName));
+ sp<IMediaPlayer> player(service->create(this, mAudioSessionId, mIdentity));
if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
(NO_ERROR != player->setDataSource(source))) {
player.clear();
@@ -201,7 +202,7 @@
status_t err = UNKNOWN_ERROR;
const sp<IMediaPlayerService> service(getMediaPlayerService());
if (service != 0) {
- sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
+ sp<IMediaPlayer> player(service->create(this, mAudioSessionId, mIdentity));
if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
(NO_ERROR != player->setDataSource(rtpParams))) {
player.clear();
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index e3cd9d8..da2b190 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -33,6 +33,8 @@
namespace android {
+using media::permission::Identity;
+
status_t MediaRecorder::setCamera(const sp<hardware::ICamera>& camera,
const sp<ICameraRecordingProxy>& proxy)
{
@@ -758,13 +760,13 @@
return INVALID_OPERATION;
}
-MediaRecorder::MediaRecorder(const String16& opPackageName) : mSurfaceMediaSource(NULL)
+MediaRecorder::MediaRecorder(const Identity &identity) : mSurfaceMediaSource(NULL)
{
ALOGV("constructor");
const sp<IMediaPlayerService> service(getMediaPlayerService());
if (service != NULL) {
- mMediaRecorder = service->createMediaRecorder(opPackageName);
+ mMediaRecorder = service->createMediaRecorder(identity);
}
if (mMediaRecorder != NULL) {
mCurrentState = MEDIA_RECORDER_IDLE;
diff --git a/media/libmedia/tests/codeclist/Android.bp b/media/libmedia/tests/codeclist/Android.bp
index 89dddc4..7dd0caa 100644
--- a/media/libmedia/tests/codeclist/Android.bp
+++ b/media/libmedia/tests/codeclist/Android.bp
@@ -25,6 +25,7 @@
cc_test {
name: "CodecListTest",
+ test_suites: ["device-tests"],
gtest: true,
srcs: [
diff --git a/media/libmedia/tests/mediaplayer/Android.bp b/media/libmedia/tests/mediaplayer/Android.bp
index 0fff7b4..50f35ea 100644
--- a/media/libmedia/tests/mediaplayer/Android.bp
+++ b/media/libmedia/tests/mediaplayer/Android.bp
@@ -39,6 +39,7 @@
"libstagefright",
"libstagefright_foundation",
"libutils",
+ "media_permission-aidl-cpp",
],
compile_multilib: "first",
cflags: [
diff --git a/media/libmedia/tests/mediaplayer/IMediaPlayerTest.cpp b/media/libmedia/tests/mediaplayer/IMediaPlayerTest.cpp
index 097e8ef..cc60933 100644
--- a/media/libmedia/tests/mediaplayer/IMediaPlayerTest.cpp
+++ b/media/libmedia/tests/mediaplayer/IMediaPlayerTest.cpp
@@ -31,7 +31,7 @@
static constexpr uint32_t PREPARE_DRM = IMediaPlayer::PREPARE_DRM;
void SetUp() override {
- mediaPlayer_ = new MediaPlayer();
+ mediaPlayer_ = sp<MediaPlayer>::make();
sp<IServiceManager> serviceManager = defaultServiceManager();
sp<IBinder> mediaPlayerService = serviceManager->getService(String16("media.player"));
sp<IMediaPlayerService> iMediaPlayerService =
diff --git a/media/libmediaformatshaper/Android.bp b/media/libmediaformatshaper/Android.bp
new file mode 100644
index 0000000..bdd1465
--- /dev/null
+++ b/media/libmediaformatshaper/Android.bp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+// these headers include the structure of needed function pointers
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_library_headers {
+ name: "libmediaformatshaper_headers",
+ export_include_dirs: ["include"],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media",
+ ],
+ min_sdk_version: "29",
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
+
+cc_defaults {
+ name: "libmediaformatshaper_defaults",
+ srcs: [
+ "CodecProperties.cpp",
+ "CodecSeeding.cpp",
+ "FormatShaper.cpp",
+ "ManageShapingCodecs.cpp",
+ "VideoShaper.cpp",
+ "VQApply.cpp",
+ ],
+
+ local_include_dirs: [
+ "include",
+ ],
+
+ header_libs: [
+ "libstagefright_headers",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libutils",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ "-fvisibility=hidden",
+ "-Wthread-safety", // enables GUARDED_BY()
+ ],
+
+ target: {
+ android: {
+ shared_libs: [
+ "libmediandk#29",
+ ],
+ },
+ },
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
+
+cc_library {
+ name: "libmediaformatshaper",
+ defaults: ["libmediaformatshaper_defaults"],
+
+ min_sdk_version: "29",
+
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media",
+ ],
+
+ version_script: "exports.lds",
+
+}
diff --git a/media/libmediaformatshaper/CodecProperties.cpp b/media/libmediaformatshaper/CodecProperties.cpp
new file mode 100644
index 0000000..c57b693
--- /dev/null
+++ b/media/libmediaformatshaper/CodecProperties.cpp
@@ -0,0 +1,500 @@
+/*
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "CodecProperties"
+#include <utils/Log.h>
+
+#include <string>
+#include <stdlib.h>
+
+#include "CodecProperties.h"
+
+#include <media/stagefright/MediaCodecConstants.h>
+
+
+// we aren't going to mess with shaping points dimensions beyond this
+static const int32_t DIMENSION_LIMIT = 16384;
+
+namespace android {
+namespace mediaformatshaper {
+
+CodecProperties::CodecProperties(std::string name, std::string mediaType) {
+ ALOGV("CodecProperties(%s, %s)", name.c_str(), mediaType.c_str());
+ mName = name;
+ mMediaType = mediaType;
+}
+
+std::string CodecProperties::getName(){
+ return mName;
+}
+
+std::string CodecProperties::getMediaType(){
+ return mMediaType;
+}
+
+int CodecProperties::supportedMinimumQuality() {
+ return mMinimumQuality;
+}
+void CodecProperties::setSupportedMinimumQuality(int vmaf) {
+ mMinimumQuality = vmaf;
+}
+
+void CodecProperties::setMissingQpBoost(double boost) {
+ mMissingQpBoost = boost;
+}
+void CodecProperties::setPhaseOut(double phaseout) {
+ mPhaseOut = phaseout;
+}
+
+// what API is this codec set up for (e.g. API of the associated partition)
+// vendor-side (OEM) codecs may be older, due to 'vendor freeze' and treble
+int CodecProperties::supportedApi() {
+ return mApi;
+}
+
+void CodecProperties::setFeatureValue(std::string key, int32_t value) {
+ ALOGD("setFeatureValue(%s,%d)", key.c_str(), value);
+ mFeatures.insert({key, value});
+
+ if (!strcmp(key.c_str(), FEATURE_QpBounds)) {
+ setSupportsQp(1);
+ } else if (!strcmp(key.c_str(), "video-minimum-quality")) {
+ setSupportedMinimumQuality(1);
+ } else if (!strcmp(key.c_str(), "vq-minimum-quality")) { // from prototyping
+ setSupportedMinimumQuality(1);
+ }
+}
+
+bool CodecProperties::getFeatureValue(std::string key, int32_t *valuep) {
+ ALOGV("getFeatureValue(%s)", key.c_str());
+ if (valuep == nullptr) {
+ return false;
+ }
+ auto mapped = mFeatures.find(key);
+ if (mapped != mFeatures.end()) {
+ *valuep = mapped->second;
+ return true;
+ }
+ return false;
+}
+
+// Tuning values (which differ from Features)
+// this is where we set up things like target bitrates and QP ranges
+// NB the tuning values arrive as a string, allowing us to convert it into an appropriate
+// format (int, float, ranges, other combinations)
+//
+void CodecProperties::setTuningValue(std::string key, std::string value) {
+ ALOGD("setTuningValue(%s,%s)", key.c_str(), value.c_str());
+ mTunings.insert({key, value});
+
+ bool legal = false;
+ // NB: old school strtol() because std::stoi() throws exceptions
+ if (!strcmp(key.c_str(), "vq-target-qpmax")) {
+ const char *p = value.c_str();
+ char *q;
+ int32_t iValue = strtol(p, &q, 0);
+ if (q != p) {
+ setTargetQpMax(iValue);
+ legal = true;
+ }
+ } else if (!strncmp(key.c_str(), "vq-target-qpmax-", strlen("vq-target-qpmax-"))) {
+ std::string resolution = key.substr(strlen("vq-target-qpmax-"));
+ if (qpMaxPoint(resolution, value)) {
+ legal = true;
+ }
+ } else if (!strcmp(key.c_str(), "vq-target-bpp")) {
+ const char *p = value.c_str();
+ char *q;
+ double bpp = strtod(p, &q);
+ if (q != p) {
+ setBpp(bpp);
+ legal = true;
+ }
+ } else if (!strncmp(key.c_str(), "vq-target-bpp-", strlen("vq-target-bpp-"))) {
+ std::string resolution = key.substr(strlen("vq-target-bpp-"));
+ if (bppPoint(resolution, value)) {
+ legal = true;
+ }
+ } else if (!strcmp(key.c_str(), "vq-target-bppx100")) {
+ // legacy, prototyping
+ const char *p = value.c_str();
+ char *q;
+ int32_t iValue = strtol(p, &q, 0);
+ if (q != p) {
+ double bpp = iValue / 100.0;
+ setBpp(bpp);
+ legal = true;
+ }
+ } else if (!strcmp(key.c_str(), "vq-bitrate-phaseout")) {
+ const char *p = value.c_str();
+ char *q;
+ double phaseout = strtod(p, &q);
+ if (q != p) {
+ setPhaseOut(phaseout);
+ legal = true;
+ }
+ } else if (!strcmp(key.c_str(), "vq-boost-missing-qp")) {
+ const char *p = value.c_str();
+ char *q;
+ double boost = strtod(p, &q);
+ if (q != p) {
+ setMissingQpBoost(boost);
+ legal = true;
+ }
+ } else {
+ legal = true;
+ }
+
+ if (!legal) {
+ ALOGW("setTuningValue() unable to apply tuning '%s' with value '%s'",
+ key.c_str(), value.c_str());
+ }
+ return;
+}
+
+bool CodecProperties::getTuningValue(std::string key, std::string &value) {
+ ALOGV("getTuningValue(%s)", key.c_str());
+ auto mapped = mFeatures.find(key);
+ if (mapped != mFeatures.end()) {
+ value = mapped->second;
+ return true;
+ }
+ return false;
+}
+
+bool CodecProperties::bppPoint(std::string resolution, std::string value) {
+
+ int32_t width = 0;
+ int32_t height = 0;
+ double bpp = -1;
+
+ // resolution is "WxH", "W*H" or a standard name like "720p"
+ if (resolution == "1080p") {
+ width = 1080; height = 1920;
+ } else if (resolution == "720p") {
+ width = 720; height = 1280;
+ } else if (resolution == "540p") {
+ width = 540; height = 960;
+ } else if (resolution == "480p") {
+ width = 480; height = 854;
+ } else {
+ size_t sep = resolution.find('x');
+ if (sep == std::string::npos) {
+ sep = resolution.find('*');
+ }
+ if (sep == std::string::npos) {
+ ALOGW("unable to parse resolution: '%s'", resolution.c_str());
+ return false;
+ }
+ std::string w = resolution.substr(0, sep);
+ std::string h = resolution.substr(sep+1);
+
+ char *q;
+ const char *p = w.c_str();
+ width = strtol(p, &q, 0);
+ if (q == p) {
+ width = -1;
+ }
+ p = h.c_str();
+ height = strtol(p, &q, 0);
+ if (q == p) {
+ height = -1;
+ }
+ if (width <= 0 || height <= 0 || width > DIMENSION_LIMIT || height > DIMENSION_LIMIT) {
+ ALOGW("unparseable: width, height '%s'", resolution.c_str());
+ return false;
+ }
+ }
+
+ const char *p = value.c_str();
+ char *q;
+ bpp = strtod(p, &q);
+ if (q == p) {
+ ALOGW("unparseable bpp '%s'", value.c_str());
+ return false;
+ }
+
+ struct bpp_point *point = (struct bpp_point*) malloc(sizeof(*point));
+ if (point == nullptr) {
+ ALOGW("unable to allocate memory for bpp point");
+ return false;
+ }
+
+ point->pixels = width * height;
+ point->width = width;
+ point->height = height;
+ point->bpp = bpp;
+
+ if (mBppPoints == nullptr) {
+ point->next = nullptr;
+ mBppPoints = point;
+ } else if (point->pixels < mBppPoints->pixels) {
+ // at the front
+ point->next = mBppPoints;
+ mBppPoints = point;
+ } else {
+ struct bpp_point *after = mBppPoints;
+ while (after->next) {
+ if (point->pixels > after->next->pixels) {
+ after = after->next;
+ continue;
+ }
+
+ // insert before after->next
+ point->next = after->next;
+ after->next = point;
+ break;
+ }
+ if (after->next == nullptr) {
+ // hasn't gone in yet
+ point->next = nullptr;
+ after->next = point;
+ }
+ }
+
+ return true;
+}
+
+double CodecProperties::getBpp(int32_t width, int32_t height) {
+ // look in the per-resolution list
+
+ int32_t pixels = width * height;
+
+ if (mBppPoints) {
+ struct bpp_point *point = mBppPoints;
+ while (point && point->pixels < pixels) {
+ point = point->next;
+ }
+ if (point) {
+ ALOGV("getBpp(w=%d,h=%d) returns %f from bpppoint w=%d h=%d",
+ width, height, point->bpp, point->width, point->height);
+ return point->bpp;
+ }
+ }
+
+ ALOGV("defaulting to %f bpp", mBpp);
+ return mBpp;
+}
+
+bool CodecProperties::qpMaxPoint(std::string resolution, std::string value) {
+
+ int32_t width = 0;
+ int32_t height = 0;
+ int qpMax = INT32_MAX;
+
+ // resolution is "WxH", "W*H" or a standard name like "720p"
+ if (resolution == "1080p") {
+ width = 1080; height = 1920;
+ } else if (resolution == "720p") {
+ width = 720; height = 1280;
+ } else if (resolution == "540p") {
+ width = 540; height = 960;
+ } else if (resolution == "480p") {
+ width = 480; height = 854;
+ } else {
+ size_t sep = resolution.find('x');
+ if (sep == std::string::npos) {
+ sep = resolution.find('*');
+ }
+ if (sep == std::string::npos) {
+ ALOGW("unable to parse resolution: '%s'", resolution.c_str());
+ return false;
+ }
+ std::string w = resolution.substr(0, sep);
+ std::string h = resolution.substr(sep+1);
+
+ char *q;
+ const char *p = w.c_str();
+ width = strtol(p, &q, 0);
+ if (q == p) {
+ width = -1;
+ }
+ p = h.c_str();
+ height = strtol(p, &q, 0);
+ if (q == p) {
+ height = -1;
+ }
+ if (width <= 0 || height <= 0 || width > DIMENSION_LIMIT || height > DIMENSION_LIMIT) {
+ ALOGW("unparseable: width, height '%s'", resolution.c_str());
+ return false;
+ }
+ }
+
+ const char *p = value.c_str();
+ char *q;
+ qpMax = strtol(p, &q, 0);
+ if (q == p) {
+ ALOGW("unparseable qpmax '%s'", value.c_str());
+ return false;
+ }
+
+ // convert to our internal 'unspecified' notation
+ if (qpMax == -1)
+ qpMax = INT32_MAX;
+
+ struct qpmax_point *point = (struct qpmax_point*) malloc(sizeof(*point));
+ if (point == nullptr) {
+ ALOGW("unable to allocate memory for qpmax point");
+ return false;
+ }
+
+ point->pixels = width * height;
+ point->width = width;
+ point->height = height;
+ point->qpMax = qpMax;
+
+ if (mQpMaxPoints == nullptr) {
+ point->next = nullptr;
+ mQpMaxPoints = point;
+ } else if (point->pixels < mQpMaxPoints->pixels) {
+ // at the front
+ point->next = mQpMaxPoints;
+ mQpMaxPoints = point;
+ } else {
+ struct qpmax_point *after = mQpMaxPoints;
+ while (after->next) {
+ if (point->pixels > after->next->pixels) {
+ after = after->next;
+ continue;
+ }
+
+ // insert before after->next
+ point->next = after->next;
+ after->next = point;
+ break;
+ }
+ if (after->next == nullptr) {
+ // hasn't gone in yet
+ point->next = nullptr;
+ after->next = point;
+ }
+ }
+
+ return true;
+}
+
+int CodecProperties::targetQpMax(int32_t width, int32_t height) {
+ // look in the per-resolution list
+
+ int32_t pixels = width * height;
+
+ if (mQpMaxPoints) {
+ struct qpmax_point *point = mQpMaxPoints;
+ while (point && point->pixels < pixels) {
+ point = point->next;
+ }
+ if (point) {
+ ALOGV("targetQpMax(w=%d,h=%d) returns %d from qpmax_point w=%d h=%d",
+ width, height, point->qpMax, point->width, point->height);
+ return point->qpMax;
+ }
+ }
+
+ ALOGV("defaulting to %d qpmax", mTargetQpMax);
+ return mTargetQpMax;
+}
+
+void CodecProperties::setTargetQpMax(int qpMax) {
+ // convert to our internal 'unspecified' notation
+ if (qpMax == -1)
+ qpMax = INT32_MAX;
+ mTargetQpMax = qpMax;
+}
+
+std::string CodecProperties::getMapping(std::string key, std::string kind) {
+ ALOGV("getMapping(key %s, kind %s )", key.c_str(), kind.c_str());
+ //play with mMappings
+ auto mapped = mMappings.find(kind + "-" + key);
+ if (mapped != mMappings.end()) {
+ std::string result = mapped->second;
+ ALOGV("getMapping(%s, %s) -> %s", key.c_str(), kind.c_str(), result.c_str());
+ return result;
+ }
+ ALOGV("nope, return unchanged key");
+ return key;
+}
+
+
+// really a bit of debugging code here.
+void CodecProperties::showMappings() {
+ ALOGD("Mappings:");
+ int count = 0;
+ for (const auto& [key, value] : mMappings) {
+ count++;
+ ALOGD("'%s' -> '%s'", key.c_str(), value.c_str());
+ }
+ ALOGD("total %d mappings", count);
+}
+
+void CodecProperties::setMapping(std::string kind, std::string key, std::string value) {
+ ALOGV("setMapping(%s,%s,%s)", kind.c_str(), key.c_str(), value.c_str());
+ std::string metaKey = kind + "-" + key;
+ mMappings.insert({metaKey, value});
+}
+
+const char **CodecProperties::getMappings(std::string kind, bool reverse) {
+ ALOGV("getMappings(kind %s, reverse %d", kind.c_str(), reverse);
+ // how many do we need?
+ int count = mMappings.size();
+ if (count == 0) {
+ ALOGV("empty mappings");
+ return nullptr;
+ }
+ size_t size = sizeof(char *) * (2 * count + 2);
+ const char **result = (const char **)malloc(size);
+ if (result == nullptr) {
+ ALOGW("no memory to return mappings");
+ return nullptr;
+ }
+ memset(result, '\0', size);
+
+ const char **pp = result;
+ for (const auto& [key, value] : mMappings) {
+ // split out the kind/key
+ size_t pos = key.find('-');
+ if (pos == std::string::npos) {
+ ALOGD("ignoring malformed key: %s", key.c_str());
+ continue;
+ }
+ std::string actualKind = key.substr(0,pos);
+ if (kind.length() != 0 && kind != actualKind) {
+ ALOGD("kinds don't match: want '%s' got '%s'", kind.c_str(), actualKind.c_str());
+ continue;
+ }
+ if (reverse) {
+ // codec specific -> std aka 'unmapping'
+ pp[0] = strdup( value.c_str());
+ pp[1] = strdup( key.substr(pos+1).c_str());
+ } else {
+ // std -> codec specific
+ pp[0] = strdup( key.substr(pos+1).c_str());
+ pp[1] = strdup( value.c_str());
+ }
+ ALOGV(" %s -> %s", pp[0], pp[1]);
+ pp += 2;
+ }
+
+ pp[0] = nullptr;
+ pp[1] = nullptr;
+
+ return result;
+}
+
+
+} // namespace mediaformatshaper
+} // namespace android
+
diff --git a/media/libmediaformatshaper/CodecProperties.h b/media/libmediaformatshaper/CodecProperties.h
new file mode 100644
index 0000000..196a40f
--- /dev/null
+++ b/media/libmediaformatshaper/CodecProperties.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBMEDIAFORMATSHAPER_CODECPROPERTIES_H_
+#define _LIBMEDIAFORMATSHAPER_CODECPROPERTIES_H_
+
+#include <map>
+#include <mutex>
+#include <string>
+
+#include <inttypes.h>
+
+#include <utils/RefBase.h>
+
+namespace android {
+namespace mediaformatshaper {
+
+class CodecProperties {
+
+ public:
+ CodecProperties(std::string name, std::string mediaType);
+
+ // seed the codec with some preconfigured values
+ // (e.g. mediaType-granularity defaults)
+ // runs from the constructor
+ void Seed();
+ void Finish();
+
+ std::string getName();
+ std::string getMediaType();
+
+ // establish a mapping from standard 'key' to non-standard 'value' in the namespace 'kind'
+ void setMapping(std::string kind, std::string key, std::string value);
+
+ // translate from from standard key to non-standard key
+ // return original standard key if there is no mapping
+ std::string getMapping(std::string key, std::string kind);
+
+ // returns an array of char *, which are paired "from" and "to" values
+ // for mapping (or unmapping). it's always expressed as from->to
+ // and 'reverse' describes which strings are to be on which side.
+ const char **getMappings(std::string kind, bool reverse);
+
+ // keep a map of all features and their parameters
+ void setFeatureValue(std::string key, int32_t value);
+ bool getFeatureValue(std::string key, int32_t *valuep);
+
+ // keep a map of all tunings and their parameters
+ void setTuningValue(std::string key, std::string value);
+ bool getTuningValue(std::string key, std::string &value);
+
+ // does the codec support the Android S minimum quality rules
+ void setSupportedMinimumQuality(int vmaf);
+ int supportedMinimumQuality();
+
+ // qp max bound used to compensate when SupportedMinimumQuality == 0
+ // 0 == let a system default handle it
+ void setTargetQpMax(int qpmax);
+ int targetQpMax(int32_t width, int32_t height);
+
+ // target bits-per-pixel (per second) for encoding operations.
+ // This is used to calculate a minimum bitrate for any particular resolution.
+ // A 1080p (1920*1080 = 2073600 pixels) to be encoded at 5Mbps has a bpp == 2.41
+ void setBpp(double bpp) { mBpp = bpp;}
+ double getBpp(int32_t width, int32_t height);
+
+ // Does this codec support QP bounding
+ // The getMapping() methods provide any needed mapping to non-standard keys.
+ void setSupportsQp(bool supported) { mSupportsQp = supported;}
+ bool supportsQp() { return mSupportsQp;}
+
+ // defines our range of operation -- multiplier on the floor bitrate
+ double getPhaseOut() { return mPhaseOut; }
+ void setPhaseOut(double overageMultiplier);
+
+ // how much (0.20 = +20%) do we add if Qp is requested but unsupported
+ double getMissingQpBoost() {return mMissingQpBoost; }
+ void setMissingQpBoost(double boost);
+
+ int supportedApi();
+
+ // a codec is not usable until it has been registered with its
+ // name/mediaType.
+ bool isRegistered() { return mIsRegistered;}
+ void setRegistered(bool registered) { mIsRegistered = registered;}
+
+ private:
+ std::string mName;
+ std::string mMediaType;
+ int mApi = 0;
+ int mMinimumQuality = 0;
+ int mTargetQpMax = INT32_MAX;
+ bool mSupportsQp = false;
+ double mBpp = 0.0;
+
+ // target bitrates above floor * mPhaseOut are left untouched
+ double mPhaseOut = 1.75;
+ // 20% bump if QP is configured but it is unavailable
+ double mMissingQpBoost = 0.20;
+
+ // allow different target bits-per-pixel based on resolution
+ // similar to codec 'performance points'
+ // uses 'next largest' (by pixel count) point as minimum bpp
+ struct bpp_point {
+ struct bpp_point *next;
+ int32_t pixels;
+ int32_t width, height;
+ double bpp;
+ };
+ struct bpp_point *mBppPoints = nullptr;
+ bool bppPoint(std::string resolution, std::string value);
+
+ // same thing for qpmax -- allow different ones based on resolution
+ // allow different target bits-per-pixel based on resolution
+ // similar to codec 'performance points'
+ // uses 'next largest' (by pixel count) point as minimum bpp
+ struct qpmax_point {
+ struct qpmax_point *next;
+ int32_t pixels;
+ int32_t width, height;
+ int qpMax;
+ };
+ struct qpmax_point *mQpMaxPoints = nullptr;
+ bool qpMaxPoint(std::string resolution, std::string value);
+
+ std::mutex mMappingLock;
+ // XXX figure out why I'm having problems getting compiler to like GUARDED_BY
+ std::map<std::string, std::string> mMappings /*GUARDED_BY(mMappingLock)*/ ;
+
+ std::map<std::string, int32_t> mFeatures /*GUARDED_BY(mMappingLock)*/ ;
+ std::map<std::string, std::string> mTunings /*GUARDED_BY(mMappingLock)*/ ;
+
+ // Seed() and Finish() use this as the underlying implementation
+ void addMediaDefaults(bool overrideable);
+
+ bool mIsRegistered = false;
+
+ // debugging of what's in the mapping dictionary
+ void showMappings();
+
+ // DISALLOW_EVIL_CONSTRUCTORS(CodecProperties);
+};
+
+extern CodecProperties *findCodec(const char *codecName, const char *mediaType);
+extern CodecProperties *registerCodec(CodecProperties *codec, const char *codecName,
+ const char *mediaType);
+
+
+} // namespace mediaformatshaper
+} // namespace android
+
+#endif // _LIBMEDIAFORMATSHAPER_CODECPROPERTIES_H_
diff --git a/media/libmediaformatshaper/CodecSeeding.cpp b/media/libmediaformatshaper/CodecSeeding.cpp
new file mode 100644
index 0000000..cc241f4
--- /dev/null
+++ b/media/libmediaformatshaper/CodecSeeding.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "CodecSeeding"
+#include <utils/Log.h>
+
+#include <string>
+
+#include "CodecProperties.h"
+
+namespace android {
+namespace mediaformatshaper {
+
+/*
+ * a block of pre-loaded tunings for codecs.
+ *
+ * things the library seeds into the codecproperties based
+ * on the mediaType.
+ * XXX: parsing from a file is likely better than embedding in code.
+ */
+typedef struct {
+ bool overrideable;
+ const char *key;
+ const char *value;
+} preloadTuning_t;
+
+typedef struct {
+ const char *mediaType;
+ preloadTuning_t *features;
+} preloadTunings_t;
+
+/*
+ * bpp == bits per pixel per second, for 30fps.
+ */
+
+static preloadTuning_t featuresAvc[] = {
+ {true, "vq-target-bpp", "0"},
+ {true, "vq-target-bpp-1080p", "1.90"},
+ {true, "vq-target-bpp-720p", "2.25"},
+ {true, "vq-target-bpp-540p", "2.65"},
+ {true, "vq-target-bpp-480p", "3.00"},
+ {true, "vq-target-qpmax", "-1"},
+ {true, "vq-target-qpmax-1080p", "45"},
+ {true, "vq-target-qpmax-720p", "43"},
+ {true, "vq-target-qpmax-540p", "42"},
+ {true, "vq-target-qpmax-480p", "38"},
+ {true, "vq-bitrate-phaseout", "1.75"},
+ {true, "vq-boost-missing-qp", "0.20"},
+ {true, nullptr, 0}
+};
+
+static preloadTuning_t featuresHevc[] = {
+ {true, "vq-target-bpp", "0"},
+ {true, "vq-target-bpp-1080p", "1.50"},
+ {true, "vq-target-bpp-720p", "1.80"},
+ {true, "vq-target-bpp-540p", "2.10"},
+ {true, "vq-target-bpp-480p", "2.30"},
+ {true, "vq-target-qpmax", "-1"},
+ {true, "vq-target-qpmax-1080p", "45"},
+ {true, "vq-target-qpmax-720p", "44"},
+ {true, "vq-target-qpmax-540p", "43"},
+ {true, "vq-target-qpmax-480p", "42"},
+ {true, "vq-bitrate-phaseout", "1.75"},
+ {true, "vq-boost-missing-qp", "0.20"},
+ {true, nullptr, 0}
+};
+
+static preloadTuning_t featuresGenericVideo[] = {
+ // 0 == off
+ {true, "vq-target-bpp", "0"},
+ {true, nullptr, 0}
+};
+
+static preloadTunings_t preloadTunings[] = {
+ { "video/avc", featuresAvc},
+ { "video/hevc", &featuresHevc[0]},
+
+ // wildcard for any video format not already captured
+ { "video/*", &featuresGenericVideo[0]},
+
+ { nullptr, nullptr}
+};
+
+void CodecProperties::addMediaDefaults(bool overrideable) {
+ ALOGD("Seed: codec %s, mediatype %s, overrideable %d",
+ mName.c_str(), mMediaType.c_str(), overrideable);
+
+ // load me up with initial configuration data
+ int count = 0;
+ for (int i = 0; ; i++) {
+ preloadTunings_t *p = &preloadTunings[i];
+ if (p->mediaType == nullptr) {
+ break;
+ }
+ bool found = false;
+ if (strcmp(p->mediaType, mMediaType.c_str()) == 0) {
+ found = true;
+ }
+ const char *r;
+ if (!found && (r = strchr(p->mediaType, '*')) != NULL) {
+ // wildcard; check the prefix
+ size_t len = r - p->mediaType;
+ if (strncmp(p->mediaType, mMediaType.c_str(), len) == 0) {
+ found = true;
+ }
+ }
+
+ if (!found) {
+ continue;
+ }
+ ALOGV("seeding from mediaType '%s'", p->mediaType);
+
+ // walk through, filling things
+ if (p->features != nullptr) {
+ for (int j=0;; j++) {
+ preloadTuning_t *q = &p->features[j];
+ if (q->key == nullptr) {
+ break;
+ }
+ if (q->overrideable != overrideable) {
+ continue;
+ }
+ setTuningValue(q->key, q->value);
+ count++;
+ }
+ break;
+ }
+ }
+ ALOGV("loaded %d preset values", count);
+}
+
+// a chance, as we create the codec to inject any default behaviors we want.
+// XXX: consider whether we need pre/post or just post. it affects what can be
+// overridden by way of the codec XML
+//
+void CodecProperties::Seed() {
+ ALOGV("Seed: for codec %s, mediatype %s", mName.c_str(), mMediaType.c_str());
+ addMediaDefaults(true);
+}
+
+void CodecProperties::Finish() {
+ ALOGV("Finish: for codec %s, mediatype %s", mName.c_str(), mMediaType.c_str());
+ addMediaDefaults(false);
+}
+
+} // namespace mediaformatshaper
+} // namespace android
+
diff --git a/media/libmediaformatshaper/FormatShaper.cpp b/media/libmediaformatshaper/FormatShaper.cpp
new file mode 100644
index 0000000..451f772
--- /dev/null
+++ b/media/libmediaformatshaper/FormatShaper.cpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "FormatShaper"
+#include <utils/Log.h>
+
+#include <string>
+#include <inttypes.h>
+
+#include <media/NdkMediaFormat.h>
+
+#include "CodecProperties.h"
+#include "VideoShaper.h"
+#include "VQops.h"
+
+#include <media/formatshaper/FormatShaper.h>
+
+namespace android {
+namespace mediaformatshaper {
+
+//
+// Caller retains ownership of and responsibility for inFormat
+//
+
+//
+// the interface to the outside
+//
+
+int shapeFormat(shaperHandle_t shaper, AMediaFormat* inFormat, int flags) {
+ CodecProperties *codec = (CodecProperties*) shaper;
+ if (codec == nullptr) {
+ return -1;
+ }
+ if (!codec->isRegistered()) {
+ return -1;
+ }
+
+ // run through the list of possible transformations
+ //
+
+ std::string mediaType = codec->getMediaType();
+ if (strncmp(mediaType.c_str(), "video/", 6) == 0) {
+ // video specific shaping
+ (void) videoShaper(codec, inFormat, flags);
+
+ } else if (strncmp(mediaType.c_str(), "audio/", 6) == 0) {
+ // audio specific shaping
+
+ } else {
+ ALOGV("unknown mediatype '%s', left untouched", mediaType.c_str());
+
+ }
+
+ return 0;
+}
+
+int setMap(shaperHandle_t shaper, const char *kind, const char *key, const char *value) {
+ ALOGV("setMap: kind %s key %s -> value %s", kind, key, value);
+ CodecProperties *codec = (CodecProperties*) shaper;
+ if (codec == nullptr) {
+ return -1;
+ }
+ // must not yet be registered
+ if (codec->isRegistered()) {
+ return -1;
+ }
+
+ codec->setMapping(kind, key, value);
+ return 0;
+}
+
+int setFeature(shaperHandle_t shaper, const char *feature, int value) {
+ ALOGV("set_feature: feature %s value %d", feature, value);
+ CodecProperties *codec = (CodecProperties*) shaper;
+ if (codec == nullptr) {
+ return -1;
+ }
+ // must not yet be registered
+ if (codec->isRegistered()) {
+ return -1;
+ }
+
+ // save a map of all features
+ codec->setFeatureValue(feature, value);
+
+ return 0;
+}
+
+int setTuning(shaperHandle_t shaper, const char *tuning, const char *value) {
+ ALOGV("setTuning: tuning %s value %s", tuning, value);
+ CodecProperties *codec = (CodecProperties*) shaper;
+ if (codec == nullptr) {
+ return -1;
+ }
+ // must not yet be registered
+ if (codec->isRegistered()) {
+ return -1;
+ }
+
+ // save a map of all features
+ codec->setTuningValue(tuning, value);
+
+ return 0;
+}
+
+/*
+ * The routines that manage finding, creating, and registering the shapers.
+ */
+
+shaperHandle_t findShaper(const char *codecName, const char *mediaType) {
+ CodecProperties *codec = findCodec(codecName, mediaType);
+ return (shaperHandle_t) codec;
+}
+
+shaperHandle_t createShaper(const char *codecName, const char *mediaType) {
+ CodecProperties *codec = new CodecProperties(codecName, mediaType);
+ if (codec != nullptr) {
+ codec->Seed();
+ }
+ return (shaperHandle_t) codec;
+}
+
+shaperHandle_t registerShaper(shaperHandle_t shaper, const char *codecName, const char *mediaType) {
+ ALOGV("registerShaper(handle, codecName %s, mediaType %s", codecName, mediaType);
+ CodecProperties *codec = (CodecProperties*) shaper;
+ if (codec == nullptr) {
+ return nullptr;
+ }
+ // must not yet be registered
+ if (codec->isRegistered()) {
+ return nullptr;
+ }
+
+ // any final cleanup for the parameters. This allows us to override
+ // bad parameters from a devices XML file.
+ codec->Finish();
+
+ // may return a different codec, if we lost a race.
+ // if so, registerCodec() reclaims the one we tried to register for us.
+ codec = registerCodec(codec, codecName, mediaType);
+ return (shaperHandle_t) codec;
+}
+
+// mapping & unmapping
+// give me the mappings for 'kind'.
+// kind==null (or empty string), means *all* mappings
+
+const char **getMappings(shaperHandle_t shaper, const char *kind) {
+ CodecProperties *codec = (CodecProperties*) shaper;
+ if (codec == nullptr)
+ return nullptr;
+ if (kind == nullptr)
+ kind = "";
+
+ return codec->getMappings(kind, /* reverse */ false);
+}
+
+const char **getReverseMappings(shaperHandle_t shaper, const char *kind) {
+ CodecProperties *codec = (CodecProperties*) shaper;
+ if (codec == nullptr)
+ return nullptr;
+ if (kind == nullptr)
+ kind = "";
+
+ return codec->getMappings(kind, /* reverse */ true);
+}
+
+
+// the system grabs this structure
+__attribute__ ((visibility ("default")))
+extern "C" FormatShaperOps_t shaper_ops = {
+ .version = SHAPER_VERSION_V1,
+
+ .findShaper = findShaper,
+ .createShaper = createShaper,
+ .setMap = setMap,
+ .setFeature = setFeature,
+ .registerShaper = registerShaper,
+
+ .shapeFormat = shapeFormat,
+ .getMappings = getMappings,
+ .getReverseMappings = getReverseMappings,
+
+ .setTuning = setTuning,
+};
+
+} // namespace mediaformatshaper
+} // namespace android
+
diff --git a/media/libmediaformatshaper/ManageShapingCodecs.cpp b/media/libmediaformatshaper/ManageShapingCodecs.cpp
new file mode 100644
index 0000000..3061d0b
--- /dev/null
+++ b/media/libmediaformatshaper/ManageShapingCodecs.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ManageShapingCodecs"
+#include <utils/Log.h>
+
+#include <mutex>
+#include <string>
+#include <inttypes.h>
+
+#include <media/NdkMediaFormat.h>
+
+#include "CodecProperties.h"
+
+namespace android {
+namespace mediaformatshaper {
+
+// manage the list of codec information.
+//
+// XXX: the mutex here is too heavy; rework that.
+//
+
+static std::mutex sCodecMutex;
+static std::map<std::string, CodecProperties*> sCodecTraits;
+
+CodecProperties *findCodec(const char *codecName, const char *mediaType) {
+ CodecProperties *codec = nullptr;
+
+ // synthesize a name from both codecName + mediaType
+ // some codecs support multiple media types and may have different capabilities
+ // for each media type
+ //
+ std::string codecKey = codecName;
+ codecKey += "-";
+ codecKey += mediaType;
+
+ std::lock_guard _l(sCodecMutex);
+
+ auto it = sCodecTraits.find(codecKey);
+ if (it != sCodecTraits.end()) {
+ codec = it->second;
+ }
+
+ return codec;
+}
+
+CodecProperties *registerCodec(CodecProperties *codec, const char *codecName,
+ const char *mediaType) {
+
+ CodecProperties *registeredCodec = nullptr;
+
+ if (codec->isRegistered()) {
+ return nullptr;
+ }
+
+ // synthesize a name from both codecName + mediaType
+ // some codecs support multiple media types and may have different capabilities
+ // for each media type
+ //
+ std::string codecKey = codecName;
+ codecKey += "-";
+ codecKey += mediaType;
+
+ std::lock_guard _l(sCodecMutex);
+
+ auto it = sCodecTraits.find(codecKey);
+ if (it != sCodecTraits.end()) {
+ registeredCodec = it->second;
+ }
+
+ if (registeredCodec == nullptr) {
+ // register the one that was passed to us
+ ALOGV("Creating entry for codec %s, mediaType %s, key %s", codecName, mediaType,
+ codecKey.c_str());
+ sCodecTraits.insert({codecKey, codec});
+ registeredCodec = codec;
+ codec->setRegistered(true);
+ } else {
+ // one has already been registered, use that
+ // and discard the candidate
+ delete codec;
+ codec = nullptr;
+ }
+
+ return registeredCodec;
+}
+
+} // namespace mediaformatshaper
+} // namespace android
+
diff --git a/media/libmediaformatshaper/VQApply.cpp b/media/libmediaformatshaper/VQApply.cpp
new file mode 100644
index 0000000..585ec6c
--- /dev/null
+++ b/media/libmediaformatshaper/VQApply.cpp
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "VQApply"
+#include <utils/Log.h>
+
+#include <string>
+#include <inttypes.h>
+
+#include <media/NdkMediaFormat.h>
+
+#include "VQops.h"
+#include "CodecProperties.h"
+#include "VideoShaper.h"
+
+namespace android {
+namespace mediaformatshaper {
+
+
+// these are all NDK#31 and we run as NDK#29 (to be within the module)
+// the __builtin_available(android 31, *) constructs didn't work for me.
+//
+#define AMEDIAFORMAT_VIDEO_QP_MAX "video-qp-max"
+#define AMEDIAFORMAT_VIDEO_QP_MIN "video-qp-min"
+
+#define AMEDIAFORMAT_VIDEO_QP_B_MAX "video-qp-b-max"
+#define AMEDIAFORMAT_VIDEO_QP_B_MIN "video-qp-b-min"
+#define AMEDIAFORMAT_VIDEO_QP_I_MAX "video-qp-i-max"
+#define AMEDIAFORMAT_VIDEO_QP_I_MIN "video-qp-i-min"
+#define AMEDIAFORMAT_VIDEO_QP_P_MAX "video-qp-p-max"
+#define AMEDIAFORMAT_VIDEO_QP_P_MIN "video-qp-p-min"
+
+// defined in the SDK, but not in the NDK
+//
+static const int BITRATE_MODE_VBR = 1;
+
+
+//
+// Caller retains ownership of and responsibility for inFormat
+//
+int VQApply(CodecProperties *codec, vqOps_t *info, AMediaFormat* inFormat, int flags) {
+ ALOGV("codecName %s inFormat %p flags x%x", codec->getName().c_str(), inFormat, flags);
+ (void) info; // unused for now
+
+ int32_t bitRateMode = -1;
+ if (AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BITRATE_MODE, &bitRateMode)
+ && bitRateMode != BITRATE_MODE_VBR) {
+ ALOGD("minquality: applies only to VBR encoding");
+ return 0;
+ }
+
+ if (codec->supportedMinimumQuality() > 0) {
+ // allow the codec provided minimum quality behavior to work at it
+ ALOGD("minquality: codec claims to implement minquality=%d",
+ codec->supportedMinimumQuality());
+ return 0;
+ }
+
+ //
+ // consider any and all tools available
+ // -- qp
+ // -- minimum bits-per-pixel
+ //
+ int64_t bitrateChosen = 0;
+ int32_t qpChosen = INT32_MAX;
+
+ int64_t bitrateConfigured = 0;
+ int32_t bitrateConfiguredTmp = 0;
+ (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, &bitrateConfiguredTmp);
+ bitrateConfigured = bitrateConfiguredTmp;
+ bitrateChosen = bitrateConfigured;
+
+ int32_t width = 0;
+ (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_WIDTH, &width);
+ int32_t height = 0;
+ (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_HEIGHT, &height);
+ int64_t pixels = ((int64_t)width) * height;
+ double minimumBpp = codec->getBpp(width, height);
+
+ int64_t bitrateFloor = pixels * minimumBpp;
+ int64_t bitrateCeiling = bitrateFloor * codec->getPhaseOut();
+ if (bitrateFloor > INT32_MAX) bitrateFloor = INT32_MAX;
+ if (bitrateCeiling > INT32_MAX) bitrateCeiling = INT32_MAX;
+
+ // if we are far enough above the target bpp, leave it alone
+ //
+ ALOGV("bitrate: configured %" PRId64 " floor %" PRId64, bitrateConfigured, bitrateFloor);
+ if (bitrateConfigured >= bitrateCeiling) {
+ ALOGV("high enough bitrate: configured %" PRId64 " >= ceiling %" PRId64,
+ bitrateConfigured, bitrateCeiling);
+ return 0;
+ }
+
+ // raise anything below the bitrate floor
+ if (bitrateConfigured < bitrateFloor) {
+ ALOGD("raise bitrate: configured %" PRId64 " to floor %" PRId64,
+ bitrateConfigured, bitrateFloor);
+ bitrateChosen = bitrateFloor;
+ }
+
+ bool qpPresent = hasQpMax(inFormat);
+
+ // calculate a target QP value
+ int32_t qpmax = codec->targetQpMax(width, height);
+ if (!qpPresent) {
+ // user didn't, so shaper wins
+ if (qpmax != INT32_MAX) {
+ ALOGV("choosing qp=%d", qpmax);
+ qpChosen = qpmax;
+ }
+ } else if (qpmax == INT32_MAX) {
+ // shaper didn't so user wins
+ qpChosen = INT32_MAX;
+ AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, &qpChosen);
+ } else {
+ // both sides want it, choose most restrictive
+ int32_t value = INT32_MAX;
+ AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, &value);
+ qpChosen = std::min(qpmax, value);
+ }
+
+ // if QP is desired but not supported, compensate with additional bits
+ if (!codec->supportsQp()) {
+ if (qpChosen != INT32_MAX) {
+ int64_t boost = bitrateChosen * codec->getMissingQpBoost();
+ ALOGD("minquality: requested QP unsupported, boost bitrate %" PRId64 " by %" PRId64,
+ bitrateChosen, boost);
+ bitrateChosen = bitrateChosen + boost;
+ qpChosen = INT32_MAX;
+ }
+ }
+
+ // limits
+ // apply our chosen values
+ //
+ if (qpChosen != INT32_MAX) {
+ ALOGD("minquality by QP: inject %s=%d", AMEDIAFORMAT_VIDEO_QP_MAX, qpChosen);
+ AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, qpChosen);
+
+ // caller (VideoShaper) handles spreading this across the subframes
+ }
+
+ if (bitrateChosen != bitrateConfigured) {
+ if (bitrateChosen > bitrateCeiling) {
+ ALOGD("minquality: bitrate increase clamped at ceiling %" PRId64, bitrateCeiling);
+ bitrateChosen = bitrateCeiling;
+ }
+ ALOGD("minquality/target bitrate raised from %" PRId64 " to %" PRId64 " bps",
+ bitrateConfigured, bitrateChosen);
+ AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, (int32_t)bitrateChosen);
+ }
+
+ return 0;
+}
+
+
+bool hasQpMaxPerFrameType(AMediaFormat *format) {
+ int32_t value;
+
+ if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &value)
+ || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &value)) {
+ return true;
+ }
+ if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &value)
+ || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &value)) {
+ return true;
+ }
+ if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &value)
+ || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &value)) {
+ return true;
+ }
+ return false;
+}
+
+bool hasQpMaxGlobal(AMediaFormat *format) {
+ int32_t value;
+ if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MAX, &value)
+ || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MIN, &value)) {
+ return true;
+ }
+ return false;
+}
+
+bool hasQpMax(AMediaFormat *format) {
+ if (hasQpMaxGlobal(format)) {
+ return true;
+ }
+ return hasQpMaxPerFrameType(format);
+}
+
+void qpSpreadPerFrameType(AMediaFormat *format, int delta,
+ int qplow, int qphigh, bool override) {
+
+ qpSpreadMinPerFrameType(format, qplow, override);
+ qpSpreadMaxPerFrameType(format, delta, qphigh, override);
+ // make sure that min<max for all the QP fields.
+ qpVerifyMinMaxOrdering(format);
+}
+
+void qpSpreadMaxPerFrameType(AMediaFormat *format, int delta, int qphigh, bool override) {
+ ALOGV("format %p delta %d hi %d override %d", format, delta, qphigh, override);
+
+ int32_t qpOffered = 0;
+ if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MAX, &qpOffered)) {
+ // propagate to frame-specific keys, choosing most restrictive
+ // ensure that we don't violate min<=max rules
+ {
+ int32_t maxI = INT32_MAX;
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &maxI);
+ int32_t value = std::min({qpOffered, qphigh, maxI});
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, value);
+ }
+ {
+ int32_t maxP = INT32_MAX;
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &maxP);
+ int32_t value = std::min({(std::min(qpOffered, INT32_MAX-1*delta) + 1*delta),
+ qphigh, maxP});
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, value);
+ }
+ {
+ int32_t maxB = INT32_MAX;
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &maxB);
+ int32_t value = std::min({(std::min(qpOffered, INT32_MAX-2*delta) + 2*delta),
+ qphigh, maxB});
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, value);
+ }
+ }
+}
+
+void qpSpreadMinPerFrameType(AMediaFormat *format, int qplow, bool override) {
+ ALOGV("format %p lo %d override %d", format, qplow, override);
+
+ int32_t qpOffered = 0;
+ if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MIN, &qpOffered)) {
+ int value = std::max(qplow, qpOffered);
+ // propagate to frame-specific keys, use lowest of this and existing per-frame value
+ int32_t minI = INT32_MAX;
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &minI);
+ int32_t setI = std::min(value, minI);
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, setI);
+
+ int32_t minP = INT32_MAX;
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &minP);
+ int32_t setP = std::min(value, minP);
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, setP);
+
+ int32_t minB = INT32_MAX;
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &minB);
+ int32_t setB = std::min(value, minB);
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, setB);
+ }
+}
+
+// XXX whether we allow min==max, or if we'll insist that min<max
+void qpVerifyMinMaxOrdering(AMediaFormat *format) {
+ // ensure that we don't violate min<=max rules
+ int32_t maxI = INT32_MAX;
+ int32_t minI = INT32_MIN;
+ if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &maxI)
+ && AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &minI)
+ && minI > maxI) {
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, maxI);
+ }
+ int32_t maxP = INT32_MAX;
+ int32_t minP = INT32_MIN;
+ if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &maxP)
+ && AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &minP)
+ && minP > maxP) {
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, maxP);
+ }
+ int32_t maxB = INT32_MAX;
+ int32_t minB = INT32_MIN;
+ if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &maxB)
+ && AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &minB)
+ && minB > maxB) {
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, maxB);
+ }
+}
+
+} // namespace mediaformatshaper
+} // namespace android
+
diff --git a/media/libmediaformatshaper/VQops.h b/media/libmediaformatshaper/VQops.h
new file mode 100644
index 0000000..74cce18
--- /dev/null
+++ b/media/libmediaformatshaper/VQops.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEDIAFORMATSHAPER_VQOPS_H_
+#define LIBMEDIAFORMATSHAPER_VQOPS_H_
+
+#include "CodecProperties.h"
+#include <media/NdkMediaFormat.h>
+
+namespace android {
+namespace mediaformatshaper {
+
+// parameterized info for the different mediaType types
+typedef struct {
+ const char *mediaType;
+
+ int32_t qpMin; // codec type limit (e.g. h264, not c2.android.avc.encoder)
+ int32_t qpMax;
+ int32_t qpDelta; // from I to P to B
+
+} vqOps_t;
+
+int VQApply(CodecProperties *codec, vqOps_t *info, AMediaFormat* inFormat, int flags);
+
+// spread the overall QP setting to any un-set per-frame-type settings
+void qpSpreadPerFrameType(AMediaFormat *format, int delta, int qplow, int qphigh, bool override);
+void qpSpreadMaxPerFrameType(AMediaFormat *format, int delta, int qphigh, bool override);
+void qpSpreadMinPerFrameType(AMediaFormat *format, int qplow, bool override);
+void qpVerifyMinMaxOrdering(AMediaFormat *format);
+
+// does the format have QP bounding entries
+bool hasQpMax(AMediaFormat *format);
+bool hasQpMaxGlobal(AMediaFormat *format);
+bool hasQpMaxPerFrameType(AMediaFormat *format);
+
+} // namespace mediaformatshaper
+} // namespace android
+
+#endif // LIBMEDIAFORMATSHAPER_VQOPS_H_
diff --git a/media/libmediaformatshaper/VideoShaper.cpp b/media/libmediaformatshaper/VideoShaper.cpp
new file mode 100644
index 0000000..cf8b50f
--- /dev/null
+++ b/media/libmediaformatshaper/VideoShaper.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "VideoShaper"
+#include <utils/Log.h>
+
+#include <string>
+#include <inttypes.h>
+
+#include <media/NdkMediaFormat.h>
+
+#include "CodecProperties.h"
+#include "VideoShaper.h"
+#include "VQops.h"
+
+namespace android {
+namespace mediaformatshaper {
+
+// mediatype-specific operations
+
+vqOps_t mediaInfo[] = {
+ {
+ .mediaType = "video/avc",
+ .qpMin = 0,
+ .qpMax = 51,
+ .qpDelta = 3,
+ },
+ {
+ .mediaType = "video/hevc",
+ .qpMin = 0,
+ .qpMax = 51,
+ .qpDelta = 3,
+ },
+ {
+ .mediaType = NULL, // matches everything, it must come last
+ .qpMin = INT32_MIN,
+ .qpMax = INT32_MAX,
+ .qpDelta = 3,
+ }
+};
+int nMediaInfos = sizeof(mediaInfo) / sizeof(mediaInfo[0]);
+
+//
+// Caller retains ownership of and responsibility for inFormat
+//
+
+int videoShaper(CodecProperties *codec, AMediaFormat* inFormat, int flags) {
+ if (codec == nullptr) {
+ return -1;
+ }
+ ALOGV("codec %s inFormat %p flags x%x", codec->getName().c_str(), inFormat, flags);
+
+ int ix;
+
+ std::string mediaType = codec->getMediaType();
+ // we should always come out of this with a selection, because the final entry
+ // is deliberaly a NULL -- so that it will act as a default
+ for(ix = 0; mediaInfo[ix].mediaType != NULL; ix++) {
+ if (strcmp(mediaType.c_str(), mediaInfo[ix].mediaType) == 0) {
+ break;
+ }
+ }
+ if (ix >= nMediaInfos) {
+ // shouldn't happen, but if it does .....
+ }
+
+ vqOps_t *info = &mediaInfo[ix];
+
+ // apply any quality transforms in here..
+ (void) VQApply(codec, info, inFormat, flags);
+
+ // We always spread any QP parameters.
+ // Sometimes it's something we inserted here, sometimes it's a value that the user injected.
+ //
+ qpSpreadPerFrameType(inFormat, info->qpDelta, info->qpMin, info->qpMax, /* override */ true);
+
+ //
+ return 0;
+
+}
+
+} // namespace mediaformatshaper
+} // namespace android
+
diff --git a/media/libmediaformatshaper/VideoShaper.h b/media/libmediaformatshaper/VideoShaper.h
new file mode 100644
index 0000000..53f1b13
--- /dev/null
+++ b/media/libmediaformatshaper/VideoShaper.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEDIAFORMATSHAPER_VIDEOSHAPER_H_
+#define LIBMEDIAFORMATSHAPER_VIDEOSHAPER_H_
+
+namespace android {
+namespace mediaformatshaper {
+
+/*
+ * runs through video-specific shaping operations for the codec/format combination.
+ * updates inFormat in place.
+ */
+int videoShaper(CodecProperties *codec, AMediaFormat* inFormat, int flags);
+
+} // namespace mediaformatshaper
+} // namespace android
+
+#endif // LIBMEDIAFORMATSHAPER_VIDEOSHAPER_H_
diff --git a/media/libmediaformatshaper/exports.lds b/media/libmediaformatshaper/exports.lds
new file mode 100644
index 0000000..a29cadb
--- /dev/null
+++ b/media/libmediaformatshaper/exports.lds
@@ -0,0 +1,6 @@
+{
+ global:
+ shaper_ops;
+ local:
+ *;
+};
diff --git a/media/libmediaformatshaper/include/media/formatshaper/FormatShaper.h b/media/libmediaformatshaper/include/media/formatshaper/FormatShaper.h
new file mode 100644
index 0000000..a1747cc
--- /dev/null
+++ b/media/libmediaformatshaper/include/media/formatshaper/FormatShaper.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * structure defining the function pointers that system-side folks
+ * use to invoke operations within the MediaFormat shaping library
+ *
+ * This is the include file the outside world uses.
+ */
+
+#ifndef LIBMEDIAFORMATSHAPER_FORMATSHAPER_H_
+#define LIBMEDIAFORMATSHAPER_FORMATSHAPER_H_
+
+namespace android {
+namespace mediaformatshaper {
+
+/*
+ * An opaque handle clients use to refer to codec+mediatype being shaped.
+ */
+typedef void (*shaperHandle_t);
+
+/*
+ * shapeFormat applies any re-shaping on the passed AMediaFormat.
+ * The updated format is returned in-place.
+ */
+typedef int (*shapeFormat_t)(shaperHandle_t shaperHandle,
+ AMediaFormat* inFormat, int flags);
+
+/*
+ * getMapping returns any mappings from standard keys to codec-specific keys.
+ * The return is a vector of const char* which are set up in pairs
+ * of "from", and "to".
+ * This array is always finished with a pair of nulls (to indicate a null from
+ * and a null to)
+ */
+
+typedef const char **(*getMappings_t)(shaperHandle_t shaperHandle, const char *kind);
+
+/*
+ * Returns a handle to the shaperHandle for the specified codec and mediatype.
+ * If none exists, it returns null.
+ */
+typedef shaperHandle_t (*findShaper_t)(const char *codecName, const char *mediaType);
+
+/*
+ * Creates and returns an empty shaperHandle that the client can populate using the
+ * setFeature() and setMap() operations.
+ */
+typedef shaperHandle_t (*createShaper_t)(const char *codecName, const char *mediaType);
+
+/*
+ * Registers the indicated shaperHandle for the indicated codec and mediatype.
+ * This call returns the shaperHandle that is to be used for further shaper operations.
+ * The returned value may be different than the one passed as an argument if another
+ * shaperinfo was registered while the passed one was being configured.
+ */
+typedef shaperHandle_t (*registerShaper_t)(shaperHandle_t shaper, const char *codecName,
+ const char *mediaType);
+
+/*
+ * establishes a mapping between the standard key "from" and the codec-specific key "to"
+ * in the "kind" namespace. This mapping is specific to the indicated codecName when
+ * encoding for the indicated mediaType.
+ */
+typedef int (*setMap_t)(shaperHandle_t shaper, const char *kind, const char *from, const char *to);
+
+/*
+ * establishes that codec "codecName" encoding for "mediaType" supports the indicated
+ * feature at the indicated value
+ */
+typedef int (*setFeature_t)(shaperHandle_t shaper, const char *feature, int value);
+
+/*
+ * establishes that codec "codecName" encoding for "mediaType" supports the indicated
+ * tuning at the indicated value
+ */
+typedef int (*setTuning_t)(shaperHandle_t shaper, const char *feature, const char * value);
+
+/*
+ * The expectation is that the client will implement a flow similar to the following when
+ * setting up an encoding.
+ *
+ * if ((shaper=formatShaperops->findShaper(codecName, mediaType)) == NULL) {
+ * for (all codec features) {
+ * get feature name, feature value
+ * formatShaperops->setFeature(shaper,, featurename, featurevalue)
+ * }
+ * for (all codec mappings) {
+ * get mapping 'kind', mapping 'from', mapping 'to'
+ * formatShaperops->setMap(shaper, kind, from, to)
+ * }
+ * }
+ *
+ */
+
+typedef struct FormatShaperOps {
+ const uint32_t version;
+
+ /*
+ * find, create, setup, and register the shaper info
+ */
+ findShaper_t findShaper;
+ createShaper_t createShaper;
+ setMap_t setMap;
+ setFeature_t setFeature;
+ registerShaper_t registerShaper;
+
+ /*
+ * use the shaper info
+ */
+ shapeFormat_t shapeFormat;
+ getMappings_t getMappings;
+ getMappings_t getReverseMappings;
+
+ setTuning_t setTuning;
+
+ // additions happen at the end of the structure
+} FormatShaperOps_t;
+
+// versioninf information
+const uint32_t SHAPER_VERSION_UNKNOWN = 0;
+const uint32_t SHAPER_VERSION_V1 = 1;
+
+} // namespace mediaformatshaper
+} // namespace android
+
+#endif // LIBMEDIAFORMATSHAPER_FORMATSHAPER_H_
diff --git a/media/libmediahelper/Android.bp b/media/libmediahelper/Android.bp
index 64ec91b..9b54199 100644
--- a/media/libmediahelper/Android.bp
+++ b/media/libmediahelper/Android.bp
@@ -18,6 +18,12 @@
enabled: false,
},
},
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.bluetooth.updatable",
+ "com.android.media",
+ "com.android.media.swcodec",
+ ],
}
cc_library {
@@ -29,6 +35,7 @@
double_loadable: true,
srcs: [
"AudioParameter.cpp",
+ "AudioValidator.cpp",
"TypeConverter.cpp",
],
cflags: [
diff --git a/media/libmediahelper/AudioParameter.cpp b/media/libmediahelper/AudioParameter.cpp
index 73c1e41..382a920 100644
--- a/media/libmediahelper/AudioParameter.cpp
+++ b/media/libmediahelper/AudioParameter.cpp
@@ -57,10 +57,10 @@
// AUDIO_PARAMETER_DEVICE_SUP_ENCAPSULATION_MODES;
// const char * const AudioParameter::keyDeviceSupportedEncapsulationMetadataTypes =
// AUDIO_PARAMETER_DEVICE_SUP_ENCAPSULATION_METADATA_TYPES;
-// const char * const AudioParameter::keyAdditionalOutputDeviceDelay =
-// AUDIO_PARAMETER_DEVICE_ADDITIONAL_OUTPUT_DELAY;
-// const char * const AudioParameter::keyMaxAdditionalOutputDeviceDelay =
-// AUDIO_PARAMETER_DEVICE_MAX_ADDITIONAL_OUTPUT_DELAY;
+const char * const AudioParameter::keyAdditionalOutputDeviceDelay =
+ AUDIO_PARAMETER_DEVICE_ADDITIONAL_OUTPUT_DELAY;
+const char * const AudioParameter::keyMaxAdditionalOutputDeviceDelay =
+ AUDIO_PARAMETER_DEVICE_MAX_ADDITIONAL_OUTPUT_DELAY;
AudioParameter::AudioParameter(const String8& keyValuePairs)
{
diff --git a/media/libmediahelper/AudioValidator.cpp b/media/libmediahelper/AudioValidator.cpp
new file mode 100644
index 0000000..7eddbe1
--- /dev/null
+++ b/media/libmediahelper/AudioValidator.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/AudioValidator.h>
+#include <cmath>
+
+namespace android {
+
+/** returns true if string is overflow */
+template <size_t size>
+bool checkStringOverflow(const char (&s)[size]) {
+ return strnlen(s, size) >= size;
+}
+
+status_t safetyNetLog(status_t status, std::string_view bugNumber) {
+ if (status != NO_ERROR && !bugNumber.empty()) {
+ android_errorWriteLog(0x534e4554, bugNumber.data()); // SafetyNet logging
+ }
+ return status;
+}
+
+status_t AudioValidator::validateAudioAttributes(
+ const audio_attributes_t& attr, std::string_view bugNumber)
+{
+ status_t status = NO_ERROR;
+ const size_t tagsMaxSize = AUDIO_ATTRIBUTES_TAGS_MAX_SIZE;
+ if (strnlen(attr.tags, tagsMaxSize) >= tagsMaxSize) {
+ status = BAD_VALUE;
+ }
+ return safetyNetLog(status, bugNumber);
+}
+
+status_t AudioValidator::validateEffectDescriptor(
+ const effect_descriptor_t& desc, std::string_view bugNumber)
+{
+ status_t status = NO_ERROR;
+ if (checkStringOverflow(desc.name)
+ | /* always */ checkStringOverflow(desc.implementor)) {
+ status = BAD_VALUE;
+ }
+ return safetyNetLog(status, bugNumber);
+}
+
+status_t AudioValidator::validateAudioPortConfig(
+ const struct audio_port_config& config, std::string_view bugNumber)
+{
+ status_t status = NO_ERROR;
+ if (config.type == AUDIO_PORT_TYPE_DEVICE &&
+ checkStringOverflow(config.ext.device.address)) {
+ status = BAD_VALUE;
+ }
+ return safetyNetLog(status, bugNumber);
+}
+
+namespace {
+
+template <typename T, std::enable_if_t<std::is_same<T, struct audio_port>::value
+ || std::is_same<T, struct audio_port_v7>::value, int> = 0>
+static status_t validateAudioPortInternal(const T& port, std::string_view bugNumber = {}) {
+ status_t status = NO_ERROR;
+ if (checkStringOverflow(port.name)) {
+ status = BAD_VALUE;
+ }
+ if (AudioValidator::validateAudioPortConfig(port.active_config) != NO_ERROR) {
+ status = BAD_VALUE;
+ }
+ if (port.type == AUDIO_PORT_TYPE_DEVICE &&
+ checkStringOverflow(port.ext.device.address)) {
+ status = BAD_VALUE;
+ }
+ return safetyNetLog(status, bugNumber);
+}
+
+} // namespace
+
+status_t AudioValidator::validateAudioPort(
+ const struct audio_port& port, std::string_view bugNumber)
+{
+ return validateAudioPortInternal(port, bugNumber);
+}
+
+status_t AudioValidator::validateAudioPort(
+ const struct audio_port_v7& port, std::string_view bugNumber)
+{
+ return validateAudioPortInternal(port, bugNumber);
+}
+
+/** returns BAD_VALUE if sanitization was required. */
+status_t AudioValidator::validateAudioPatch(
+ const struct audio_patch& patch, std::string_view bugNumber)
+{
+ status_t status = NO_ERROR;
+ if (patch.num_sources > AUDIO_PATCH_PORTS_MAX) {
+ status = BAD_VALUE;
+ }
+ if (patch.num_sinks > AUDIO_PATCH_PORTS_MAX) {
+ status = BAD_VALUE;
+ }
+ for (size_t i = 0; i < patch.num_sources; i++) {
+ if (validateAudioPortConfig(patch.sources[i]) != NO_ERROR) {
+ status = BAD_VALUE;
+ }
+ }
+ for (size_t i = 0; i < patch.num_sinks; i++) {
+ if (validateAudioPortConfig(patch.sinks[i]) != NO_ERROR) {
+ status = BAD_VALUE;
+ }
+ }
+ return safetyNetLog(status, bugNumber);
+}
+
+/* static */
+status_t AudioValidator::validateAudioDescriptionMixLevel(float leveldB)
+{
+ constexpr float MAX_AUDIO_DESCRIPTION_MIX_LEVEL = 48.f;
+ return std::isnan(leveldB) || leveldB > MAX_AUDIO_DESCRIPTION_MIX_LEVEL ? BAD_VALUE : OK;
+}
+
+/* static */
+status_t AudioValidator::validateDualMonoMode(audio_dual_mono_mode_t dualMonoMode)
+{
+ switch (dualMonoMode) {
+ case AUDIO_DUAL_MONO_MODE_OFF:
+ case AUDIO_DUAL_MONO_MODE_LR:
+ case AUDIO_DUAL_MONO_MODE_LL:
+ case AUDIO_DUAL_MONO_MODE_RR:
+ return OK;
+ }
+ return BAD_VALUE;
+}
+
+/* static */
+status_t AudioValidator::validatePlaybackRateFallbackMode(
+ audio_timestretch_fallback_mode_t fallbackMode)
+{
+ switch (fallbackMode) {
+ case AUDIO_TIMESTRETCH_FALLBACK_CUT_REPEAT:
+ // This is coarse sounding timestretching used for internal debugging,
+ // not intended for general use.
+ break; // warning if not listed.
+ case AUDIO_TIMESTRETCH_FALLBACK_DEFAULT:
+ case AUDIO_TIMESTRETCH_FALLBACK_MUTE:
+ case AUDIO_TIMESTRETCH_FALLBACK_FAIL:
+ return OK;
+ }
+ return BAD_VALUE;
+}
+
+/* static */
+status_t AudioValidator::validatePlaybackRateStretchMode(
+ audio_timestretch_stretch_mode_t stretchMode)
+{
+ switch (stretchMode) {
+ case AUDIO_TIMESTRETCH_STRETCH_DEFAULT:
+ case AUDIO_TIMESTRETCH_STRETCH_VOICE:
+ return OK;
+ }
+ return BAD_VALUE;
+}
+
+/* static */
+status_t AudioValidator::validatePlaybackRate(
+ const audio_playback_rate_t& playbackRate)
+{
+ if (playbackRate.mSpeed < 0.f || playbackRate.mPitch < 0.f) return BAD_VALUE;
+ return validatePlaybackRateFallbackMode(playbackRate.mFallbackMode) ?:
+ validatePlaybackRateStretchMode(playbackRate.mStretchMode);
+}
+
+}; // namespace android
diff --git a/media/libmediahelper/include/media/AudioParameter.h b/media/libmediahelper/include/media/AudioParameter.h
index b72d0d5..9a6ca8a 100644
--- a/media/libmediahelper/include/media/AudioParameter.h
+++ b/media/libmediahelper/include/media/AudioParameter.h
@@ -104,8 +104,8 @@
// static const char * const keyDeviceSupportedEncapsulationModes;
// static const char * const keyDeviceSupportedEncapsulationMetadataTypes;
- // static const char * const keyAdditionalOutputDeviceDelay;
- // static const char * const keyMaxAdditionalOutputDeviceDelay;
+ static const char * const keyAdditionalOutputDeviceDelay;
+ static const char * const keyMaxAdditionalOutputDeviceDelay;
String8 toString() const { return toStringImpl(true); }
String8 keysToString() const { return toStringImpl(false); }
diff --git a/media/libmediahelper/include/media/AudioValidator.h b/media/libmediahelper/include/media/AudioValidator.h
new file mode 100644
index 0000000..56c2fa6
--- /dev/null
+++ b/media/libmediahelper/include/media/AudioValidator.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AUDIO_VALIDATOR_H_
+#define ANDROID_AUDIO_VALIDATOR_H_
+
+#include <system/audio.h>
+#include <system/audio_effect.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+
+#include <string_view>
+
+namespace android {
+
+/**
+ * AudioValidator is a class to validate audio data in binder call. NO_ERROR will be returned only
+ * when there is no error with the data.
+ */
+class AudioValidator {
+public:
+ /**
+ * Return NO_ERROR only when there is no error with the given audio attributes.
+ * Otherwise, return BAD_VALUE.
+ */
+ static status_t validateAudioAttributes(
+ const audio_attributes_t& attr, std::string_view bugNumber = {});
+
+ /**
+ * Return NO_ERROR only when there is no error with the given effect descriptor.
+ * Otherwise, return BAD_VALUE.
+ */
+ static status_t validateEffectDescriptor(
+ const effect_descriptor_t& desc, std::string_view bugNumber = {});
+
+ /**
+ * Return NO_ERROR only when there is no error with the given audio port config.
+ * Otherwise, return BAD_VALUE.
+ */
+ static status_t validateAudioPortConfig(
+ const struct audio_port_config& config, std::string_view bugNumber = {});
+
+ /**
+ * Return NO_ERROR only when there is no error with the given audio port.
+ * Otherwise, return BAD_VALUE.
+ */
+ static status_t validateAudioPort(
+ const struct audio_port& port, std::string_view bugNumber = {});
+
+ /**
+ * Return NO_ERROR only when there is no error with the given audio_port_v7.
+ * Otherwise, return BAD_VALUE.
+ */
+ static status_t validateAudioPort(
+ const struct audio_port_v7& port, std::string_view bugNumber = {});
+
+ /**
+ * Return NO_ERROR only when there is no error with the given audio patch.
+ * Otherwise, return BAD_VALUE.
+ */
+ static status_t validateAudioPatch(
+ const struct audio_patch& patch, std::string_view bugNumber = {});
+
+ /**
+ * Return NO_ERROR if leveldB is acceptable, otherwise BAD_VALUE.
+ */
+ static status_t validateAudioDescriptionMixLevel(float leveldB);
+
+ /**
+ * Return NO_ERROR if dualMonoMode is one of the enum values, otherwise BAD_VALUE.
+ */
+ static status_t validateDualMonoMode(audio_dual_mono_mode_t dualMonoMode);
+
+ /**
+ * Return NO_ERROR if fallbackMode is one of the enum values, otherwise BAD_VALUE.
+ */
+ static status_t validatePlaybackRateFallbackMode(
+ audio_timestretch_fallback_mode_t fallbackMode);
+
+ /**
+ * Return NO_ERROR if fallbackMode is one of the enum values, otherwise BAD_VALUE.
+ */
+ static status_t validatePlaybackRateStretchMode(audio_timestretch_stretch_mode_t stretchMode);
+
+ /**
+ * Return NO_ERROR if playbackRate is acceptable - the enums are correct and the
+ * rate and speed non-negative, otherwise BAD_VALUE.
+ *
+ * This is a basic bounds check - the system might have stricter requirements for
+ * playbackRate on a particular stream / device.
+ */
+ static status_t validatePlaybackRate(const audio_playback_rate_t& playbackRate);
+};
+
+}; // namespace android
+
+#endif /*ANDROID_AUDIO_VALIDATOR_H_*/
diff --git a/media/libmediametrics/Android.bp b/media/libmediametrics/Android.bp
index 3843e70..d758391 100644
--- a/media/libmediametrics/Android.bp
+++ b/media/libmediametrics/Android.bp
@@ -16,7 +16,6 @@
name: "libmediametrics",
srcs: [
- "IMediaMetricsService.cpp",
"MediaMetricsItem.cpp",
"MediaMetrics.cpp",
],
@@ -26,6 +25,7 @@
"libcutils",
"liblog",
"libutils",
+ "mediametricsservice-aidl-cpp",
],
export_include_dirs: ["include"],
@@ -67,3 +67,21 @@
"//frameworks/base/media/jni",
],
}
+
+aidl_interface {
+ name: "mediametricsservice-aidl",
+ unstable: true,
+ local_include_dir: "aidl",
+ vendor_available: true,
+ srcs: [
+ "aidl/android/media/IMediaMetricsService.aidl",
+ ],
+ double_loadable: true,
+ backend: {
+ cpp: {
+ apex_available: [
+ "//apex_available:platform",
+ ],
+ },
+ },
+}
diff --git a/media/libmediametrics/MediaMetricsItem.cpp b/media/libmediametrics/MediaMetricsItem.cpp
index 7cdbe5f..a8350ea 100644
--- a/media/libmediametrics/MediaMetricsItem.cpp
+++ b/media/libmediametrics/MediaMetricsItem.cpp
@@ -31,8 +31,9 @@
#include <utils/SortedVector.h>
#include <utils/threads.h>
+#include <android/media/BnMediaMetricsService.h> // for direct Binder access
+#include <android/media/IMediaMetricsService.h>
#include <binder/IServiceManager.h>
-#include <media/IMediaMetricsService.h>
#include <media/MediaMetricsItem.h>
#include <private/android_filesystem_config.h>
@@ -278,17 +279,19 @@
// 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 {
+
+ char *str;
+ size_t size;
+ status_t status = writeToByteString(&str, &size);
+ if (status == NO_ERROR) {
+ status = submitBuffer(str, size);
+ free(str);
+ }
+ if (status != NO_ERROR) {
+ ALOGW("%s: failed to record: %s", __func__, this->toString().c_str());
return false;
}
+ return true;
}
//static
@@ -327,7 +330,7 @@
static sp<MediaMetricsDeathNotifier> sNotifier;
// static
-sp<IMediaMetricsService> BaseItem::sMediaMetricsService;
+sp<media::IMediaMetricsService> BaseItem::sMediaMetricsService;
static std::mutex sServiceMutex;
static int sRemainingBindAttempts = SVC_TRIES;
@@ -339,29 +342,67 @@
}
// 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();
- */
-
+status_t BaseItem::submitBuffer(const char *buffer, size_t size) {
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;
+
+ // Validate size
+ if (size > std::numeric_limits<int32_t>::max()) return BAD_VALUE;
+
+ // Do we have the service available?
+ sp<media::IMediaMetricsService> svc = getService();
+ if (svc == nullptr) return NO_INIT;
+
+ ::android::status_t status = NO_ERROR;
+ if constexpr (/* DISABLES CODE */ (false)) {
+ // THIS PATH IS FOR REFERENCE ONLY.
+ // It is compiled so that any changes to IMediaMetricsService::submitBuffer()
+ // will lead here. If this code is changed, the else branch must
+ // be changed as well.
+ //
+ // Use the AIDL calling interface - this is a bit slower as a byte vector must be
+ // constructed. As the call is one-way, the only a transaction error occurs.
+ status = svc->submitBuffer({buffer, buffer + size}).transactionError();
+ } else {
+ // Use the Binder calling interface - this direct implementation avoids
+ // malloc/copy/free for the vector and reduces the overhead for logging.
+ // We based this off of the AIDL generated file:
+ // out/soong/.intermediates/frameworks/av/media/libmediametrics/mediametricsservice-aidl-unstable-cpp-source/gen/android/media/IMediaMetricsService.cpp
+ // TODO: Create an AIDL C++ back end optimized form of vector writing.
+ ::android::Parcel _aidl_data;
+ ::android::Parcel _aidl_reply; // we don't care about this as it is one-way.
+
+ status = _aidl_data.writeInterfaceToken(svc->getInterfaceDescriptor());
+ if (status != ::android::OK) goto _aidl_error;
+
+ status = _aidl_data.writeInt32(static_cast<int32_t>(size));
+ if (status != ::android::OK) goto _aidl_error;
+
+ status = _aidl_data.write(buffer, static_cast<int32_t>(size));
+ if (status != ::android::OK) goto _aidl_error;
+
+ status = ::android::IInterface::asBinder(svc)->transact(
+ ::android::media::BnMediaMetricsService::TRANSACTION_submitBuffer,
+ _aidl_data, &_aidl_reply, ::android::IBinder::FLAG_ONEWAY);
+
+ // AIDL permits setting a default implementation for additional functionality.
+ // See go/aog/713984. This is not used here.
+ // if (status == ::android::UNKNOWN_TRANSACTION
+ // && ::android::media::IMediaMetricsService::getDefaultImpl()) {
+ // status = ::android::media::IMediaMetricsService::getDefaultImpl()
+ // ->submitBuffer(immutableByteVectorFromBuffer(buffer, size))
+ // .transactionError();
+ // }
}
- return false;
+
+ if (status == NO_ERROR) return NO_ERROR;
+
+ _aidl_error:
+ ALOGW("%s: failed(%d) to record: %zu bytes", __func__, status, size);
+ return status;
}
//static
-sp<IMediaMetricsService> BaseItem::getService() {
+sp<media::IMediaMetricsService> BaseItem::getService() {
static const char *servicename = "media.metrics";
static const bool enabled = isEnabled(); // singleton initialized
@@ -379,7 +420,7 @@
if (sm != nullptr) {
sp<IBinder> binder = sm->getService(String16(servicename));
if (binder != nullptr) {
- sMediaMetricsService = interface_cast<IMediaMetricsService>(binder);
+ sMediaMetricsService = interface_cast<media::IMediaMetricsService>(binder);
sNotifier = new MediaMetricsDeathNotifier();
binder->linkToDeath(sNotifier);
} else {
diff --git a/media/libmediametrics/aidl/android/media/IMediaMetricsService.aidl b/media/libmediametrics/aidl/android/media/IMediaMetricsService.aidl
new file mode 100644
index 0000000..b14962d
--- /dev/null
+++ b/media/libmediametrics/aidl/android/media/IMediaMetricsService.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * MediaMetrics service interface
+ *
+ * {@hide}
+ */
+interface IMediaMetricsService {
+ oneway void submitBuffer(in byte[] buffer);
+}
diff --git a/media/libmediametrics/include/MediaMetricsConstants.h b/media/libmediametrics/include/MediaMetricsConstants.h
index 674df17..a09a673 100644
--- a/media/libmediametrics/include/MediaMetricsConstants.h
+++ b/media/libmediametrics/include/MediaMetricsConstants.h
@@ -123,14 +123,17 @@
#define AMEDIAMETRICS_PROP_FRAMECOUNT "frameCount" // int32
#define AMEDIAMETRICS_PROP_INPUTDEVICES "inputDevices" // string value
+#define AMEDIAMETRICS_PROP_INTERNALTRACKID "internalTrackId" // int32
#define AMEDIAMETRICS_PROP_INTERVALCOUNT "intervalCount" // int32
#define AMEDIAMETRICS_PROP_LATENCYMS "latencyMs" // double value
+#define AMEDIAMETRICS_PROP_LOGSESSIONID "logSessionId" // hex string, "" none
#define AMEDIAMETRICS_PROP_NAME "name" // string value
#define AMEDIAMETRICS_PROP_ORIGINALFLAGS "originalFlags" // int32
#define AMEDIAMETRICS_PROP_OUTPUTDEVICES "outputDevices" // string value
#define AMEDIAMETRICS_PROP_PERFORMANCEMODE "performanceMode" // string value, "none", lowLatency"
#define AMEDIAMETRICS_PROP_PLAYBACK_PITCH "playback.pitch" // double value (AudioTrack)
#define AMEDIAMETRICS_PROP_PLAYBACK_SPEED "playback.speed" // double value (AudioTrack)
+#define AMEDIAMETRICS_PROP_PLAYERIID "playerIId" // int32 (-1 invalid/unset IID)
#define AMEDIAMETRICS_PROP_ROUTEDDEVICEID "routedDeviceId" // int32
#define AMEDIAMETRICS_PROP_SAMPLERATE "sampleRate" // int32
#define AMEDIAMETRICS_PROP_SELECTEDDEVICEID "selectedDeviceId" // int32
@@ -157,6 +160,14 @@
#define AMEDIAMETRICS_PROP_VOLUME_LEFT "volume.left" // double (AudioTrack)
#define AMEDIAMETRICS_PROP_VOLUME_RIGHT "volume.right" // double (AudioTrack)
#define AMEDIAMETRICS_PROP_WHERE "where" // string value
+// EncodingClient is the encoding format requested by the client
+#define AMEDIAMETRICS_PROP_ENCODINGCLIENT "encodingClient" // string
+// PerformanceModeActual is the actual selected performance mode, could be "none', "lowLatency" or
+// "powerSaving"
+#define AMEDIAMETRICS_PROP_PERFORMANCEMODEACTUAL "performanceModeActual" // string
+#define AMEDIAMETRICS_PROP_FRAMESTRANSFERRED "framesTransferred" // int64_t, transferred frames
+// string value, "exclusive", "shared". the actual selected sharing mode by the server
+#define AMEDIAMETRICS_PROP_SHARINGMODEACTUAL "sharingModeActual"
// Timing values: millisecond values are suffixed with MS and the type is double
// nanosecond values are suffixed with NS and the type is int64.
@@ -171,6 +182,7 @@
#define AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR "ctor"
#define AMEDIAMETRICS_PROP_EVENT_VALUE_DISCONNECT "disconnect"
#define AMEDIAMETRICS_PROP_EVENT_VALUE_DTOR "dtor"
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAAUDIOSTREAM "endAAudioStream" // AAudioStream
#define AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP "endAudioIntervalGroup"
#define AMEDIAMETRICS_PROP_EVENT_VALUE_FLUSH "flush" // AudioTrack
#define AMEDIAMETRICS_PROP_EVENT_VALUE_INVALIDATE "invalidate" // server track, record
@@ -181,7 +193,9 @@
#define AMEDIAMETRICS_PROP_EVENT_VALUE_RESTORE "restore"
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETMODE "setMode" // AudioFlinger
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETBUFFERSIZE "setBufferSize" // AudioTrack
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETLOGSESSIONID "setLogSessionId" // AudioTrack, Record
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETPLAYBACKPARAM "setPlaybackParam" // AudioTrack
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETPLAYERIID "setPlayerIId" // AudioTrack
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETSTARTTHRESHOLD "setStartThreshold" // AudioTrack
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOICEVOLUME "setVoiceVolume" // AudioFlinger
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOLUME "setVolume" // AudioTrack
diff --git a/media/libmediametrics/include/media/IMediaMetricsService.h b/media/libmediametrics/include/media/IMediaMetricsService.h
deleted file mode 100644
index d6871ec..0000000
--- a/media/libmediametrics/include/media/IMediaMetricsService.h
+++ /dev/null
@@ -1,73 +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/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/media/MediaMetricsItem.h b/media/libmediametrics/include/media/MediaMetricsItem.h
index 303343f..428992c 100644
--- a/media/libmediametrics/include/media/MediaMetricsItem.h
+++ b/media/libmediametrics/include/media/MediaMetricsItem.h
@@ -32,7 +32,8 @@
namespace android {
-class IMediaMetricsService;
+namespace media { class IMediaMetricsService; }
+
class Parcel;
/*
@@ -239,7 +240,10 @@
public:
// are we collecting metrics data
static bool isEnabled();
- static sp<IMediaMetricsService> getService();
+ // returns the MediaMetrics service if active.
+ static sp<media::IMediaMetricsService> getService();
+ // submits a raw buffer directly to the MediaMetrics service - this is highly optimized.
+ static status_t submitBuffer(const char *buffer, size_t len);
protected:
static constexpr const char * const EnabledProperty = "media.metrics.enabled";
@@ -247,10 +251,9 @@
static const int EnabledProperty_default = 1;
// let's reuse a binder connection
- static sp<IMediaMetricsService> sMediaMetricsService;
+ static sp<media::IMediaMetricsService> sMediaMetricsService;
static void dropInstance();
- static bool submitBuffer(const char *buffer, size_t len);
template <typename T>
struct is_item_type {
@@ -573,7 +576,7 @@
bool record() {
return updateHeader()
- && BaseItem::submitBuffer(getBuffer(), getLength());
+ && BaseItem::submitBuffer(getBuffer(), getLength()) == OK;
}
bool isValid () const {
diff --git a/media/libmediaplayerservice/Android.bp b/media/libmediaplayerservice/Android.bp
index c5d3c1d..287317d 100644
--- a/media/libmediaplayerservice/Android.bp
+++ b/media/libmediaplayerservice/Android.bp
@@ -34,6 +34,9 @@
shared_libs: [
"android.hardware.media.c2@1.0",
"android.hardware.media.omx@1.0",
+ "av-types-aidl-cpp",
+ "media_permission-aidl-cpp",
+ "libaudioclient_aidl_conversion",
"libbase",
"libactivitymanager_aidl",
"libandroid_net",
@@ -73,9 +76,13 @@
"libstagefright_nuplayer",
"libstagefright_rtsp",
"libstagefright_timedtext",
+ "media_permission-aidl-cpp",
],
- export_shared_lib_headers: ["libmedia"],
+ export_shared_lib_headers: [
+ "libmedia",
+ "media_permission-aidl-cpp",
+ ],
include_dirs: [
"frameworks/av/media/libstagefright/rtsp",
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 7cda2fb..dc4aea5 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -49,6 +49,7 @@
#include <codec2/hidl/client.h>
#include <datasource/HTTPBase.h>
+#include <media/AidlConversion.h>
#include <media/IMediaHTTPService.h>
#include <media/IRemoteDisplay.h>
#include <media/IRemoteDisplayClient.h>
@@ -94,6 +95,7 @@
using android::NOT_ENOUGH_DATA;
using android::Parcel;
using android::media::VolumeShaper;
+using android::media::permission::Identity;
// Max number of entries in the filter.
const int kMaxFilterSize = 64; // I pulled that out of thin air.
@@ -453,14 +455,21 @@
ALOGV("MediaPlayerService destroyed");
}
-sp<IMediaRecorder> MediaPlayerService::createMediaRecorder(const String16 &opPackageName)
+sp<IMediaRecorder> MediaPlayerService::createMediaRecorder(const Identity& identity)
{
- pid_t pid = IPCThreadState::self()->getCallingPid();
- sp<MediaRecorderClient> recorder = new MediaRecorderClient(this, pid, opPackageName);
+ // TODO b/182392769: use identity util
+ Identity verifiedIdentity = identity;
+ verifiedIdentity.uid = VALUE_OR_FATAL(
+ legacy2aidl_uid_t_int32_t(IPCThreadState::self()->getCallingUid()));
+ verifiedIdentity.pid = VALUE_OR_FATAL(
+ legacy2aidl_pid_t_int32_t(IPCThreadState::self()->getCallingPid()));
+ sp<MediaRecorderClient> recorder =
+ new MediaRecorderClient(this, verifiedIdentity);
wp<MediaRecorderClient> w = recorder;
Mutex::Autolock lock(mLock);
mMediaRecorderClients.add(w);
- ALOGV("Create new media recorder client from pid %d", pid);
+ ALOGV("Create new media recorder client from pid %s",
+ verifiedIdentity.toString().c_str());
return recorder;
}
@@ -480,17 +489,21 @@
}
sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client,
- audio_session_t audioSessionId, std::string opPackageName)
+ audio_session_t audioSessionId, const Identity& identity)
{
- pid_t pid = IPCThreadState::self()->getCallingPid();
int32_t connId = android_atomic_inc(&mNextConnId);
+ // TODO b/182392769: use identity util
+ Identity verifiedIdentity = identity;
+ verifiedIdentity.pid = VALUE_OR_FATAL(
+ legacy2aidl_pid_t_int32_t(IPCThreadState::self()->getCallingPid()));
+ verifiedIdentity.uid = VALUE_OR_FATAL(
+ legacy2aidl_uid_t_int32_t(IPCThreadState::self()->getCallingUid()));
sp<Client> c = new Client(
- this, pid, connId, client, audioSessionId,
- IPCThreadState::self()->getCallingUid(), opPackageName);
+ this, verifiedIdentity, connId, client, audioSessionId);
- ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, pid,
- IPCThreadState::self()->getCallingUid());
+ ALOGV("Create new client(%d) from %s, ", connId,
+ verifiedIdentity.toString().c_str());
wp<Client> w = c;
{
@@ -543,8 +556,8 @@
char buffer[SIZE];
String8 result;
result.append(" Client\n");
- snprintf(buffer, 255, " pid(%d), connId(%d), status(%d), looping(%s)\n",
- mPid, mConnId, mStatus, mLoop?"true": "false");
+ snprintf(buffer, 255, " Identity(%s), connId(%d), status(%d), looping(%s)\n",
+ mIdentity.toString().c_str(), mConnId, mStatus, mLoop?"true": "false");
result.append(buffer);
sp<MediaPlayerBase> p;
@@ -608,7 +621,7 @@
for (int i = 0, n = mMediaRecorderClients.size(); i < n; ++i) {
sp<MediaRecorderClient> c = mMediaRecorderClients[i].promote();
if (c != 0) {
- snprintf(buffer, 255, " MediaRecorderClient pid(%d)\n", c->mPid);
+ snprintf(buffer, 255, " MediaRecorderClient pid(%d)\n", c->mIdentity.pid);
result.append(buffer);
write(fd, result.string(), result.size());
result = "\n";
@@ -731,20 +744,18 @@
}
MediaPlayerService::Client::Client(
- const sp<MediaPlayerService>& service, pid_t pid,
+ const sp<MediaPlayerService>& service, const Identity& identity,
int32_t connId, const sp<IMediaPlayerClient>& client,
- audio_session_t audioSessionId, uid_t uid, const std::string& opPackageName)
- : mOpPackageName(opPackageName)
+ audio_session_t audioSessionId)
+ : mIdentity(identity)
{
ALOGV("Client(%d) constructor", connId);
- mPid = pid;
mConnId = connId;
mService = service;
mClient = client;
mLoop = false;
mStatus = NO_INIT;
mAudioSessionId = audioSessionId;
- mUid = uid;
mRetransmitEndpointValid = false;
mAudioAttributes = NULL;
mListener = new Listener(this);
@@ -757,7 +768,7 @@
MediaPlayerService::Client::~Client()
{
- ALOGV("Client(%d) destructor pid = %d", mConnId, mPid);
+ ALOGV("Client(%d) destructor identity = %s", mConnId, mIdentity.toString().c_str());
mAudioOutput.clear();
wp<Client> client(this);
disconnect();
@@ -770,7 +781,7 @@
void MediaPlayerService::Client::disconnect()
{
- ALOGV("disconnect(%d) from pid %d", mConnId, mPid);
+ ALOGV("disconnect(%d) from identity %s", mConnId, mIdentity.toString().c_str());
// grab local reference and clear main reference to prevent future
// access to object
sp<MediaPlayerBase> p;
@@ -810,11 +821,12 @@
p.clear();
}
if (p == NULL) {
- p = MediaPlayerFactory::createPlayer(playerType, mListener, mPid);
+ p = MediaPlayerFactory::createPlayer(playerType, mListener,
+ VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mIdentity.pid)));
}
if (p != NULL) {
- p->setUID(mUid);
+ p->setUID(VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(mIdentity.uid)));
}
return p;
@@ -922,8 +934,8 @@
mAudioDeviceUpdatedListener = new AudioDeviceUpdatedNotifier(p);
if (!p->hardwareOutput()) {
- mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(),
- mPid, mAudioAttributes, mAudioDeviceUpdatedListener, mOpPackageName);
+ mAudioOutput = new AudioOutput(mAudioSessionId, mIdentity,
+ mAudioAttributes, mAudioDeviceUpdatedListener);
static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
}
@@ -1772,9 +1784,8 @@
#undef LOG_TAG
#define LOG_TAG "AudioSink"
-MediaPlayerService::AudioOutput::AudioOutput(audio_session_t sessionId, uid_t uid, int pid,
- const audio_attributes_t* attr, const sp<AudioSystem::AudioDeviceCallback>& deviceCallback,
- const std::string& opPackageName)
+MediaPlayerService::AudioOutput::AudioOutput(audio_session_t sessionId, const Identity& identity,
+ const audio_attributes_t* attr, const sp<AudioSystem::AudioDeviceCallback>& deviceCallback)
: mCallback(NULL),
mCallbackCookie(NULL),
mCallbackData(NULL),
@@ -1786,8 +1797,7 @@
mMsecsPerFrame(0),
mFrameSize(0),
mSessionId(sessionId),
- mUid(uid),
- mPid(pid),
+ mIdentity(identity),
mSendLevel(0.0),
mAuxEffectId(0),
mFlags(AUDIO_OUTPUT_FLAG_NONE),
@@ -1795,8 +1805,7 @@
mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE),
mDeviceCallbackEnabled(false),
- mDeviceCallback(deviceCallback),
- mOpPackageName(opPackageName)
+ mDeviceCallback(deviceCallback)
{
ALOGV("AudioOutput(%d)", sessionId);
if (attr != NULL) {
@@ -2184,13 +2193,11 @@
mSessionId,
AudioTrack::TRANSFER_CALLBACK,
offloadInfo,
- mUid,
- mPid,
+ mIdentity,
mAttributes,
doNotReconnect,
1.0f, // default value for maxRequiredSpeed
- mSelectedDeviceId,
- mOpPackageName);
+ mSelectedDeviceId);
} else {
// TODO: Due to buffer memory concerns, we use a max target playback speed
// based on mPlaybackRate at the time of open (instead of kMaxRequiredSpeed),
@@ -2213,13 +2220,11 @@
mSessionId,
AudioTrack::TRANSFER_DEFAULT,
NULL, // offload info
- mUid,
- mPid,
+ mIdentity,
mAttributes,
doNotReconnect,
targetSpeed,
- mSelectedDeviceId,
- mOpPackageName);
+ mSelectedDeviceId);
}
// Set caller name so it can be logged in destructor.
// MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_MEDIA
@@ -2245,6 +2250,12 @@
mRecycledTrack->frameCount(), t->frameCount());
reuse = false;
}
+ // If recycled and new tracks are not on the same output,
+ // don't reuse the recycled one.
+ if (mRecycledTrack->getOutput() != t->getOutput()) {
+ ALOGV("output has changed, don't reuse track");
+ reuse = false;
+ }
}
if (reuse) {
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index b2f1b9b..35a65d3 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -27,12 +27,13 @@
#include <utils/String8.h>
#include <utils/Vector.h>
+#include <media/AidlConversion.h>
#include <media/AudioResamplerPublic.h>
#include <media/AudioSystem.h>
#include <media/MediaPlayerInterface.h>
#include <media/Metadata.h>
#include <media/stagefright/foundation/ABase.h>
-
+#include <android/media/permission/Identity.h>
#include <system/audio.h>
@@ -79,11 +80,9 @@
public:
AudioOutput(
audio_session_t sessionId,
- uid_t uid,
- int pid,
+ const media::permission::Identity& identity,
const audio_attributes_t * attr,
- const sp<AudioSystem::AudioDeviceCallback>& deviceCallback,
- const std::string& opPackageName);
+ const sp<AudioSystem::AudioDeviceCallback>& deviceCallback);
virtual ~AudioOutput();
virtual bool ready() const { return mTrack != 0; }
@@ -100,6 +99,7 @@
virtual audio_session_t getSessionId() const;
virtual uint32_t getSampleRate() const;
virtual int64_t getBufferDurationInUs() const;
+ virtual audio_output_flags_t getFlags() const { return mFlags; }
virtual status_t open(
uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
@@ -169,8 +169,7 @@
float mMsecsPerFrame;
size_t mFrameSize;
audio_session_t mSessionId;
- uid_t mUid;
- int mPid;
+ media::permission::Identity mIdentity;
float mSendLevel;
int mAuxEffectId;
audio_output_flags_t mFlags;
@@ -180,7 +179,6 @@
bool mDeviceCallbackEnabled;
wp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
mutable Mutex mLock;
- const std::string mOpPackageName;
// static variables below not protected by mutex
static bool mIsOnEmulator;
@@ -233,13 +231,13 @@
static void instantiate();
// IMediaPlayerService interface
- virtual sp<IMediaRecorder> createMediaRecorder(const String16 &opPackageName);
+ virtual sp<IMediaRecorder> createMediaRecorder(const media::permission::Identity &identity);
void removeMediaRecorderClient(const wp<MediaRecorderClient>& client);
virtual sp<IMediaMetadataRetriever> createMetadataRetriever();
virtual sp<IMediaPlayer> create(const sp<IMediaPlayerClient>& client,
audio_session_t audioSessionId,
- const std::string opPackageName);
+ const media::permission::Identity& identity);
virtual sp<IMediaCodecList> getCodecList() const;
@@ -381,7 +379,9 @@
void notify(int msg, int ext1, int ext2, const Parcel *obj);
- pid_t pid() const { return mPid; }
+ pid_t pid() const {
+ return VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mIdentity.pid));
+ }
virtual status_t dump(int fd, const Vector<String16>& args);
audio_session_t getAudioSessionId() { return mAudioSessionId; }
@@ -411,12 +411,10 @@
friend class MediaPlayerService;
Client( const sp<MediaPlayerService>& service,
- pid_t pid,
+ const media::permission::Identity& identity,
int32_t connId,
const sp<IMediaPlayerClient>& client,
- audio_session_t audioSessionId,
- uid_t uid,
- const std::string& opPackageName);
+ audio_session_t audioSessionId);
Client();
virtual ~Client();
@@ -460,20 +458,18 @@
sp<MediaPlayerService> mService;
sp<IMediaPlayerClient> mClient;
sp<AudioOutput> mAudioOutput;
- pid_t mPid;
+ const media::permission::Identity mIdentity;
status_t mStatus;
bool mLoop;
int32_t mConnId;
audio_session_t mAudioSessionId;
audio_attributes_t * mAudioAttributes;
- uid_t mUid;
sp<ANativeWindow> mConnectedWindow;
sp<IBinder> mConnectedWindowBinder;
struct sockaddr_in mRetransmitEndpoint;
bool mRetransmitEndpointValid;
sp<Client> mNextClient;
sp<MediaPlayerBase::Listener> mListener;
- const std::string mOpPackageName;
// Metadata filters.
media::Metadata::Filter mMetadataAllow; // protected by mLock
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index 9f16a22..e2c8f8f 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -124,11 +124,10 @@
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)) {
+ if ((as == AUDIO_SOURCE_FM_TUNER
+ && !(captureAudioOutputAllowed(mIdentity) || captureTunerAudioInputAllowed(mIdentity)))
+ || !recordingAllowed(mIdentity)) {
return PERMISSION_DENIED;
}
Mutex::Autolock lock(mLock);
@@ -377,12 +376,13 @@
return NO_ERROR;
}
-MediaRecorderClient::MediaRecorderClient(const sp<MediaPlayerService>& service, pid_t pid,
- const String16& opPackageName)
+MediaRecorderClient::MediaRecorderClient(const sp<MediaPlayerService>& service,
+ const Identity& identity)
{
ALOGV("Client constructor");
- mPid = pid;
- mRecorder = new StagefrightRecorder(opPackageName);
+ // identity already validated in createMediaRecorder
+ mIdentity = identity;
+ mRecorder = new StagefrightRecorder(identity);
mMediaPlayerService = service;
}
diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h
index e041855..24c6ee1 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.h
+++ b/media/libmediaplayerservice/MediaRecorderClient.h
@@ -22,6 +22,7 @@
#include <media/AudioSystem.h>
#include <media/IMediaRecorder.h>
+#include <android/media/permission/Identity.h>
#include <vector>
@@ -93,14 +94,13 @@
MediaRecorderClient(
const sp<MediaPlayerService>& service,
- pid_t pid,
- const String16& opPackageName);
+ const media::permission::Identity& identity);
virtual ~MediaRecorderClient();
std::vector<DeathNotifier> mDeathNotifiers;
sp<AudioDeviceUpdatedNotifier> mAudioDeviceUpdatedNotifier;
- pid_t mPid;
+ media::permission::Identity mIdentity;
mutable Mutex mLock;
MediaRecorderBase *mRecorder;
sp<MediaPlayerService> mMediaPlayerService;
diff --git a/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp b/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp
index 41b6f72..6dc3e3f 100644
--- a/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp
+++ b/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp
@@ -174,9 +174,7 @@
ALOGV("getting track %zu of %zu, meta=%s", i, n, meta->toString().c_str());
const char *mime;
- CHECK(meta->findCString(kKeyMIMEType, &mime));
-
- if (!strncasecmp(mime, "image/", 6)) {
+ if (meta->findCString(kKeyMIMEType, &mime) && !strncasecmp(mime, "image/", 6)) {
int32_t isPrimary;
if ((index < 0 && meta->findInt32(
kKeyTrackIsDefault, &isPrimary) && isPrimary)
@@ -208,12 +206,19 @@
}
const char *mime;
- CHECK(trackMeta->findCString(kKeyMIMEType, &mime));
+ if (!trackMeta->findCString(kKeyMIMEType, &mime)) {
+ ALOGE("image track has no mime type");
+ return NULL;
+ }
ALOGV("extracting from %s track", mime);
if (!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC)) {
mime = MEDIA_MIMETYPE_VIDEO_HEVC;
trackMeta = new MetaData(*trackMeta);
trackMeta->setCString(kKeyMIMEType, mime);
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_AVIF)) {
+ mime = MEDIA_MIMETYPE_VIDEO_AV1;
+ trackMeta = new MetaData(*trackMeta);
+ trackMeta->setCString(kKeyMIMEType, mime);
}
bool preferhw = property_get_bool(
@@ -299,9 +304,7 @@
}
const char *mime;
- CHECK(meta->findCString(kKeyMIMEType, &mime));
-
- if (!strncasecmp(mime, "video/", 6)) {
+ if (meta->findCString(kKeyMIMEType, &mime) && !strncasecmp(mime, "video/", 6)) {
break;
}
}
@@ -337,7 +340,10 @@
}
const char *mime;
- CHECK(trackMeta->findCString(kKeyMIMEType, &mime));
+ if (!trackMeta->findCString(kKeyMIMEType, &mime)) {
+ ALOGE("video track has no mime information.");
+ return NULL;
+ }
bool preferhw = property_get_bool(
"media.stagefright.thumbnail.prefer_hw_codecs", false);
@@ -523,6 +529,15 @@
mMetaData.add(METADATA_KEY_EXIF_LENGTH, String8(tmp));
}
+ int64_t xmpOffset, xmpSize;
+ if (meta->findInt64(kKeyXmpOffset, &xmpOffset)
+ && meta->findInt64(kKeyXmpSize, &xmpSize)) {
+ sprintf(tmp, "%lld", (long long)xmpOffset);
+ mMetaData.add(METADATA_KEY_XMP_OFFSET, String8(tmp));
+ sprintf(tmp, "%lld", (long long)xmpSize);
+ mMetaData.add(METADATA_KEY_XMP_LENGTH, String8(tmp));
+ }
+
bool hasAudio = false;
bool hasVideo = false;
int32_t videoWidth = -1;
@@ -531,14 +546,14 @@
int32_t audioBitrate = -1;
int32_t rotationAngle = -1;
int32_t imageCount = 0;
- int32_t imagePrimary = 0;
+ int32_t imagePrimary = -1;
int32_t imageWidth = -1;
int32_t imageHeight = -1;
int32_t imageRotation = -1;
// The overall duration is the duration of the longest track.
int64_t maxDurationUs = 0;
- String8 timedTextLang;
+ String8 timedTextLang, videoMime;
for (size_t i = 0; i < numTracks; ++i) {
sp<MetaData> trackMeta = mExtractor->getTrackMetaData(i);
if (!trackMeta) {
@@ -574,28 +589,33 @@
mMetaData.add(METADATA_KEY_SAMPLERATE, String8(tmp));
}
} else if (!hasVideo && !strncasecmp("video/", mime, 6)) {
- hasVideo = true;
-
- CHECK(trackMeta->findInt32(kKeyWidth, &videoWidth));
- CHECK(trackMeta->findInt32(kKeyHeight, &videoHeight));
if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) {
rotationAngle = 0;
}
if (!trackMeta->findInt32(kKeyFrameCount, &videoFrameCount)) {
videoFrameCount = 0;
}
-
- parseColorAspects(trackMeta);
+ if (trackMeta->findInt32(kKeyWidth, &videoWidth)
+ && trackMeta->findInt32(kKeyHeight, &videoHeight)) {
+ hasVideo = true;
+ videoMime = String8(mime);
+ parseColorAspects(trackMeta);
+ } else {
+ ALOGE("video track ignored for missing dimensions");
+ }
} else if (!strncasecmp("image/", mime, 6)) {
int32_t isPrimary;
if (trackMeta->findInt32(
kKeyTrackIsDefault, &isPrimary) && isPrimary) {
- imagePrimary = imageCount;
- CHECK(trackMeta->findInt32(kKeyWidth, &imageWidth));
- CHECK(trackMeta->findInt32(kKeyHeight, &imageHeight));
if (!trackMeta->findInt32(kKeyRotation, &imageRotation)) {
imageRotation = 0;
}
+ if (trackMeta->findInt32(kKeyWidth, &imageWidth)
+ && trackMeta->findInt32(kKeyHeight, &imageHeight)) {
+ imagePrimary = imageCount;
+ } else {
+ ALOGE("primary image track ignored for missing dimensions");
+ }
}
imageCount++;
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
@@ -618,7 +638,8 @@
}
// The duration value is a string representing the duration in ms.
- sprintf(tmp, "%" PRId64, (maxDurationUs + 500) / 1000);
+ sprintf(tmp, "%" PRId64,
+ (maxDurationUs > (INT64_MAX - 500) ? INT64_MAX : (maxDurationUs + 500)) / 1000);
mMetaData.add(METADATA_KEY_DURATION, String8(tmp));
if (hasAudio) {
@@ -628,22 +649,27 @@
if (hasVideo) {
mMetaData.add(METADATA_KEY_HAS_VIDEO, String8("yes"));
+ CHECK(videoWidth >= 0);
sprintf(tmp, "%d", videoWidth);
mMetaData.add(METADATA_KEY_VIDEO_WIDTH, String8(tmp));
+ CHECK(videoHeight >= 0);
sprintf(tmp, "%d", videoHeight);
mMetaData.add(METADATA_KEY_VIDEO_HEIGHT, String8(tmp));
sprintf(tmp, "%d", rotationAngle);
mMetaData.add(METADATA_KEY_VIDEO_ROTATION, String8(tmp));
+ mMetaData.add(METADATA_KEY_VIDEO_CODEC_MIME_TYPE, videoMime);
+
if (videoFrameCount > 0) {
sprintf(tmp, "%d", videoFrameCount);
mMetaData.add(METADATA_KEY_VIDEO_FRAME_COUNT, String8(tmp));
}
}
- if (imageCount > 0) {
+ // only if we have a primary image
+ if (imageCount > 0 && imagePrimary >= 0) {
mMetaData.add(METADATA_KEY_HAS_IMAGE, String8("yes"));
sprintf(tmp, "%d", imageCount);
@@ -652,9 +678,11 @@
sprintf(tmp, "%d", imagePrimary);
mMetaData.add(METADATA_KEY_IMAGE_PRIMARY, String8(tmp));
+ CHECK(imageWidth >= 0);
sprintf(tmp, "%d", imageWidth);
mMetaData.add(METADATA_KEY_IMAGE_WIDTH, String8(tmp));
+ CHECK(imageHeight >= 0);
sprintf(tmp, "%d", imageHeight);
mMetaData.add(METADATA_KEY_IMAGE_HEIGHT, String8(tmp));
@@ -682,10 +710,9 @@
!strcasecmp(fileMIME, "video/x-matroska")) {
sp<MetaData> trackMeta = mExtractor->getTrackMetaData(0);
const char *trackMIME;
- if (trackMeta != nullptr) {
- CHECK(trackMeta->findCString(kKeyMIMEType, &trackMIME));
- }
- if (!strncasecmp("audio/", trackMIME, 6)) {
+ if (trackMeta != nullptr
+ && trackMeta->findCString(kKeyMIMEType, &trackMIME)
+ && !strncasecmp("audio/", trackMIME, 6)) {
// The matroska file only contains a single audio track,
// rewrite its mime type.
mMetaData.add(
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index ecbdf61..ce642f3 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -33,6 +33,7 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
+#include <media/AidlConversion.h>
#include <media/IMediaPlayerService.h>
#include <media/MediaMetricsItem.h>
#include <media/stagefright/foundation/ABuffer.h>
@@ -78,6 +79,7 @@
// NB: these are matched with public Java API constants defined
// in frameworks/base/media/java/android/media/MediaRecorder.java
// These must be kept synchronized with the constants there.
+static const char *kRecorderLogSessionId = "android.media.mediarecorder.log-session-id";
static const char *kRecorderAudioBitrate = "android.media.mediarecorder.audio-bitrate";
static const char *kRecorderAudioChannels = "android.media.mediarecorder.audio-channels";
static const char *kRecorderAudioSampleRate = "android.media.mediarecorder.audio-samplerate";
@@ -114,8 +116,8 @@
}
-StagefrightRecorder::StagefrightRecorder(const String16 &opPackageName)
- : MediaRecorderBase(opPackageName),
+StagefrightRecorder::StagefrightRecorder(const Identity& clientIdentity)
+ : MediaRecorderBase(clientIdentity),
mWriter(NULL),
mOutputFd(-1),
mAudioSource((audio_source_t)AUDIO_SOURCE_CNT), // initialize with invalid value
@@ -157,7 +159,9 @@
// we run as part of the media player service; what we really want to
// know is the app which requested the recording.
- mMetricsItem->setUid(mClientUid);
+ mMetricsItem->setUid(VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(mClient.uid)));
+
+ mMetricsItem->setCString(kRecorderLogSessionId, mLogSessionId.c_str());
// populate the values from the raw fields.
@@ -913,6 +917,14 @@
return ret;
}
+status_t StagefrightRecorder::setLogSessionId(const String8 &log_session_id) {
+ ALOGV("setLogSessionId: %s", log_session_id.string());
+
+ // TODO: validity check that log_session_id is a 32-byte hex digit.
+ mLogSessionId.setTo(log_session_id.string());
+ return OK;
+}
+
status_t StagefrightRecorder::setParameter(
const String8 &key, const String8 &value) {
ALOGV("setParameter: key (%s) => value (%s)", key.string(), value.string());
@@ -1081,6 +1093,8 @@
if (safe_strtoi64(value.string(), &networkHandle)) {
return setSocketNetwork(networkHandle);
}
+ } else if (key == "log-session-id") {
+ return setLogSessionId(value);
} else {
ALOGE("setParameter: failed to find key %s", key.string());
}
@@ -1129,7 +1143,8 @@
}
status_t StagefrightRecorder::setClientName(const String16& clientName) {
- mClientName = clientName;
+
+ mClient.packageName = VALUE_OR_RETURN_STATUS(legacy2aidl_String16_string(clientName));
return OK;
}
@@ -1141,10 +1156,6 @@
return INVALID_OPERATION;
}
- // Get UID and PID here for permission checking
- mClientUid = IPCThreadState::self()->getCallingUid();
- mClientPid = IPCThreadState::self()->getCallingPid();
-
status_t status = OK;
switch (mOutputFormat) {
@@ -1344,12 +1355,10 @@
sp<AudioSource> audioSource =
new AudioSource(
&attr,
- mOpPackageName,
+ mClient,
sourceSampleRate,
mAudioChannels,
mSampleRate,
- mClientUid,
- mClientPid,
mSelectedDeviceId,
mSelectedMicDirection,
mSelectedMicFieldDimension);
@@ -1871,6 +1880,10 @@
Size videoSize;
videoSize.width = mVideoWidth;
videoSize.height = mVideoHeight;
+ uid_t uid = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_uid_t(mClient.uid));
+ pid_t pid = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_pid_t(mClient.pid));
+ String16 clientName = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_string_view_String16(mClient.packageName.value_or("")));
if (mCaptureFpsEnable) {
if (!(mCaptureFps > 0.)) {
ALOGE("Invalid mCaptureFps value: %lf", mCaptureFps);
@@ -1878,13 +1891,13 @@
}
mCameraSourceTimeLapse = CameraSourceTimeLapse::CreateFromCamera(
- mCamera, mCameraProxy, mCameraId, mClientName, mClientUid, mClientPid,
+ mCamera, mCameraProxy, mCameraId, clientName, uid, pid,
videoSize, mFrameRate, mPreviewSurface,
std::llround(1e6 / mCaptureFps));
*cameraSource = mCameraSourceTimeLapse;
} else {
*cameraSource = CameraSource::CreateFromCamera(
- mCamera, mCameraProxy, mCameraId, mClientName, mClientUid, mClientPid,
+ mCamera, mCameraProxy, mCameraId, clientName, uid, pid,
videoSize, mFrameRate,
mPreviewSurface);
}
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 4bba869..59a080e 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -26,6 +26,9 @@
#include <system/audio.h>
#include <media/hardware/MetadataBufferType.h>
+#include <android/media/permission/Identity.h>
+
+using namespace android::media::permission;
namespace android {
@@ -42,9 +45,10 @@
struct ALooper;
struct StagefrightRecorder : public MediaRecorderBase {
- explicit StagefrightRecorder(const String16 &opPackageName);
+ explicit StagefrightRecorder(const Identity& clientIdentity);
virtual ~StagefrightRecorder();
virtual status_t init();
+ virtual status_t setLogSessionId(const String8 &id);
virtual status_t setAudioSource(audio_source_t as);
status_t setPrivacySensitive(bool privacySensitive) override;
status_t isPrivacySensitive(bool *privacySensitive) const override;
@@ -98,9 +102,6 @@
sp<IGraphicBufferProducer> mPreviewSurface;
sp<PersistentSurface> mPersistentSurface;
sp<IMediaRecorderClient> mListener;
- String16 mClientName;
- uid_t mClientUid;
- pid_t mClientPid;
sp<MediaWriter> mWriter;
int mOutputFd;
sp<AudioSource> mAudioSourceNode;
@@ -110,6 +111,7 @@
void flushAndResetMetrics(bool reinitialize);
void updateMetrics();
+ AString mLogSessionId;
audio_source_t mAudioSource;
privacy_sensitive_t mPrivacySensitive;
video_source mVideoSource;
diff --git a/media/libmediaplayerservice/include/MediaPlayerInterface.h b/media/libmediaplayerservice/include/MediaPlayerInterface.h
index 8d94698..1cbd8a0 100644
--- a/media/libmediaplayerservice/include/MediaPlayerInterface.h
+++ b/media/libmediaplayerservice/include/MediaPlayerInterface.h
@@ -108,6 +108,7 @@
virtual audio_stream_type_t getAudioStreamType() const = 0;
virtual uint32_t getSampleRate() const = 0;
virtual int64_t getBufferDurationInUs() const = 0;
+ virtual audio_output_flags_t getFlags() const = 0;
// If no callback is specified, use the "write" API below to submit
// audio data.
diff --git a/media/libmediaplayerservice/nuplayer/AWakeLock.cpp b/media/libmediaplayerservice/nuplayer/AWakeLock.cpp
index 684ba2e..af9cf45 100644
--- a/media/libmediaplayerservice/nuplayer/AWakeLock.cpp
+++ b/media/libmediaplayerservice/nuplayer/AWakeLock.cpp
@@ -52,18 +52,19 @@
if (binder == NULL) {
ALOGW("could not get the power manager service");
} else {
- mPowerManager = interface_cast<IPowerManager>(binder);
+ mPowerManager = interface_cast<os::IPowerManager>(binder);
binder->linkToDeath(mDeathRecipient);
}
}
if (mPowerManager != NULL) {
sp<IBinder> binder = new BBinder();
int64_t token = IPCThreadState::self()->clearCallingIdentity();
- status_t status = mPowerManager->acquireWakeLock(
- POWERMANAGER_PARTIAL_WAKE_LOCK,
- binder, String16("AWakeLock"), String16("media"));
+ binder::Status status = mPowerManager->acquireWakeLock(
+ binder, POWERMANAGER_PARTIAL_WAKE_LOCK,
+ String16("AWakeLock"), String16("media"),
+ {} /* workSource */, {} /* historyTag */, -1 /* displayId */);
IPCThreadState::self()->restoreCallingIdentity(token);
- if (status == NO_ERROR) {
+ if (status.isOk()) {
mWakeLockToken = binder;
mWakeLockCount++;
return true;
diff --git a/media/libmediaplayerservice/nuplayer/AWakeLock.h b/media/libmediaplayerservice/nuplayer/AWakeLock.h
index 323e7d7..8aa3b41 100644
--- a/media/libmediaplayerservice/nuplayer/AWakeLock.h
+++ b/media/libmediaplayerservice/nuplayer/AWakeLock.h
@@ -18,7 +18,7 @@
#define A_WAKELOCK_H_
#include <media/stagefright/foundation/ABase.h>
-#include <powermanager/IPowerManager.h>
+#include <android/os/IPowerManager.h>
#include <utils/RefBase.h>
namespace android {
@@ -37,7 +37,7 @@
virtual ~AWakeLock();
private:
- sp<IPowerManager> mPowerManager;
+ sp<os::IPowerManager> mPowerManager;
sp<IBinder> mWakeLockToken;
uint32_t mWakeLockCount;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 389249e..d94cecf 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -871,10 +871,12 @@
readFromAMessage(msg, &rate);
status_t err = OK;
if (mRenderer != NULL) {
- // AudioSink allows only 1.f and 0.f for offload mode.
- // For other speed, switch to non-offload mode.
- if (mOffloadAudio && ((rate.mSpeed != 0.f && rate.mSpeed != 1.f)
- || rate.mPitch != 1.f)) {
+ // AudioSink allows only 1.f and 0.f for offload and direct modes.
+ // For other speeds, restart audio to fallback to supported paths
+ bool audioDirectOutput = (mAudioSink->getFlags() & AUDIO_OUTPUT_FLAG_DIRECT) != 0;
+ if ((mOffloadAudio || audioDirectOutput) &&
+ ((rate.mSpeed != 0.f && rate.mSpeed != 1.f) || rate.mPitch != 1.f)) {
+
int64_t currentPositionUs;
if (getCurrentPosition(¤tPositionUs) != OK) {
currentPositionUs = mPreviousSeekTimeUs;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 5b60bbf..2c1f158 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -302,7 +302,7 @@
ALOGV("[%s] onConfigure (surface=%p)", mComponentName.c_str(), mSurface.get());
mCodec = MediaCodec::CreateByType(
- mCodecLooper, mime.c_str(), false /* encoder */, NULL /* err */, mPid, mUid);
+ mCodecLooper, mime.c_str(), false /* encoder */, NULL /* err */, mPid, mUid, format);
int32_t secure = 0;
if (format->findInt32("secure", &secure) && secure != 0) {
if (mCodec != NULL) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 6a8c708..4a65f71 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -1962,7 +1962,7 @@
ALOGV("Mime \"%s\" mapped to audio_format 0x%x",
mime.c_str(), audioFormat);
- int avgBitRate = -1;
+ int avgBitRate = 0;
format->findInt32("bitrate", &avgBitRate);
int32_t aacProfile = -1;
diff --git a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
index f114046..c81a659 100644
--- a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
+++ b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
@@ -65,6 +65,14 @@
return true;
}
+ virtual bool overrideProcessInfo(
+ int /* pid */, int /* procState */, int /* oomScore */) {
+ return true;
+ }
+
+ virtual void removeProcessInfoOverride(int /* pid */) {
+ }
+
private:
DISALLOW_EVIL_CONSTRUCTORS(FakeProcessInfo);
};
diff --git a/media/libmediaplayerservice/tests/stagefrightRecorder/Android.bp b/media/libmediaplayerservice/tests/stagefrightRecorder/Android.bp
index b84d64b..5b16911 100644
--- a/media/libmediaplayerservice/tests/stagefrightRecorder/Android.bp
+++ b/media/libmediaplayerservice/tests/stagefrightRecorder/Android.bp
@@ -73,6 +73,8 @@
"libstagefright",
"libstagefright_foundation",
"libutils",
+ "media_permission-aidl-cpp",
+ "libaudioclient_aidl_conversion",
],
cflags: [
diff --git a/media/libmediaplayerservice/tests/stagefrightRecorder/StagefrightRecorderTest.cpp b/media/libmediaplayerservice/tests/stagefrightRecorder/StagefrightRecorderTest.cpp
index 5751631..6dea53d 100644
--- a/media/libmediaplayerservice/tests/stagefrightRecorder/StagefrightRecorderTest.cpp
+++ b/media/libmediaplayerservice/tests/stagefrightRecorder/StagefrightRecorderTest.cpp
@@ -59,7 +59,10 @@
}
void SetUp() override {
- mStfRecorder = new StagefrightRecorder(String16(LOG_TAG));
+ // TODO b/182392769: use identity util
+ Identity identity;
+ identity.packageName = std::string(LOG_TAG);
+ mStfRecorder = new StagefrightRecorder(identity);
ASSERT_NE(mStfRecorder, nullptr) << "Failed to create the instance of recorder";
mOutputAudioFp = fopen(OUTPUT_FILE_NAME_AUDIO, "wb");
diff --git a/media/libmediatranscoding/.clang-format b/media/libmediatranscoding/.clang-format
index 3198d00..f23b842 100644
--- a/media/libmediatranscoding/.clang-format
+++ b/media/libmediatranscoding/.clang-format
@@ -26,4 +26,26 @@
DerivePointerAlignment: false
IndentWidth: 4
PointerAlignment: Left
-TabWidth: 4
\ No newline at end of file
+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 supports C++17 now, but it seems only Cpp11 will work now.
+# "Cpp11 is a deprecated alias for Latest" according to
+# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
+Standard: Cpp11
diff --git a/media/libmediatranscoding/Android.bp b/media/libmediatranscoding/Android.bp
index 9375e4e..042850c 100644
--- a/media/libmediatranscoding/Android.bp
+++ b/media/libmediatranscoding/Android.bp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-// AIDL interfaces of MediaTranscoding.
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
@@ -24,47 +23,102 @@
default_applicable_licenses: ["frameworks_av_license"],
}
+filegroup {
+ name: "libmediatranscoding_aidl",
+ srcs: [
+ "aidl/android/media/IMediaTranscodingService.aidl",
+ "aidl/android/media/ITranscodingClient.aidl",
+ "aidl/android/media/ITranscodingClientCallback.aidl",
+ "aidl/android/media/TranscodingErrorCode.aidl",
+ "aidl/android/media/TranscodingSessionPriority.aidl",
+ "aidl/android/media/TranscodingSessionStats.aidl",
+ "aidl/android/media/TranscodingType.aidl",
+ "aidl/android/media/TranscodingVideoCodecType.aidl",
+ "aidl/android/media/TranscodingVideoTrackFormat.aidl",
+ "aidl/android/media/TranscodingSessionParcel.aidl",
+ "aidl/android/media/TranscodingRequestParcel.aidl",
+ "aidl/android/media/TranscodingResultParcel.aidl",
+ "aidl/android/media/TranscodingTestConfig.aidl",
+ ],
+ path: "aidl",
+}
+
+// AIDL interfaces of MediaTranscoding.
aidl_interface {
name: "mediatranscoding_aidl_interface",
unstable: true,
local_include_dir: "aidl",
- srcs: [
- "aidl/android/media/IMediaTranscodingService.aidl",
- "aidl/android/media/ITranscodingServiceClient.aidl",
- "aidl/android/media/TranscodingErrorCode.aidl",
- "aidl/android/media/TranscodingJobPriority.aidl",
- "aidl/android/media/TranscodingType.aidl",
- "aidl/android/media/TranscodingVideoCodecType.aidl",
- "aidl/android/media/TranscodingJobParcel.aidl",
- "aidl/android/media/TranscodingRequestParcel.aidl",
- "aidl/android/media/TranscodingResultParcel.aidl",
- ],
+ srcs: [":libmediatranscoding_aidl"],
+ backend:
+ {
+ java: {
+ enabled: true,
+ apex_available: [
+ "com.android.media",
+ "test_com.android.media",
+ ],
+ min_sdk_version: "29",
+ },
+ ndk: {
+ enabled: true,
+ apex_available: [
+ "com.android.media",
+ "test_com.android.media",
+ ],
+ min_sdk_version: "29",
+ },
+ },
}
-cc_library_shared {
+cc_library {
name: "libmediatranscoding",
+ min_sdk_version: "29",
+ apex_available: [
+ "com.android.media",
+ "test_com.android.media",
+ ],
+
srcs: [
- "TranscodingClientManager.cpp"
+ "TranscoderWrapper.cpp",
+ "TranscodingClientManager.cpp",
+ "TranscodingLogger.cpp",
+ "TranscodingResourcePolicy.cpp",
+ "TranscodingSessionController.cpp",
+ "TranscodingThermalPolicy.cpp",
+ "TranscodingUidPolicy.cpp",
],
shared_libs: [
+ "libandroid#31",
"libbinder_ndk",
"libcutils",
"liblog",
"libutils",
+ "libmediatranscoder",
+ "libmediandk",
+ "libstatssocket#30",
+ ],
+ export_shared_lib_headers: [
+ "libmediandk",
],
export_include_dirs: ["include"],
static_libs: [
"mediatranscoding_aidl_interface-ndk_platform",
+ "resourceobserver_aidl_interface-V1-ndk_platform",
+ "libstatslog_media",
],
cflags: [
- "-Werror",
- "-Wno-error=deprecated-declarations",
"-Wall",
+ "-Werror",
+ "-Wformat",
+ "-Wno-error=deprecated-declarations",
+ "-Wthread-safety",
+ "-Wunused",
+ "-Wunreachable-code",
],
sanitize: {
@@ -75,3 +129,43 @@
cfi: true,
},
}
+
+cc_library_static {
+ name: "libstatslog_media",
+ generated_sources: ["statslog_media.cpp"],
+ generated_headers: ["statslog_media.h"],
+ min_sdk_version: "29",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ export_generated_headers: ["statslog_media.h"],
+ apex_available: [
+ "com.android.media",
+ "test_com.android.media",
+ ],
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ "libstatssocket#30",
+ "libutils",
+ ],
+}
+
+genrule {
+ name: "statslog_media.h",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_media.h --module media --namespace android,media,stats",
+ out: [
+ "statslog_media.h",
+ ],
+}
+
+genrule {
+ name: "statslog_media.cpp",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_media.cpp --module media --namespace android,media,stats --importHeader statslog_media.h",
+ out: [
+ "statslog_media.cpp",
+ ],
+}
\ No newline at end of file
diff --git a/media/libmediatranscoding/OWNERS b/media/libmediatranscoding/OWNERS
index 02287cb..b08d573 100644
--- a/media/libmediatranscoding/OWNERS
+++ b/media/libmediatranscoding/OWNERS
@@ -1,3 +1,5 @@
-akersten@google.com
+chz@google.com
+gokrishnan@google.com
hkuang@google.com
lnilsson@google.com
+pawin@google.com
diff --git a/media/libmediatranscoding/TEST_MAPPING b/media/libmediatranscoding/TEST_MAPPING
new file mode 100644
index 0000000..40f7b21
--- /dev/null
+++ b/media/libmediatranscoding/TEST_MAPPING
@@ -0,0 +1,35 @@
+{
+ "presubmit": [
+ {
+ "name": "MediaSampleQueueTests"
+ },
+ {
+ "name": "MediaSampleReaderNDKTests"
+ },
+ {
+ "name": "MediaSampleWriterTests"
+ },
+ {
+ "name": "MediaTrackTranscoderTests"
+ },
+ {
+ "name": "MediaTranscoderTests"
+ },
+ {
+ "name": "PassthroughTrackTranscoderTests"
+ },
+ {
+ "name": "TranscodingClientManager_tests"
+ },
+ {
+ "name": "TranscodingSessionController_tests"
+ },
+ {
+ "name": "VideoTrackTranscoderTests"
+ },
+ {
+ "name": "CtsMediaTranscodingTestCases"
+ }
+ ]
+}
+
diff --git a/media/libmediatranscoding/TranscoderWrapper.cpp b/media/libmediatranscoding/TranscoderWrapper.cpp
new file mode 100644
index 0000000..60b780f
--- /dev/null
+++ b/media/libmediatranscoding/TranscoderWrapper.cpp
@@ -0,0 +1,627 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "TranscoderWrapper"
+
+#include <aidl/android/media/TranscodingErrorCode.h>
+#include <aidl/android/media/TranscodingRequestParcel.h>
+#include <media/MediaTranscoder.h>
+#include <media/NdkCommon.h>
+#include <media/TranscoderWrapper.h>
+#include <media/TranscodingRequest.h>
+#include <utils/AndroidThreads.h>
+#include <utils/Log.h>
+
+#include <thread>
+
+namespace android {
+using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::media::TranscodingErrorCode;
+using ::aidl::android::media::TranscodingVideoCodecType;
+using ::aidl::android::media::TranscodingVideoTrackFormat;
+
+static TranscodingErrorCode toTranscodingError(media_status_t status) {
+ switch (status) {
+ case AMEDIA_OK:
+ return TranscodingErrorCode::kNoError;
+ case AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE: // FALLTHRU
+ case AMEDIACODEC_ERROR_RECLAIMED:
+ return TranscodingErrorCode::kInsufficientResources;
+ case AMEDIA_ERROR_MALFORMED:
+ return TranscodingErrorCode::kMalformed;
+ case AMEDIA_ERROR_UNSUPPORTED:
+ return TranscodingErrorCode::kUnsupported;
+ case AMEDIA_ERROR_INVALID_OBJECT: // FALLTHRU
+ case AMEDIA_ERROR_INVALID_PARAMETER:
+ return TranscodingErrorCode::kInvalidParameter;
+ case AMEDIA_ERROR_INVALID_OPERATION:
+ return TranscodingErrorCode::kInvalidOperation;
+ case AMEDIA_ERROR_IO:
+ return TranscodingErrorCode::kErrorIO;
+ case AMEDIA_ERROR_UNKNOWN: // FALLTHRU
+ default:
+ return TranscodingErrorCode::kUnknown;
+ }
+}
+
+static std::shared_ptr<AMediaFormat> getVideoFormat(
+ const char* originalMime,
+ const std::optional<TranscodingVideoTrackFormat>& requestedFormat) {
+ if (requestedFormat == std::nullopt) {
+ return nullptr;
+ }
+
+ std::shared_ptr<AMediaFormat> format =
+ std::shared_ptr<AMediaFormat>(AMediaFormat_new(), &AMediaFormat_delete);
+ bool changed = false;
+ if (requestedFormat->codecType == TranscodingVideoCodecType::kHevc &&
+ strcmp(originalMime, AMEDIA_MIMETYPE_VIDEO_HEVC)) {
+ AMediaFormat_setString(format.get(), AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_HEVC);
+ changed = true;
+ } else if (requestedFormat->codecType == TranscodingVideoCodecType::kAvc &&
+ strcmp(originalMime, AMEDIA_MIMETYPE_VIDEO_AVC)) {
+ AMediaFormat_setString(format.get(), AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_AVC);
+ changed = true;
+ }
+ if (requestedFormat->bitrateBps > 0) {
+ AMediaFormat_setInt32(format.get(), AMEDIAFORMAT_KEY_BIT_RATE, requestedFormat->bitrateBps);
+ changed = true;
+ }
+ // TODO: translate other fields from requestedFormat to the format for MediaTranscoder.
+ // Also need to determine more settings to expose in TranscodingVideoTrackFormat.
+ if (!changed) {
+ // Use null format for passthru.
+ format.reset();
+ }
+ return format;
+}
+
+//static
+std::string TranscoderWrapper::toString(const Event& event) {
+ std::string typeStr;
+ switch (event.type) {
+ case Event::Start:
+ typeStr = "Start";
+ break;
+ case Event::Pause:
+ typeStr = "Pause";
+ break;
+ case Event::Resume:
+ typeStr = "Resume";
+ break;
+ case Event::Stop:
+ typeStr = "Stop";
+ break;
+ case Event::Finish:
+ typeStr = "Finish";
+ break;
+ case Event::Error:
+ typeStr = "Error";
+ break;
+ case Event::Progress:
+ typeStr = "Progress";
+ break;
+ case Event::HeartBeat:
+ typeStr = "HeartBeat";
+ break;
+ case Event::Abandon:
+ typeStr = "Abandon";
+ break;
+ default:
+ return "(unknown)";
+ }
+ std::string result;
+ result = "session {" + std::to_string(event.clientId) + "," + std::to_string(event.sessionId) +
+ "}: " + typeStr;
+ if (event.type == Event::Error || event.type == Event::Progress) {
+ result += " " + std::to_string(event.arg);
+ }
+ return result;
+}
+
+class TranscoderWrapper::CallbackImpl : public MediaTranscoder::CallbackInterface {
+public:
+ CallbackImpl(const std::shared_ptr<TranscoderWrapper>& owner, ClientIdType clientId,
+ SessionIdType sessionId)
+ : mOwner(owner), mClientId(clientId), mSessionId(sessionId) {}
+
+ virtual void onFinished(const MediaTranscoder* transcoder __unused) override {
+ auto owner = mOwner.lock();
+ if (owner != nullptr) {
+ owner->onFinish(mClientId, mSessionId);
+ }
+ }
+
+ virtual void onError(const MediaTranscoder* transcoder __unused,
+ media_status_t error) override {
+ auto owner = mOwner.lock();
+ if (owner != nullptr) {
+ owner->onError(mClientId, mSessionId, error);
+ }
+ }
+
+ virtual void onProgressUpdate(const MediaTranscoder* transcoder __unused,
+ int32_t progress) override {
+ auto owner = mOwner.lock();
+ if (owner != nullptr) {
+ owner->onProgress(mClientId, mSessionId, progress);
+ }
+ }
+
+ virtual void onHeartBeat(const MediaTranscoder* transcoder __unused) override {
+ auto owner = mOwner.lock();
+ if (owner != nullptr) {
+ owner->onHeartBeat(mClientId, mSessionId);
+ }
+ }
+
+ virtual void onCodecResourceLost(const MediaTranscoder* transcoder __unused,
+ const std::shared_ptr<ndk::ScopedAParcel>& pausedState
+ __unused) override {
+ ALOGV("%s: session {%lld, %d}", __FUNCTION__, (long long)mClientId, mSessionId);
+ }
+
+private:
+ std::weak_ptr<TranscoderWrapper> mOwner;
+ ClientIdType mClientId;
+ SessionIdType mSessionId;
+};
+
+TranscoderWrapper::TranscoderWrapper(const std::shared_ptr<TranscoderCallbackInterface>& cb,
+ const std::shared_ptr<TranscodingLogger>& logger,
+ int64_t heartBeatIntervalUs)
+ : mCallback(cb),
+ mLogger(logger),
+ mHeartBeatIntervalUs(heartBeatIntervalUs),
+ mCurrentClientId(0),
+ mCurrentSessionId(-1),
+ mLooperReady(false) {
+ ALOGV("TranscoderWrapper CTOR: %p", this);
+}
+
+TranscoderWrapper::~TranscoderWrapper() {
+ ALOGV("TranscoderWrapper DTOR: %p", this);
+}
+
+static bool isResourceError(media_status_t err) {
+ return err == AMEDIACODEC_ERROR_RECLAIMED || err == AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE;
+}
+
+void TranscoderWrapper::reportError(ClientIdType clientId, SessionIdType sessionId,
+ media_status_t err) {
+ auto callback = mCallback.lock();
+ if (callback != nullptr) {
+ if (isResourceError(err)) {
+ // Add a placeholder pause state to mPausedStateMap. This is required when resuming.
+ // TODO: remove this when transcoder pause/resume logic is ready. New logic will
+ // no longer use the pause states.
+ auto it = mPausedStateMap.find(SessionKeyType(clientId, sessionId));
+ if (it == mPausedStateMap.end()) {
+ mPausedStateMap.emplace(SessionKeyType(clientId, sessionId),
+ new ndk::ScopedAParcel());
+ }
+
+ callback->onResourceLost(clientId, sessionId);
+ } else {
+ callback->onError(clientId, sessionId, toTranscodingError(err));
+ }
+ }
+}
+
+void TranscoderWrapper::start(ClientIdType clientId, SessionIdType sessionId,
+ const TranscodingRequestParcel& requestParcel, uid_t callingUid,
+ const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
+ TranscodingRequest request{requestParcel};
+ queueEvent(Event::Start, clientId, sessionId, [=] {
+ media_status_t err = handleStart(clientId, sessionId, request, callingUid, clientCb);
+ if (err != AMEDIA_OK) {
+ cleanup();
+ reportError(clientId, sessionId, err);
+ } else {
+ auto callback = mCallback.lock();
+ if (callback != nullptr) {
+ callback->onStarted(clientId, sessionId);
+ }
+ }
+ });
+}
+
+void TranscoderWrapper::pause(ClientIdType clientId, SessionIdType sessionId) {
+ queueEvent(Event::Pause, clientId, sessionId, [=] {
+ media_status_t err = handlePause(clientId, sessionId);
+
+ cleanup();
+
+ if (err != AMEDIA_OK) {
+ reportError(clientId, sessionId, err);
+ } else {
+ auto callback = mCallback.lock();
+ if (callback != nullptr) {
+ callback->onPaused(clientId, sessionId);
+ }
+ }
+ });
+}
+
+void TranscoderWrapper::resume(ClientIdType clientId, SessionIdType sessionId,
+ const TranscodingRequestParcel& requestParcel, uid_t callingUid,
+ const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
+ TranscodingRequest request{requestParcel};
+ queueEvent(Event::Resume, clientId, sessionId, [=] {
+ media_status_t err = handleResume(clientId, sessionId, request, callingUid, clientCb);
+ if (err != AMEDIA_OK) {
+ cleanup();
+ reportError(clientId, sessionId, err);
+ } else {
+ auto callback = mCallback.lock();
+ if (callback != nullptr) {
+ callback->onResumed(clientId, sessionId);
+ }
+ }
+ });
+}
+
+void TranscoderWrapper::stop(ClientIdType clientId, SessionIdType sessionId, bool abandon) {
+ queueEvent(Event::Stop, clientId, sessionId, [=] {
+ if (mTranscoder != nullptr && clientId == mCurrentClientId &&
+ sessionId == mCurrentSessionId) {
+ // Cancelling the currently running session.
+ media_status_t err = mTranscoder->cancel();
+ if (err != AMEDIA_OK) {
+ ALOGW("failed to stop transcoder: %d", err);
+ } else {
+ ALOGI("transcoder stopped");
+ }
+ logSessionEnded(TranscodingLogger::SessionEndedReason::CANCELLED, err);
+ cleanup();
+ } else {
+ // For sessions that's not currently running, release any pausedState for the session.
+ mPausedStateMap.erase(SessionKeyType(clientId, sessionId));
+ }
+ // No callback needed for stop.
+ });
+
+ if (abandon) {
+ queueEvent(Event::Abandon, 0, 0, nullptr);
+ }
+}
+
+void TranscoderWrapper::onFinish(ClientIdType clientId, SessionIdType sessionId) {
+ queueEvent(Event::Finish, clientId, sessionId, [=] {
+ if (mTranscoder != nullptr && clientId == mCurrentClientId &&
+ sessionId == mCurrentSessionId) {
+ logSessionEnded(TranscodingLogger::SessionEndedReason::FINISHED, AMEDIA_OK);
+ cleanup();
+ }
+
+ auto callback = mCallback.lock();
+ if (callback != nullptr) {
+ callback->onFinish(clientId, sessionId);
+ }
+ });
+}
+
+void TranscoderWrapper::onError(ClientIdType clientId, SessionIdType sessionId,
+ media_status_t error) {
+ queueEvent(
+ Event::Error, clientId, sessionId,
+ [=] {
+ if (mTranscoder != nullptr && clientId == mCurrentClientId &&
+ sessionId == mCurrentSessionId) {
+ logSessionEnded(TranscodingLogger::SessionEndedReason::ERROR, error);
+ cleanup();
+ }
+ reportError(clientId, sessionId, error);
+ },
+ error);
+}
+
+void TranscoderWrapper::onProgress(ClientIdType clientId, SessionIdType sessionId,
+ int32_t progress) {
+ queueEvent(
+ Event::Progress, clientId, sessionId,
+ [=] {
+ auto callback = mCallback.lock();
+ if (callback != nullptr) {
+ callback->onProgressUpdate(clientId, sessionId, progress);
+ }
+ },
+ progress);
+}
+
+void TranscoderWrapper::onHeartBeat(ClientIdType clientId, SessionIdType sessionId) {
+ queueEvent(Event::HeartBeat, clientId, sessionId, [=] {
+ auto callback = mCallback.lock();
+ if (callback != nullptr) {
+ callback->onHeartBeat(clientId, sessionId);
+ }
+ });
+}
+
+media_status_t TranscoderWrapper::setupTranscoder(
+ ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& request,
+ uid_t callingUid, const std::shared_ptr<ITranscodingClientCallback>& clientCb,
+ TranscodingLogger::SessionEndedReason* failureReason,
+ const std::shared_ptr<ndk::ScopedAParcel>& pausedState) {
+ if (clientCb == nullptr) {
+ ALOGE("client callback is null");
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (mTranscoder != nullptr) {
+ ALOGE("transcoder already running");
+ return AMEDIA_ERROR_INVALID_OPERATION;
+ }
+
+ // Unwrap the callback and send heartbeats to the client after each operation during setup.
+ auto callback = mCallback.lock();
+ if (callback == nullptr) {
+ return AMEDIA_ERROR_INVALID_OPERATION;
+ }
+
+ Status status;
+ ::ndk::ScopedFileDescriptor srcFd, dstFd;
+ int srcFdInt = request.sourceFd.get();
+ if (srcFdInt < 0) {
+ status = clientCb->openFileDescriptor(request.sourceFilePath, "r", &srcFd);
+ if (!status.isOk() || srcFd.get() < 0) {
+ ALOGE("failed to open source");
+ *failureReason = TranscodingLogger::SessionEndedReason::OPEN_SRC_FD_FAILED;
+ return AMEDIA_ERROR_IO;
+ }
+ srcFdInt = srcFd.get();
+ }
+
+ callback->onHeartBeat(clientId, sessionId);
+
+ int dstFdInt = request.destinationFd.get();
+ if (dstFdInt < 0) {
+ // Open dest file with "rw", as the transcoder could potentially reuse part of it
+ // for resume case. We might want the further differentiate and open with "w" only
+ // for start.
+ status = clientCb->openFileDescriptor(request.destinationFilePath, "rw", &dstFd);
+ if (!status.isOk() || dstFd.get() < 0) {
+ ALOGE("failed to open destination");
+ *failureReason = TranscodingLogger::SessionEndedReason::OPEN_DST_FD_FAILED;
+ return AMEDIA_ERROR_IO;
+ }
+ dstFdInt = dstFd.get();
+ }
+
+ callback->onHeartBeat(clientId, sessionId);
+
+ mCurrentClientId = clientId;
+ mCurrentSessionId = sessionId;
+ mCurrentCallingUid = callingUid;
+ mTranscoderCb = std::make_shared<CallbackImpl>(shared_from_this(), clientId, sessionId);
+ mTranscoder = MediaTranscoder::create(mTranscoderCb, mHeartBeatIntervalUs, request.clientPid,
+ request.clientUid, pausedState);
+ if (mTranscoder == nullptr) {
+ ALOGE("failed to create transcoder");
+ *failureReason = TranscodingLogger::SessionEndedReason::CREATE_FAILED;
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+
+ callback->onHeartBeat(clientId, sessionId);
+
+ media_status_t err = mTranscoder->configureSource(srcFdInt);
+ if (err != AMEDIA_OK) {
+ ALOGE("failed to configure source: %d", err);
+ *failureReason = TranscodingLogger::SessionEndedReason::CONFIG_SRC_FAILED;
+ return err;
+ }
+
+ callback->onHeartBeat(clientId, sessionId);
+
+ std::vector<std::shared_ptr<AMediaFormat>> trackFormats = mTranscoder->getTrackFormats();
+ if (trackFormats.size() == 0) {
+ ALOGE("failed to get track formats!");
+ *failureReason = TranscodingLogger::SessionEndedReason::NO_TRACKS;
+ return AMEDIA_ERROR_MALFORMED;
+ }
+
+ callback->onHeartBeat(clientId, sessionId);
+
+ for (int i = 0; i < trackFormats.size(); ++i) {
+ std::shared_ptr<AMediaFormat> format;
+ const char* mime = nullptr;
+ AMediaFormat_getString(trackFormats[i].get(), AMEDIAFORMAT_KEY_MIME, &mime);
+
+ if (!strncmp(mime, "video/", 6)) {
+ format = getVideoFormat(mime, request.requestedVideoTrackFormat);
+
+ mSrcFormat = trackFormats[i];
+ mDstFormat = format;
+ }
+
+ err = mTranscoder->configureTrackFormat(i, format.get());
+ if (err != AMEDIA_OK) {
+ ALOGE("failed to configure track format for track %d: %d", i, err);
+ *failureReason = TranscodingLogger::SessionEndedReason::CONFIG_TRACK_FAILED;
+ return err;
+ }
+
+ callback->onHeartBeat(clientId, sessionId);
+ }
+
+ err = mTranscoder->configureDestination(dstFdInt);
+ if (err != AMEDIA_OK) {
+ ALOGE("failed to configure dest: %d", err);
+ *failureReason = TranscodingLogger::SessionEndedReason::CONFIG_DST_FAILED;
+ return err;
+ }
+
+ callback->onHeartBeat(clientId, sessionId);
+
+ return AMEDIA_OK;
+}
+
+media_status_t TranscoderWrapper::handleStart(
+ ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& request,
+ uid_t callingUid, const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
+ ALOGI("%s: setting up transcoder for start", __FUNCTION__);
+ TranscodingLogger::SessionEndedReason reason = TranscodingLogger::SessionEndedReason::UNKNOWN;
+ media_status_t err =
+ setupTranscoder(clientId, sessionId, request, callingUid, clientCb, &reason);
+ if (err != AMEDIA_OK) {
+ ALOGE("%s: failed to setup transcoder", __FUNCTION__);
+ logSessionEnded(reason, err);
+ return err;
+ }
+
+ mTranscodeStartTime = std::chrono::steady_clock::now();
+
+ err = mTranscoder->start();
+ if (err != AMEDIA_OK) {
+ ALOGE("%s: failed to start transcoder: %d", __FUNCTION__, err);
+ logSessionEnded(TranscodingLogger::SessionEndedReason::START_FAILED, err);
+ return err;
+ }
+
+ ALOGI("%s: transcoder started", __FUNCTION__);
+ return AMEDIA_OK;
+}
+
+media_status_t TranscoderWrapper::handlePause(ClientIdType clientId, SessionIdType sessionId) {
+ if (mTranscoder == nullptr) {
+ ALOGE("%s: transcoder is not running", __FUNCTION__);
+ return AMEDIA_ERROR_INVALID_OPERATION;
+ }
+
+ if (clientId != mCurrentClientId || sessionId != mCurrentSessionId) {
+ ALOGW("%s: stopping session {%lld, %d} that's not current session {%lld, %d}", __FUNCTION__,
+ (long long)clientId, sessionId, (long long)mCurrentClientId, mCurrentSessionId);
+ }
+
+ ALOGI("%s: pausing transcoder", __FUNCTION__);
+
+ std::shared_ptr<ndk::ScopedAParcel> pauseStates;
+ media_status_t err = mTranscoder->pause(&pauseStates);
+ logSessionEnded(TranscodingLogger::SessionEndedReason::PAUSED, err);
+ if (err != AMEDIA_OK) {
+ ALOGE("%s: failed to pause transcoder: %d", __FUNCTION__, err);
+ return err;
+ }
+ mPausedStateMap[SessionKeyType(clientId, sessionId)] = pauseStates;
+
+ ALOGI("%s: transcoder paused", __FUNCTION__);
+ return AMEDIA_OK;
+}
+
+media_status_t TranscoderWrapper::handleResume(
+ ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& request,
+ uid_t callingUid, const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
+ std::shared_ptr<ndk::ScopedAParcel> pausedState;
+ auto it = mPausedStateMap.find(SessionKeyType(clientId, sessionId));
+ if (it != mPausedStateMap.end()) {
+ pausedState = it->second;
+ mPausedStateMap.erase(it);
+ } else {
+ ALOGE("%s: can't find paused state", __FUNCTION__);
+ return AMEDIA_ERROR_INVALID_OPERATION;
+ }
+
+ ALOGI("%s: setting up transcoder for resume", __FUNCTION__);
+ TranscodingLogger::SessionEndedReason reason = TranscodingLogger::SessionEndedReason::UNKNOWN;
+ media_status_t err = setupTranscoder(clientId, sessionId, request, callingUid, clientCb,
+ &reason, pausedState);
+ if (err != AMEDIA_OK) {
+ ALOGE("%s: failed to setup transcoder: %d", __FUNCTION__, err);
+ logSessionEnded(reason, err);
+ return err;
+ }
+
+ // Note: For now resume() will just restart transcoding from the beginning, so there is no need
+ // to distinguish between resume and start from a performance perspective.
+ mTranscodeStartTime = std::chrono::steady_clock::now();
+
+ err = mTranscoder->resume();
+ if (err != AMEDIA_OK) {
+ ALOGE("%s: failed to resume transcoder: %d", __FUNCTION__, err);
+ logSessionEnded(TranscodingLogger::SessionEndedReason::RESUME_FAILED, err);
+ return err;
+ }
+
+ ALOGI("%s: transcoder resumed", __FUNCTION__);
+ return AMEDIA_OK;
+}
+
+void TranscoderWrapper::cleanup() {
+ mCurrentClientId = 0;
+ mCurrentSessionId = -1;
+ mCurrentCallingUid = -1;
+ mTranscoderCb = nullptr;
+ mTranscoder = nullptr;
+ mSrcFormat = nullptr;
+ mDstFormat = nullptr;
+}
+
+void TranscoderWrapper::logSessionEnded(const TranscodingLogger::SessionEndedReason& reason,
+ int error) {
+ std::chrono::microseconds transcodeDuration(-1);
+ if (reason == TranscodingLogger::SessionEndedReason::FINISHED && error == AMEDIA_OK) {
+ transcodeDuration = std::chrono::duration_cast<std::chrono::microseconds>(
+ std::chrono::steady_clock::now() - mTranscodeStartTime);
+ }
+
+ mLogger->logSessionEnded(reason, mCurrentCallingUid, error, transcodeDuration, mSrcFormat.get(),
+ mDstFormat.get());
+}
+
+void TranscoderWrapper::queueEvent(Event::Type type, ClientIdType clientId, SessionIdType sessionId,
+ const std::function<void()> runnable, int32_t arg) {
+ std::scoped_lock lock{mLock};
+
+ if (!mLooperReady) {
+ // A shared_ptr to ourselves is given to the thread's stack, so that the TranscoderWrapper
+ // object doesn't go away until the thread exits. When a watchdog timeout happens, this
+ // allows the session controller to release its reference to the TranscoderWrapper object
+ // without blocking on the thread exits.
+ std::thread([owner = shared_from_this()]() { owner->threadLoop(); }).detach();
+ mLooperReady = true;
+ }
+
+ mQueue.push_back({type, clientId, sessionId, runnable, arg});
+ mCondition.notify_one();
+}
+
+void TranscoderWrapper::threadLoop() {
+ androidSetThreadPriority(0 /*tid (0 = current) */, ANDROID_PRIORITY_BACKGROUND);
+ std::unique_lock<std::mutex> lock{mLock};
+ // TranscoderWrapper currently lives in the transcoding service, as long as
+ // MediaTranscodingService itself.
+ while (true) {
+ // Wait for the next event.
+ while (mQueue.empty()) {
+ mCondition.wait(lock);
+ }
+
+ Event event = *mQueue.begin();
+ mQueue.pop_front();
+
+ ALOGV("%s: %s", __FUNCTION__, toString(event).c_str());
+
+ if (event.type == Event::Abandon) {
+ break;
+ }
+
+ lock.unlock();
+ event.runnable();
+ lock.lock();
+ }
+}
+} // namespace android
diff --git a/media/libmediatranscoding/TranscodingClientManager.cpp b/media/libmediatranscoding/TranscodingClientManager.cpp
index 7252437..6dbcaf9 100644
--- a/media/libmediatranscoding/TranscodingClientManager.cpp
+++ b/media/libmediatranscoding/TranscodingClientManager.cpp
@@ -17,129 +17,449 @@
// #define LOG_NDEBUG 0
#define LOG_TAG "TranscodingClientManager"
+#include <aidl/android/media/BnTranscodingClient.h>
+#include <aidl/android/media/IMediaTranscodingService.h>
+#include <android/binder_ibinder.h>
+#include <android/permission_manager.h>
#include <inttypes.h>
#include <media/TranscodingClientManager.h>
+#include <media/TranscodingRequest.h>
+#include <media/TranscodingUidPolicy.h>
+#include <private/android_filesystem_config.h>
#include <utils/Log.h>
+#include <utils/String16.h>
namespace android {
-using Status = ::ndk::ScopedAStatus;
+static_assert(sizeof(ClientIdType) == sizeof(void*), "ClientIdType should be pointer-sized");
-// static
-TranscodingClientManager& TranscodingClientManager::getInstance() {
- static TranscodingClientManager gInstance{};
- return gInstance;
+using ::aidl::android::media::BnTranscodingClient;
+using ::aidl::android::media::IMediaTranscodingService; // For service error codes
+using ::aidl::android::media::TranscodingRequestParcel;
+using ::aidl::android::media::TranscodingSessionParcel;
+using Status = ::ndk::ScopedAStatus;
+using ::ndk::SpAIBinder;
+
+//static
+std::atomic<ClientIdType> TranscodingClientManager::sCookieCounter = 0;
+//static
+std::mutex TranscodingClientManager::sCookie2ClientLock;
+//static
+std::map<ClientIdType, std::shared_ptr<TranscodingClientManager::ClientImpl>>
+ TranscodingClientManager::sCookie2Client;
+///////////////////////////////////////////////////////////////////////////////
+
+// Convenience methods for constructing binder::Status objects for error returns
+#define STATUS_ERROR_FMT(errorCode, errorString, ...) \
+ Status::fromServiceSpecificErrorWithMessage( \
+ errorCode, \
+ String8::format("%s:%d: " errorString, __FUNCTION__, __LINE__, ##__VA_ARGS__))
+
+/**
+ * ClientImpl implements a single client and contains all its information.
+ */
+struct TranscodingClientManager::ClientImpl : public BnTranscodingClient {
+ /* The remote client callback that this ClientInfo is associated with.
+ * Once the ClientInfo is created, we hold an SpAIBinder so that the binder
+ * object doesn't get created again, otherwise the binder object pointer
+ * may not be unique.
+ */
+ SpAIBinder mClientBinder;
+ std::shared_ptr<ITranscodingClientCallback> mClientCallback;
+ /* A unique id assigned to the client by the service. This number is used
+ * by the service for indexing. Here we use the binder object's pointer
+ * (casted to int64t_t) as the client id.
+ */
+ ClientIdType mClientId;
+ std::string mClientName;
+ std::string mClientOpPackageName;
+
+ // Next sessionId to assign.
+ std::atomic<int32_t> mNextSessionId;
+ // Whether this client has been unregistered already.
+ std::atomic<bool> mAbandoned;
+ // Weak pointer to the client manager for this client.
+ std::weak_ptr<TranscodingClientManager> mOwner;
+
+ ClientImpl(const std::shared_ptr<ITranscodingClientCallback>& callback,
+ const std::string& clientName, const std::string& opPackageName,
+ const std::weak_ptr<TranscodingClientManager>& owner);
+
+ Status submitRequest(const TranscodingRequestParcel& /*in_request*/,
+ TranscodingSessionParcel* /*out_session*/,
+ bool* /*_aidl_return*/) override;
+
+ Status cancelSession(int32_t /*in_sessionId*/, bool* /*_aidl_return*/) override;
+
+ Status getSessionWithId(int32_t /*in_sessionId*/, TranscodingSessionParcel* /*out_session*/,
+ bool* /*_aidl_return*/) override;
+
+ Status addClientUid(int32_t /*in_sessionId*/, int32_t /*in_clientUid*/,
+ bool* /*_aidl_return*/) override;
+
+ Status getClientUids(int32_t /*in_sessionId*/,
+ std::optional<std::vector<int32_t>>* /*_aidl_return*/) override;
+
+ Status unregister() override;
+};
+
+TranscodingClientManager::ClientImpl::ClientImpl(
+ const std::shared_ptr<ITranscodingClientCallback>& callback, const std::string& clientName,
+ const std::string& opPackageName, const std::weak_ptr<TranscodingClientManager>& owner)
+ : mClientBinder((callback != nullptr) ? callback->asBinder() : nullptr),
+ mClientCallback(callback),
+ mClientId(sCookieCounter.fetch_add(1, std::memory_order_relaxed)),
+ mClientName(clientName),
+ mClientOpPackageName(opPackageName),
+ mNextSessionId(0),
+ mAbandoned(false),
+ mOwner(owner) {}
+
+Status TranscodingClientManager::ClientImpl::submitRequest(
+ const TranscodingRequestParcel& in_request, TranscodingSessionParcel* out_session,
+ bool* _aidl_return) {
+ *_aidl_return = false;
+
+ std::shared_ptr<TranscodingClientManager> owner;
+ if (mAbandoned || (owner = mOwner.lock()) == nullptr) {
+ return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED);
+ }
+
+ if (in_request.sourceFilePath.empty() || in_request.destinationFilePath.empty()) {
+ return Status::ok();
+ }
+
+ int32_t callingPid = AIBinder_getCallingPid();
+ int32_t callingUid = AIBinder_getCallingUid();
+ int32_t in_clientUid = in_request.clientUid;
+ int32_t in_clientPid = in_request.clientPid;
+
+ // Check if we can trust clientUid. Only privilege caller could forward the
+ // uid on app client's behalf.
+ if (in_clientUid == IMediaTranscodingService::USE_CALLING_UID) {
+ in_clientUid = callingUid;
+ } else if (in_clientUid < 0) {
+ return Status::ok();
+ } else if (in_clientUid != callingUid && !owner->isTrustedCaller(callingPid, callingUid)) {
+ ALOGE("submitRequest rejected (clientPid %d, clientUid %d) "
+ "(don't trust callingUid %d)",
+ in_clientPid, in_clientUid, callingUid);
+ return STATUS_ERROR_FMT(IMediaTranscodingService::ERROR_PERMISSION_DENIED,
+ "submitRequest rejected (clientPid %d, clientUid %d) "
+ "(don't trust callingUid %d)",
+ in_clientPid, in_clientUid, callingUid);
+ }
+
+ // Check if we can trust clientPid. Only privilege caller could forward the
+ // pid on app client's behalf.
+ if (in_clientPid == IMediaTranscodingService::USE_CALLING_PID) {
+ in_clientPid = callingPid;
+ } else if (in_clientPid < 0) {
+ return Status::ok();
+ } else if (in_clientPid != callingPid && !owner->isTrustedCaller(callingPid, callingUid)) {
+ ALOGE("submitRequest rejected (clientPid %d, clientUid %d) "
+ "(don't trust callingUid %d)",
+ in_clientPid, in_clientUid, callingUid);
+ return STATUS_ERROR_FMT(IMediaTranscodingService::ERROR_PERMISSION_DENIED,
+ "submitRequest rejected (clientPid %d, clientUid %d) "
+ "(don't trust callingUid %d)",
+ in_clientPid, in_clientUid, callingUid);
+ }
+
+ int32_t sessionId = mNextSessionId.fetch_add(1);
+
+ *_aidl_return = owner->mSessionController->submit(mClientId, sessionId, callingUid,
+ in_clientUid, in_request, mClientCallback);
+
+ if (*_aidl_return) {
+ out_session->sessionId = sessionId;
+
+ // TODO(chz): is some of this coming from SessionController?
+ *(TranscodingRequest*)&out_session->request = in_request;
+ out_session->awaitNumberOfSessions = 0;
+ }
+
+ return Status::ok();
}
+Status TranscodingClientManager::ClientImpl::cancelSession(int32_t in_sessionId,
+ bool* _aidl_return) {
+ *_aidl_return = false;
+
+ std::shared_ptr<TranscodingClientManager> owner;
+ if (mAbandoned || (owner = mOwner.lock()) == nullptr) {
+ return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED);
+ }
+
+ if (in_sessionId < 0) {
+ return Status::ok();
+ }
+
+ *_aidl_return = owner->mSessionController->cancel(mClientId, in_sessionId);
+ return Status::ok();
+}
+
+Status TranscodingClientManager::ClientImpl::getSessionWithId(int32_t in_sessionId,
+ TranscodingSessionParcel* out_session,
+ bool* _aidl_return) {
+ *_aidl_return = false;
+
+ std::shared_ptr<TranscodingClientManager> owner;
+ if (mAbandoned || (owner = mOwner.lock()) == nullptr) {
+ return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED);
+ }
+
+ if (in_sessionId < 0) {
+ return Status::ok();
+ }
+
+ *_aidl_return =
+ owner->mSessionController->getSession(mClientId, in_sessionId, &out_session->request);
+
+ if (*_aidl_return) {
+ out_session->sessionId = in_sessionId;
+ out_session->awaitNumberOfSessions = 0;
+ }
+ return Status::ok();
+}
+
+Status TranscodingClientManager::ClientImpl::addClientUid(int32_t in_sessionId,
+ int32_t in_clientUid,
+ bool* _aidl_return) {
+ *_aidl_return = false;
+
+ std::shared_ptr<TranscodingClientManager> owner;
+ if (mAbandoned || (owner = mOwner.lock()) == nullptr) {
+ return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED);
+ }
+
+ if (in_sessionId < 0) {
+ return Status::ok();
+ }
+
+ int32_t callingPid = AIBinder_getCallingPid();
+ int32_t callingUid = AIBinder_getCallingUid();
+
+ // Check if we can trust clientUid. Only privilege caller could add uid to existing sessions.
+ if (in_clientUid == IMediaTranscodingService::USE_CALLING_UID) {
+ in_clientUid = callingUid;
+ } else if (in_clientUid < 0) {
+ return Status::ok();
+ } else if (in_clientUid != callingUid && !owner->isTrustedCaller(callingPid, callingUid)) {
+ ALOGE("addClientUid rejected (clientUid %d) "
+ "(don't trust callingUid %d)",
+ in_clientUid, callingUid);
+ return STATUS_ERROR_FMT(IMediaTranscodingService::ERROR_PERMISSION_DENIED,
+ "addClientUid rejected (clientUid %d) "
+ "(don't trust callingUid %d)",
+ in_clientUid, callingUid);
+ }
+
+ *_aidl_return = owner->mSessionController->addClientUid(mClientId, in_sessionId, in_clientUid);
+ return Status::ok();
+}
+
+Status TranscodingClientManager::ClientImpl::getClientUids(
+ int32_t in_sessionId, std::optional<std::vector<int32_t>>* _aidl_return) {
+ *_aidl_return = std::nullopt;
+
+ std::shared_ptr<TranscodingClientManager> owner;
+ if (mAbandoned || (owner = mOwner.lock()) == nullptr) {
+ return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED);
+ }
+
+ if (in_sessionId < 0) {
+ return Status::ok();
+ }
+
+ std::vector<int32_t> result;
+
+ if (owner->mSessionController->getClientUids(mClientId, in_sessionId, &result)) {
+ *_aidl_return = result;
+ }
+ return Status::ok();
+}
+
+Status TranscodingClientManager::ClientImpl::unregister() {
+ bool abandoned = mAbandoned.exchange(true);
+
+ std::shared_ptr<TranscodingClientManager> owner;
+ if (abandoned || (owner = mOwner.lock()) == nullptr) {
+ return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED);
+ }
+
+ // Use sessionId == -1 to cancel all realtime sessions for this client with the controller.
+ owner->mSessionController->cancel(mClientId, -1);
+ owner->removeClient(mClientId);
+
+ return Status::ok();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
// static
void TranscodingClientManager::BinderDiedCallback(void* cookie) {
- int32_t clientId = static_cast<int32_t>(reinterpret_cast<intptr_t>(cookie));
- ALOGD("Client %" PRId32 " is dead", clientId);
- // Don't check for pid validity since we know it's already dead.
- TranscodingClientManager& manager = TranscodingClientManager::getInstance();
- manager.removeClient(clientId);
+ ClientIdType clientId = reinterpret_cast<ClientIdType>(cookie);
+
+ ALOGD("Client %lld is dead", (long long)clientId);
+
+ std::shared_ptr<ClientImpl> client;
+
+ {
+ std::scoped_lock lock{sCookie2ClientLock};
+
+ auto it = sCookie2Client.find(clientId);
+ if (it != sCookie2Client.end()) {
+ client = it->second;
+ }
+ }
+
+ if (client != nullptr) {
+ client->unregister();
+ }
}
-TranscodingClientManager::TranscodingClientManager()
- : mDeathRecipient(AIBinder_DeathRecipient_new(BinderDiedCallback)) {
+TranscodingClientManager::TranscodingClientManager(
+ const std::shared_ptr<ControllerClientInterface>& controller)
+ : mDeathRecipient(AIBinder_DeathRecipient_new(BinderDiedCallback)),
+ mSessionController(controller) {
ALOGD("TranscodingClientManager started");
+ for (uid_t uid : {AID_ROOT, AID_SYSTEM, AID_SHELL, AID_MEDIA}) {
+ mTrustedUids.insert(uid);
+ }
}
TranscodingClientManager::~TranscodingClientManager() {
ALOGD("TranscodingClientManager exited");
}
-bool TranscodingClientManager::isClientIdRegistered(int32_t clientId) const {
- std::scoped_lock lock{mLock};
- return mClientIdToClientInfoMap.find(clientId) != mClientIdToClientInfoMap.end();
-}
-
void TranscodingClientManager::dumpAllClients(int fd, const Vector<String16>& args __unused) {
String8 result;
const size_t SIZE = 256;
char buffer[SIZE];
+ std::scoped_lock lock{mLock};
- snprintf(buffer, SIZE, " Total num of Clients: %zu\n", mClientIdToClientInfoMap.size());
- result.append(buffer);
-
- if (mClientIdToClientInfoMap.size() > 0) {
- snprintf(buffer, SIZE, "========== Dumping all clients =========\n");
+ if (mClientIdToClientMap.size() > 0) {
+ snprintf(buffer, SIZE, "\n========== Dumping all clients =========\n");
result.append(buffer);
}
- for (const auto& iter : mClientIdToClientInfoMap) {
- const std::shared_ptr<ITranscodingServiceClient> client = iter.second->mClient;
- std::string clientName;
- Status status = client->getName(&clientName);
- if (!status.isOk()) {
- ALOGE("Failed to get client: %d information", iter.first);
- continue;
- }
- snprintf(buffer, SIZE, " -- Clients: %d name: %s\n", iter.first, clientName.c_str());
+ snprintf(buffer, SIZE, " Total num of Clients: %zu\n", mClientIdToClientMap.size());
+ result.append(buffer);
+
+ for (const auto& iter : mClientIdToClientMap) {
+ snprintf(buffer, SIZE, " Client %lld: pkg: %s\n", (long long)iter.first,
+ iter.second->mClientName.c_str());
result.append(buffer);
}
write(fd, result.string(), result.size());
}
-status_t TranscodingClientManager::addClient(std::unique_ptr<ClientInfo> client) {
- // Validate the client.
- if (client == nullptr || client->mClientId < 0 || client->mClientPid < 0 ||
- client->mClientUid < 0 || client->mClientOpPackageName.empty() ||
- client->mClientOpPackageName == "") {
- ALOGE("Invalid client");
- return BAD_VALUE;
+bool TranscodingClientManager::isTrustedCaller(pid_t pid, uid_t uid) {
+ if (uid > 0 && mTrustedUids.count(uid) > 0) {
+ return true;
}
+ int32_t result;
+ if (__builtin_available(android __TRANSCODING_MIN_API__, *)) {
+ if (APermissionManager_checkPermission("android.permission.WRITE_MEDIA_STORAGE", pid, uid,
+ &result) == PERMISSION_MANAGER_STATUS_OK &&
+ result == PERMISSION_MANAGER_PERMISSION_GRANTED) {
+ mTrustedUids.insert(uid);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+status_t TranscodingClientManager::addClient(
+ const std::shared_ptr<ITranscodingClientCallback>& callback, const std::string& clientName,
+ const std::string& opPackageName, std::shared_ptr<ITranscodingClient>* outClient) {
+ int32_t callingPid = AIBinder_getCallingPid();
+ int32_t callingUid = AIBinder_getCallingUid();
+
+ // Check if client has the permission
+ if (!isTrustedCaller(callingPid, callingUid)) {
+ ALOGE("addClient rejected (clientPid %d, clientUid %d)", callingPid, callingUid);
+ return IMediaTranscodingService::ERROR_PERMISSION_DENIED;
+ }
+
+ // Validate the client.
+ if (callback == nullptr || clientName.empty() || opPackageName.empty()) {
+ ALOGE("Invalid client");
+ return IMediaTranscodingService::ERROR_ILLEGAL_ARGUMENT;
+ }
+
+ SpAIBinder binder = callback->asBinder();
+
std::scoped_lock lock{mLock};
- // Check if the client already exists.
- if (mClientIdToClientInfoMap.count(client->mClientId) != 0) {
- ALOGW("Client already exists.");
- return ALREADY_EXISTS;
+ // Checks if the client already registers.
+ if (mRegisteredCallbacks.count((uintptr_t)binder.get()) > 0) {
+ return IMediaTranscodingService::ERROR_ALREADY_EXISTS;
}
- ALOGD("Adding client id %d pid: %d uid: %d %s", client->mClientId, client->mClientPid,
- client->mClientUid, client->mClientOpPackageName.c_str());
+ // Creates the client (with the id assigned by ClientImpl).
+ std::shared_ptr<ClientImpl> client = ::ndk::SharedRefBase::make<ClientImpl>(
+ callback, clientName, opPackageName, shared_from_this());
- AIBinder_linkToDeath(client->mClient->asBinder().get(), mDeathRecipient.get(),
+ ALOGD("Adding client id %lld, name %s, package %s", (long long)client->mClientId,
+ client->mClientName.c_str(), client->mClientOpPackageName.c_str());
+
+ {
+ std::scoped_lock lock{sCookie2ClientLock};
+ sCookie2Client.emplace(std::make_pair(client->mClientId, client));
+ }
+
+ AIBinder_linkToDeath(binder.get(), mDeathRecipient.get(),
reinterpret_cast<void*>(client->mClientId));
// Adds the new client to the map.
- mClientIdToClientInfoMap[client->mClientId] = std::move(client);
+ mRegisteredCallbacks.insert((uintptr_t)binder.get());
+ mClientIdToClientMap[client->mClientId] = client;
+
+ *outClient = client;
return OK;
}
-status_t TranscodingClientManager::removeClient(int32_t clientId) {
- ALOGD("Removing client id %d", clientId);
+status_t TranscodingClientManager::removeClient(ClientIdType clientId) {
+ ALOGD("Removing client id %lld", (long long)clientId);
std::scoped_lock lock{mLock};
// Checks if the client is valid.
- auto it = mClientIdToClientInfoMap.find(clientId);
- if (it == mClientIdToClientInfoMap.end()) {
- ALOGE("Client id %d does not exist", clientId);
- return INVALID_OPERATION;
+ auto it = mClientIdToClientMap.find(clientId);
+ if (it == mClientIdToClientMap.end()) {
+ ALOGE("Client id %lld does not exist", (long long)clientId);
+ return IMediaTranscodingService::ERROR_INVALID_OPERATION;
}
- std::shared_ptr<ITranscodingServiceClient> client = it->second->mClient;
+ SpAIBinder binder = it->second->mClientBinder;
// Check if the client still live. If alive, unlink the death.
- if (client) {
- AIBinder_unlinkToDeath(client->asBinder().get(), mDeathRecipient.get(),
- reinterpret_cast<void*>(clientId));
+ if (binder.get() != nullptr) {
+ AIBinder_unlinkToDeath(binder.get(), mDeathRecipient.get(),
+ reinterpret_cast<void*>(it->second->mClientId));
+ }
+
+ {
+ std::scoped_lock lock{sCookie2ClientLock};
+ sCookie2Client.erase(it->second->mClientId);
}
// Erase the entry.
- mClientIdToClientInfoMap.erase(it);
+ mClientIdToClientMap.erase(it);
+ mRegisteredCallbacks.erase((uintptr_t)binder.get());
return OK;
}
size_t TranscodingClientManager::getNumOfClients() const {
std::scoped_lock lock{mLock};
- return mClientIdToClientInfoMap.size();
+ return mClientIdToClientMap.size();
}
} // namespace android
diff --git a/media/libmediatranscoding/TranscodingLogger.cpp b/media/libmediatranscoding/TranscodingLogger.cpp
new file mode 100644
index 0000000..29a52b0
--- /dev/null
+++ b/media/libmediatranscoding/TranscodingLogger.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "TranscodingLogger"
+
+#include <media/NdkCommon.h>
+#include <media/TranscodingLogger.h>
+#include <statslog_media.h>
+#include <utils/Log.h>
+
+#include <cmath>
+#include <string>
+
+namespace android {
+
+static_assert(TranscodingLogger::UNKNOWN ==
+ android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__UNKNOWN,
+ "Session event mismatch");
+static_assert(TranscodingLogger::FINISHED ==
+ android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__FINISHED,
+ "Session event mismatch");
+static_assert(TranscodingLogger::ERROR ==
+ android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__ERROR,
+ "Session event mismatch");
+static_assert(TranscodingLogger::PAUSED ==
+ android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__PAUSED,
+ "Session event mismatch");
+static_assert(TranscodingLogger::CANCELLED ==
+ android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__CANCELLED,
+ "Session event mismatch");
+static_assert(TranscodingLogger::START_FAILED ==
+ android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__START_FAILED,
+ "Session event mismatch");
+static_assert(TranscodingLogger::RESUME_FAILED ==
+ android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__RESUME_FAILED,
+ "Session event mismatch");
+static_assert(TranscodingLogger::CREATE_FAILED ==
+ android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__CREATE_FAILED,
+ "Session event mismatch");
+static_assert(
+ TranscodingLogger::CONFIG_SRC_FAILED ==
+ android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__CONFIG_SRC_FAILED,
+ "Session event mismatch");
+static_assert(
+ TranscodingLogger::CONFIG_DST_FAILED ==
+ android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__CONFIG_DST_FAILED,
+ "Session event mismatch");
+static_assert(
+ TranscodingLogger::CONFIG_TRACK_FAILED ==
+ android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__CONFIG_TRACK_FAILED,
+ "Session event mismatch");
+static_assert(
+ TranscodingLogger::OPEN_SRC_FD_FAILED ==
+ android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__OPEN_SRC_FD_FAILED,
+ "Session event mismatch");
+static_assert(
+ TranscodingLogger::OPEN_DST_FD_FAILED ==
+ android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__OPEN_DST_FD_FAILED,
+ "Session event mismatch");
+static_assert(TranscodingLogger::NO_TRACKS ==
+ android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED__REASON__NO_TRACKS,
+ "Session event mismatch");
+
+static inline int32_t getInt32(AMediaFormat* fmt, const char* key, int32_t defaultValue = -1) {
+ int32_t value;
+ if (fmt == nullptr || !AMediaFormat_getInt32(fmt, key, &value)) {
+ ALOGW("Unable to get %s", key);
+ value = defaultValue;
+ }
+ return value;
+}
+
+// Note: returned string is owned by format and only valid until the next getString.
+static inline const char* getString(AMediaFormat* fmt, const char* key,
+ const char* defaultValue = "(null)") {
+ const char* value;
+ if (fmt == nullptr || !AMediaFormat_getString(fmt, key, &value)) {
+ ALOGW("Unable to get %s", key);
+ value = defaultValue;
+ }
+ return value;
+}
+
+TranscodingLogger::TranscodingLogger()
+ : mSessionEndedAtomWriter(&android::media::stats::stats_write) {}
+
+void TranscodingLogger::logSessionEnded(enum SessionEndedReason reason, uid_t callingUid,
+ int status, std::chrono::microseconds duration,
+ AMediaFormat* srcFormat, AMediaFormat* dstFormat) {
+ logSessionEnded(std::chrono::steady_clock::now(), reason, callingUid, status, duration,
+ srcFormat, dstFormat);
+}
+
+void TranscodingLogger::logSessionEnded(const std::chrono::steady_clock::time_point& now,
+ enum SessionEndedReason reason, uid_t callingUid,
+ int status, std::chrono::microseconds duration,
+ AMediaFormat* srcFormat, AMediaFormat* dstFormat) {
+ if (srcFormat == nullptr) {
+ ALOGE("Source format is null. Dropping event.");
+ return;
+ }
+
+ if (!shouldLogAtom(now, status)) {
+ ALOGD("Maximum logged event count reached. Dropping event.");
+ return;
+ }
+
+ // Extract the pieces of information to log.
+ const int32_t srcWidth = getInt32(srcFormat, AMEDIAFORMAT_KEY_WIDTH);
+ const int32_t srcHeight = getInt32(srcFormat, AMEDIAFORMAT_KEY_HEIGHT);
+ const char* srcMime = getString(srcFormat, AMEDIAFORMAT_KEY_MIME);
+ const int32_t srcProfile = getInt32(srcFormat, AMEDIAFORMAT_KEY_PROFILE);
+ const int32_t srcLevel = getInt32(srcFormat, AMEDIAFORMAT_KEY_LEVEL);
+ const int32_t srcFrameRate = getInt32(srcFormat, AMEDIAFORMAT_KEY_FRAME_RATE);
+ const int32_t srcFrameCount = getInt32(srcFormat, AMEDIAFORMAT_KEY_FRAME_COUNT);
+ const bool srcIsHdr = AMediaFormatUtils::VideoIsHdr(srcFormat);
+
+ int32_t dstWidth = getInt32(dstFormat, AMEDIAFORMAT_KEY_WIDTH, srcWidth);
+ int32_t dstHeight = getInt32(dstFormat, AMEDIAFORMAT_KEY_HEIGHT, srcHeight);
+ const char* dstMime = dstFormat == nullptr
+ ? "passthrough"
+ : getString(dstFormat, AMEDIAFORMAT_KEY_MIME, srcMime);
+ const bool dstIsHdr = false; // Transcoder always request SDR output.
+
+ int64_t tmpDurationUs;
+ const int32_t srcDurationMs =
+ AMediaFormat_getInt64(srcFormat, AMEDIAFORMAT_KEY_DURATION, &tmpDurationUs)
+ ? static_cast<int32_t>(tmpDurationUs / 1000)
+ : -1;
+
+ int32_t transcodeFrameRate = -1;
+ if (status == 0 && srcFrameCount > 0 && duration.count() > 0) {
+ std::chrono::duration<double> seconds{duration};
+ transcodeFrameRate = static_cast<int32_t>(
+ std::round(static_cast<double>(srcFrameCount) / seconds.count()));
+ }
+
+ // Write the atom.
+ mSessionEndedAtomWriter(android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED,
+ static_cast<int>(reason), callingUid, status, transcodeFrameRate,
+ srcWidth, srcHeight, srcMime, srcProfile, srcLevel, srcFrameRate,
+ srcDurationMs, srcIsHdr, dstWidth, dstHeight, dstMime, dstIsHdr);
+}
+
+bool TranscodingLogger::shouldLogAtom(const std::chrono::steady_clock::time_point& now,
+ int status) {
+ std::scoped_lock lock{mLock};
+ static const std::chrono::hours oneDay(24);
+
+ // Remove events older than one day.
+ while (mLastLoggedAtoms.size() > 0 && (now - mLastLoggedAtoms.front().first) >= oneDay) {
+ if (mLastLoggedAtoms.front().second == AMEDIA_OK) {
+ --mSuccessfulCount;
+ }
+ mLastLoggedAtoms.pop();
+ }
+
+ // Don't log if maximum number of events is reached.
+ if (mLastLoggedAtoms.size() >= kMaxAtomsPerDay) {
+ return false;
+ }
+
+ // Don't log if the event is successful and the maximum number of successful events is reached.
+ if (status == AMEDIA_OK && mSuccessfulCount >= kMaxSuccessfulAtomsPerDay) {
+ return false;
+ }
+
+ // Record the event.
+ if (status == AMEDIA_OK) {
+ ++mSuccessfulCount;
+ }
+ mLastLoggedAtoms.emplace(now, status);
+ return true;
+}
+
+void TranscodingLogger::setSessionEndedAtomWriter(const SessionEndedAtomWriter& writer) {
+ mSessionEndedAtomWriter = writer;
+}
+
+} // namespace android
diff --git a/media/libmediatranscoding/TranscodingResourcePolicy.cpp b/media/libmediatranscoding/TranscodingResourcePolicy.cpp
new file mode 100644
index 0000000..af53f64
--- /dev/null
+++ b/media/libmediatranscoding/TranscodingResourcePolicy.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "TranscodingResourcePolicy"
+
+#include <aidl/android/media/BnResourceObserver.h>
+#include <aidl/android/media/IResourceObserverService.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <media/TranscodingResourcePolicy.h>
+#include <utils/Log.h>
+
+namespace android {
+
+using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::media::BnResourceObserver;
+using ::aidl::android::media::IResourceObserverService;
+using ::aidl::android::media::MediaObservableEvent;
+using ::aidl::android::media::MediaObservableFilter;
+using ::aidl::android::media::MediaObservableParcel;
+using ::aidl::android::media::MediaObservableType;
+
+static std::string toString(const MediaObservableParcel& observable) {
+ return "{" + ::aidl::android::media::toString(observable.type) + ", " +
+ std::to_string(observable.value) + "}";
+}
+
+struct TranscodingResourcePolicy::ResourceObserver : public BnResourceObserver {
+ explicit ResourceObserver(TranscodingResourcePolicy* owner) : mOwner(owner) {}
+
+ // IResourceObserver
+ ::ndk::ScopedAStatus onStatusChanged(
+ MediaObservableEvent event, int32_t uid, int32_t pid,
+ const std::vector<MediaObservableParcel>& observables) override {
+ ALOGD("%s: %s, uid %d, pid %d, %s", __FUNCTION__,
+ ::aidl::android::media::toString(event).c_str(), uid, pid,
+ toString(observables[0]).c_str());
+
+ // Only report kIdle event.
+ if (((uint64_t)event & (uint64_t)MediaObservableEvent::kIdle) != 0) {
+ for (auto& observable : observables) {
+ if (observable.type == MediaObservableType::kVideoSecureCodec ||
+ observable.type == MediaObservableType::kVideoNonSecureCodec) {
+ mOwner->onResourceAvailable(pid);
+ break;
+ }
+ }
+ }
+ return ::ndk::ScopedAStatus::ok();
+ }
+
+ TranscodingResourcePolicy* mOwner;
+};
+
+// static
+void TranscodingResourcePolicy::BinderDiedCallback(void* cookie) {
+ TranscodingResourcePolicy* owner = reinterpret_cast<TranscodingResourcePolicy*>(cookie);
+ if (owner != nullptr) {
+ owner->unregisterSelf();
+ }
+ // TODO(chz): retry to connecting to IResourceObserverService after failure.
+ // Also need to have back-up logic if IResourceObserverService is offline for
+ // Prolonged period of time. A possible alternative could be, during period where
+ // IResourceObserverService is not available, trigger onResourceAvailable() everytime
+ // when top uid changes (in hope that'll free up some codec instances that we could
+ // reclaim).
+}
+
+TranscodingResourcePolicy::TranscodingResourcePolicy()
+ : mRegistered(false),
+ mResourceLostPid(-1),
+ mDeathRecipient(AIBinder_DeathRecipient_new(BinderDiedCallback)) {
+ registerSelf();
+}
+
+TranscodingResourcePolicy::~TranscodingResourcePolicy() {
+ unregisterSelf();
+}
+
+void TranscodingResourcePolicy::registerSelf() {
+ ALOGI("TranscodingResourcePolicy: registerSelf");
+
+ ::ndk::SpAIBinder binder(AServiceManager_getService("media.resource_observer"));
+
+ std::scoped_lock lock{mRegisteredLock};
+
+ if (mRegistered) {
+ return;
+ }
+
+ // TODO(chz): retry to connecting to IResourceObserverService after failure.
+ mService = IResourceObserverService::fromBinder(binder);
+ if (mService == nullptr) {
+ ALOGE("Failed to get IResourceObserverService");
+ return;
+ }
+
+ // Only register filters for codec resource available.
+ mObserver = ::ndk::SharedRefBase::make<ResourceObserver>(this);
+ std::vector<MediaObservableFilter> filters = {
+ {MediaObservableType::kVideoSecureCodec, MediaObservableEvent::kIdle},
+ {MediaObservableType::kVideoNonSecureCodec, MediaObservableEvent::kIdle}};
+
+ Status status = mService->registerObserver(mObserver, filters);
+ if (!status.isOk()) {
+ ALOGE("failed to register: error %d", status.getServiceSpecificError());
+ mService = nullptr;
+ mObserver = nullptr;
+ return;
+ }
+
+ AIBinder_linkToDeath(binder.get(), mDeathRecipient.get(), reinterpret_cast<void*>(this));
+
+ ALOGD("@@@ registered observer");
+ mRegistered = true;
+}
+
+void TranscodingResourcePolicy::unregisterSelf() {
+ ALOGI("TranscodingResourcePolicy: unregisterSelf");
+
+ std::scoped_lock lock{mRegisteredLock};
+
+ if (!mRegistered) {
+ return;
+ }
+
+ ::ndk::SpAIBinder binder = mService->asBinder();
+ if (binder.get() != nullptr) {
+ Status status = mService->unregisterObserver(mObserver);
+ AIBinder_unlinkToDeath(binder.get(), mDeathRecipient.get(), reinterpret_cast<void*>(this));
+ }
+
+ mService = nullptr;
+ mObserver = nullptr;
+ mRegistered = false;
+}
+
+void TranscodingResourcePolicy::setCallback(
+ const std::shared_ptr<ResourcePolicyCallbackInterface>& cb) {
+ std::scoped_lock lock{mCallbackLock};
+ mResourcePolicyCallback = cb;
+}
+
+void TranscodingResourcePolicy::setPidResourceLost(pid_t pid) {
+ std::scoped_lock lock{mCallbackLock};
+ mResourceLostPid = pid;
+}
+
+void TranscodingResourcePolicy::onResourceAvailable(pid_t pid) {
+ std::shared_ptr<ResourcePolicyCallbackInterface> cb;
+ {
+ std::scoped_lock lock{mCallbackLock};
+ // Only callback if codec resource is released from other processes.
+ if (mResourceLostPid != -1 && mResourceLostPid != pid) {
+ cb = mResourcePolicyCallback.lock();
+ mResourceLostPid = -1;
+ }
+ }
+
+ if (cb != nullptr) {
+ cb->onResourceAvailable();
+ }
+}
+} // namespace android
diff --git a/media/libmediatranscoding/TranscodingSessionController.cpp b/media/libmediatranscoding/TranscodingSessionController.cpp
new file mode 100644
index 0000000..ea3e518
--- /dev/null
+++ b/media/libmediatranscoding/TranscodingSessionController.cpp
@@ -0,0 +1,1190 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "TranscodingSessionController"
+
+#define VALIDATE_STATE 1
+
+#include <android/permission_manager.h>
+#include <inttypes.h>
+#include <media/TranscodingSessionController.h>
+#include <media/TranscodingUidPolicy.h>
+#include <utils/AndroidThreads.h>
+#include <utils/Log.h>
+
+#include <thread>
+#include <utility>
+
+namespace android {
+
+static_assert((SessionIdType)-1 < 0, "SessionIdType should be signed");
+
+constexpr static uid_t OFFLINE_UID = -1;
+constexpr static size_t kSessionHistoryMax = 100;
+
+//static
+String8 TranscodingSessionController::sessionToString(const SessionKeyType& sessionKey) {
+ return String8::format("{client:%lld, session:%d}", (long long)sessionKey.first,
+ sessionKey.second);
+}
+
+//static
+const char* TranscodingSessionController::sessionStateToString(const Session::State sessionState) {
+ switch (sessionState) {
+ case Session::State::NOT_STARTED:
+ return "NOT_STARTED";
+ case Session::State::RUNNING:
+ return "RUNNING";
+ case Session::State::PAUSED:
+ return "PAUSED";
+ case Session::State::FINISHED:
+ return "FINISHED";
+ case Session::State::CANCELED:
+ return "CANCELED";
+ case Session::State::ERROR:
+ return "ERROR";
+ default:
+ break;
+ }
+ return "(unknown)";
+}
+
+///////////////////////////////////////////////////////////////////////////////
+struct TranscodingSessionController::Watchdog {
+ Watchdog(TranscodingSessionController* owner, int64_t timeoutUs);
+ ~Watchdog();
+
+ // Starts monitoring the session.
+ void start(const SessionKeyType& key);
+ // Stops monitoring the session.
+ void stop();
+ // Signals that the session is still alive. Must be sent at least every mTimeoutUs.
+ // (Timeout will happen if no ping in mTimeoutUs since the last ping.)
+ void keepAlive();
+
+private:
+ void threadLoop();
+ void updateTimer_l();
+
+ TranscodingSessionController* mOwner;
+ const int64_t mTimeoutUs;
+ mutable std::mutex mLock;
+ std::condition_variable mCondition GUARDED_BY(mLock);
+ // Whether watchdog is monitoring a session for timeout.
+ bool mActive GUARDED_BY(mLock);
+ // Whether watchdog is aborted and the monitoring thread should exit.
+ bool mAbort GUARDED_BY(mLock);
+ // When watchdog is active, the next timeout time point.
+ std::chrono::steady_clock::time_point mNextTimeoutTime GUARDED_BY(mLock);
+ // When watchdog is active, the session being watched.
+ SessionKeyType mSessionToWatch GUARDED_BY(mLock);
+ std::thread mThread;
+};
+
+TranscodingSessionController::Watchdog::Watchdog(TranscodingSessionController* owner,
+ int64_t timeoutUs)
+ : mOwner(owner),
+ mTimeoutUs(timeoutUs),
+ mActive(false),
+ mAbort(false),
+ mThread(&Watchdog::threadLoop, this) {
+ ALOGV("Watchdog CTOR: %p", this);
+}
+
+TranscodingSessionController::Watchdog::~Watchdog() {
+ ALOGV("Watchdog DTOR: %p", this);
+
+ {
+ // Exit the looper thread.
+ std::scoped_lock lock{mLock};
+
+ mAbort = true;
+ mCondition.notify_one();
+ }
+
+ mThread.join();
+ ALOGV("Watchdog DTOR: %p, done.", this);
+}
+
+void TranscodingSessionController::Watchdog::start(const SessionKeyType& key) {
+ std::scoped_lock lock{mLock};
+
+ if (!mActive) {
+ ALOGI("Watchdog start: %s", sessionToString(key).c_str());
+
+ mActive = true;
+ mSessionToWatch = key;
+ updateTimer_l();
+ mCondition.notify_one();
+ }
+}
+
+void TranscodingSessionController::Watchdog::stop() {
+ std::scoped_lock lock{mLock};
+
+ if (mActive) {
+ ALOGI("Watchdog stop: %s", sessionToString(mSessionToWatch).c_str());
+
+ mActive = false;
+ mCondition.notify_one();
+ }
+}
+
+void TranscodingSessionController::Watchdog::keepAlive() {
+ std::scoped_lock lock{mLock};
+
+ if (mActive) {
+ ALOGI("Watchdog keepAlive: %s", sessionToString(mSessionToWatch).c_str());
+
+ updateTimer_l();
+ mCondition.notify_one();
+ }
+}
+
+// updateTimer_l() is only called with lock held.
+void TranscodingSessionController::Watchdog::updateTimer_l() NO_THREAD_SAFETY_ANALYSIS {
+ std::chrono::microseconds timeout(mTimeoutUs);
+ mNextTimeoutTime = std::chrono::steady_clock::now() + timeout;
+}
+
+// Unfortunately std::unique_lock is incompatible with -Wthread-safety.
+void TranscodingSessionController::Watchdog::threadLoop() NO_THREAD_SAFETY_ANALYSIS {
+ androidSetThreadPriority(0 /*tid (0 = current) */, ANDROID_PRIORITY_BACKGROUND);
+ std::unique_lock<std::mutex> lock{mLock};
+
+ while (!mAbort) {
+ if (!mActive) {
+ mCondition.wait(lock);
+ continue;
+ }
+ // Watchdog active, wait till next timeout time.
+ if (mCondition.wait_until(lock, mNextTimeoutTime) == std::cv_status::timeout) {
+ // If timeout happens, report timeout and deactivate watchdog.
+ mActive = false;
+ // Make a copy of session key, as once we unlock, it could be unprotected.
+ SessionKeyType sessionKey = mSessionToWatch;
+
+ ALOGE("Watchdog timeout: %s", sessionToString(sessionKey).c_str());
+
+ lock.unlock();
+ mOwner->onError(sessionKey.first, sessionKey.second,
+ TranscodingErrorCode::kWatchdogTimeout);
+ lock.lock();
+ }
+ }
+}
+///////////////////////////////////////////////////////////////////////////////
+struct TranscodingSessionController::Pacer {
+ Pacer(const ControllerConfig& config)
+ : mBurstThresholdMs(config.pacerBurstThresholdMs),
+ mBurstCountQuota(config.pacerBurstCountQuota),
+ mBurstTimeQuotaSec(config.pacerBurstTimeQuotaSeconds) {}
+
+ ~Pacer() = default;
+
+ bool onSessionStarted(uid_t uid, uid_t callingUid);
+ void onSessionCompleted(uid_t uid, std::chrono::microseconds runningTime);
+ void onSessionCancelled(uid_t uid);
+
+private:
+ // Threshold of time between finish/start below which a back-to-back start is counted.
+ int32_t mBurstThresholdMs;
+ // Maximum allowed back-to-back start count.
+ int32_t mBurstCountQuota;
+ // Maximum allowed back-to-back running time.
+ int32_t mBurstTimeQuotaSec;
+
+ struct UidHistoryEntry {
+ bool sessionActive = false;
+ int32_t burstCount = 0;
+ std::chrono::steady_clock::duration burstDuration{0};
+ std::chrono::steady_clock::time_point lastCompletedTime;
+ };
+ std::map<uid_t, UidHistoryEntry> mUidHistoryMap;
+ std::unordered_set<uid_t> mMtpUids;
+ std::unordered_set<uid_t> mNonMtpUids;
+
+ bool isSubjectToQuota(uid_t uid, uid_t callingUid);
+};
+
+bool TranscodingSessionController::Pacer::isSubjectToQuota(uid_t uid, uid_t callingUid) {
+ // Submitting with self uid is not limited (which can only happen if it's used as an
+ // app-facing API). MediaProvider usage always submit on behalf of other uids.
+ if (uid == callingUid) {
+ return false;
+ }
+
+ if (mMtpUids.find(uid) != mMtpUids.end()) {
+ return false;
+ }
+
+ if (mNonMtpUids.find(uid) != mNonMtpUids.end()) {
+ return true;
+ }
+
+ // We don't have MTP permission info about this uid yet, check permission and save the result.
+ int32_t result;
+ if (__builtin_available(android __TRANSCODING_MIN_API__, *)) {
+ if (APermissionManager_checkPermission("android.permission.ACCESS_MTP", -1 /*pid*/, uid,
+ &result) == PERMISSION_MANAGER_STATUS_OK &&
+ result == PERMISSION_MANAGER_PERMISSION_GRANTED) {
+ mMtpUids.insert(uid);
+ return false;
+ }
+ }
+
+ mNonMtpUids.insert(uid);
+ return true;
+}
+
+bool TranscodingSessionController::Pacer::onSessionStarted(uid_t uid, uid_t callingUid) {
+ if (!isSubjectToQuota(uid, callingUid)) {
+ ALOGI("Pacer::onSessionStarted: uid %d (caling uid: %d): not subject to quota", uid,
+ callingUid);
+ return true;
+ }
+
+ // If uid doesn't exist, only insert the entry and mark session active. Skip quota checking.
+ if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
+ mUidHistoryMap.emplace(uid, UidHistoryEntry{});
+ mUidHistoryMap[uid].sessionActive = true;
+ ALOGV("Pacer::onSessionStarted: uid %d: new", uid);
+ return true;
+ }
+
+ // TODO: if Thermal throttling or resoure lost happened to occurr between this start
+ // and the previous completion, we should deduct the paused time from the elapsed time.
+ // (Individual session's pause time, on the other hand, doesn't need to be deducted
+ // because it doesn't affect the gap between last completion and the start.
+ auto timeSinceLastComplete =
+ std::chrono::steady_clock::now() - mUidHistoryMap[uid].lastCompletedTime;
+ if (mUidHistoryMap[uid].burstCount >= mBurstCountQuota &&
+ mUidHistoryMap[uid].burstDuration >= std::chrono::seconds(mBurstTimeQuotaSec)) {
+ ALOGW("Pacer::onSessionStarted: uid %d: over quota, burst count %d, time %lldms", uid,
+ mUidHistoryMap[uid].burstCount,
+ (long long)mUidHistoryMap[uid].burstDuration.count() / 1000000);
+ return false;
+ }
+
+ // If not over quota, allow the session, and reset as long as this is not too close
+ // to previous completion.
+ if (timeSinceLastComplete > std::chrono::milliseconds(mBurstThresholdMs)) {
+ ALOGV("Pacer::onSessionStarted: uid %d: reset quota", uid);
+ mUidHistoryMap[uid].burstCount = 0;
+ mUidHistoryMap[uid].burstDuration = std::chrono::milliseconds(0);
+ } else {
+ ALOGV("Pacer::onSessionStarted: uid %d: burst count %d, time %lldms", uid,
+ mUidHistoryMap[uid].burstCount,
+ (long long)mUidHistoryMap[uid].burstDuration.count() / 1000000);
+ }
+
+ mUidHistoryMap[uid].sessionActive = true;
+ return true;
+}
+
+void TranscodingSessionController::Pacer::onSessionCompleted(
+ uid_t uid, std::chrono::microseconds runningTime) {
+ // Skip quota update if this uid missed the start. (Could happen if the uid is added via
+ // addClientUid() after the session start.)
+ if (mUidHistoryMap.find(uid) == mUidHistoryMap.end() || !mUidHistoryMap[uid].sessionActive) {
+ ALOGV("Pacer::onSessionCompleted: uid %d: not started", uid);
+ return;
+ }
+ ALOGV("Pacer::onSessionCompleted: uid %d: runningTime %lld", uid, runningTime.count() / 1000);
+ mUidHistoryMap[uid].sessionActive = false;
+ mUidHistoryMap[uid].burstCount++;
+ mUidHistoryMap[uid].burstDuration += runningTime;
+ mUidHistoryMap[uid].lastCompletedTime = std::chrono::steady_clock::now();
+}
+
+void TranscodingSessionController::Pacer::onSessionCancelled(uid_t uid) {
+ if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
+ ALOGV("Pacer::onSessionCancelled: uid %d: not present", uid);
+ return;
+ }
+ // This is only called if a uid is removed from a session (due to it being killed
+ // or the original submitting client was gone but session was kept for offline use).
+ // Since the uid is going to miss the onSessionCompleted(), we can't track this
+ // session, and have to check back at next onSessionStarted().
+ mUidHistoryMap[uid].sessionActive = false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+TranscodingSessionController::TranscodingSessionController(
+ const TranscoderFactoryType& transcoderFactory,
+ const std::shared_ptr<UidPolicyInterface>& uidPolicy,
+ const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy,
+ const std::shared_ptr<ThermalPolicyInterface>& thermalPolicy,
+ const ControllerConfig* config)
+ : mTranscoderFactory(transcoderFactory),
+ mUidPolicy(uidPolicy),
+ mResourcePolicy(resourcePolicy),
+ mThermalPolicy(thermalPolicy),
+ mCurrentSession(nullptr),
+ mResourceLost(false) {
+ // Only push empty offline queue initially. Realtime queues are added when requests come in.
+ mUidSortedList.push_back(OFFLINE_UID);
+ mOfflineUidIterator = mUidSortedList.begin();
+ mSessionQueues.emplace(OFFLINE_UID, SessionQueueType());
+ mUidPackageNames[OFFLINE_UID] = "(offline)";
+ mThermalThrottling = thermalPolicy->getThrottlingStatus();
+ if (config != nullptr) {
+ mConfig = *config;
+ }
+ mPacer.reset(new Pacer(mConfig));
+ ALOGD("@@@ watchdog %lld, burst count %d, burst time %d, burst threshold %d",
+ (long long)mConfig.watchdogTimeoutUs, mConfig.pacerBurstCountQuota,
+ mConfig.pacerBurstTimeQuotaSeconds, mConfig.pacerBurstThresholdMs);
+}
+
+TranscodingSessionController::~TranscodingSessionController() {}
+
+void TranscodingSessionController::dumpSession_l(const Session& session, String8& result,
+ bool closedSession) {
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ const TranscodingRequestParcel& request = session.request;
+ snprintf(buffer, SIZE, " Session: %s, %s, %d%%\n", sessionToString(session.key).c_str(),
+ sessionStateToString(session.getState()), session.lastProgress);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " pkg: %s\n", request.clientPackageName.c_str());
+ result.append(buffer);
+ snprintf(buffer, SIZE, " src: %s\n", request.sourceFilePath.c_str());
+ result.append(buffer);
+ snprintf(buffer, SIZE, " dst: %s\n", request.destinationFilePath.c_str());
+ result.append(buffer);
+
+ if (closedSession) {
+ snprintf(buffer, SIZE,
+ " waiting: %.1fs, running: %.1fs, paused: %.1fs, paused count: %d\n",
+ session.waitingTime.count() / 1000000.0f, session.runningTime.count() / 1000000.0f,
+ session.pausedTime.count() / 1000000.0f, session.pauseCount);
+ result.append(buffer);
+ }
+}
+
+void TranscodingSessionController::dumpAllSessions(int fd, const Vector<String16>& args __unused) {
+ String8 result;
+
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ std::scoped_lock lock{mLock};
+
+ snprintf(buffer, SIZE, "\n========== Dumping live sessions queues =========\n");
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Total num of Sessions: %zu\n", mSessionMap.size());
+ result.append(buffer);
+
+ std::vector<int32_t> uids(mUidSortedList.begin(), mUidSortedList.end());
+
+ for (int32_t i = 0; i < uids.size(); i++) {
+ const uid_t uid = uids[i];
+
+ if (mSessionQueues[uid].empty()) {
+ continue;
+ }
+ snprintf(buffer, SIZE, " uid: %d, pkg: %s\n", uid,
+ mUidPackageNames.count(uid) > 0 ? mUidPackageNames[uid].c_str() : "(unknown)");
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Num of sessions: %zu\n", mSessionQueues[uid].size());
+ result.append(buffer);
+ for (auto& sessionKey : mSessionQueues[uid]) {
+ auto sessionIt = mSessionMap.find(sessionKey);
+ if (sessionIt == mSessionMap.end()) {
+ snprintf(buffer, SIZE, "Failed to look up Session %s \n",
+ sessionToString(sessionKey).c_str());
+ result.append(buffer);
+ continue;
+ }
+ dumpSession_l(sessionIt->second, result);
+ }
+ }
+
+ snprintf(buffer, SIZE, "\n========== Dumping past sessions =========\n");
+ result.append(buffer);
+ for (auto& session : mSessionHistory) {
+ dumpSession_l(session, result, true /*closedSession*/);
+ }
+
+ write(fd, result.string(), result.size());
+}
+
+/*
+ * Returns nullptr if there is no session, or we're paused globally (due to resource lost,
+ * thermal throttling, etc.). Otherwise, return the session that should be run next.
+ */
+TranscodingSessionController::Session* TranscodingSessionController::getTopSession_l() {
+ if (mSessionMap.empty()) {
+ return nullptr;
+ }
+
+ // Return nullptr if we're paused globally due to resource lost or thermal throttling.
+ if (((mResourcePolicy != nullptr && mResourceLost) ||
+ (mThermalPolicy != nullptr && mThermalThrottling))) {
+ return nullptr;
+ }
+
+ uid_t topUid = *mUidSortedList.begin();
+ // If the current session is running, and it's in the topUid's queue, let it continue
+ // to run even if it's not the earliest in that uid's queue.
+ // For example, uid(B) is added to a session while it's pending in uid(A)'s queue, then
+ // B is brought to front which caused the session to run, then user switches back to A.
+ if (mCurrentSession != nullptr && mCurrentSession->getState() == Session::RUNNING &&
+ mCurrentSession->allClientUids.count(topUid) > 0) {
+ return mCurrentSession;
+ }
+ SessionKeyType topSessionKey = *mSessionQueues[topUid].begin();
+ return &mSessionMap[topSessionKey];
+}
+
+void TranscodingSessionController::setSessionState_l(Session* session, Session::State state) {
+ bool wasRunning = (session->getState() == Session::RUNNING);
+ session->setState(state);
+ bool isRunning = (session->getState() == Session::RUNNING);
+
+ if (wasRunning == isRunning) {
+ return;
+ }
+
+ // Currently we only have 1 running session, and we always put the previous
+ // session in non-running state before we run the new session, so it's okay
+ // to start/stop the watchdog here. If this assumption changes, we need to
+ // track the number of running sessions and start/stop watchdog based on that.
+ if (isRunning) {
+ mWatchdog->start(session->key);
+ } else {
+ mWatchdog->stop();
+ }
+}
+
+void TranscodingSessionController::Session::setState(Session::State newState) {
+ if (state == newState) {
+ return;
+ }
+ auto nowTime = std::chrono::steady_clock::now();
+ if (state != INVALID) {
+ std::chrono::microseconds elapsedTime =
+ std::chrono::duration_cast<std::chrono::microseconds>(nowTime - stateEnterTime);
+ switch (state) {
+ case PAUSED:
+ pausedTime = pausedTime + elapsedTime;
+ break;
+ case RUNNING:
+ runningTime = runningTime + elapsedTime;
+ break;
+ case NOT_STARTED:
+ waitingTime = waitingTime + elapsedTime;
+ break;
+ default:
+ break;
+ }
+ }
+ if (newState == PAUSED) {
+ pauseCount++;
+ }
+ stateEnterTime = nowTime;
+ state = newState;
+}
+
+void TranscodingSessionController::updateCurrentSession_l() {
+ Session* curSession = mCurrentSession;
+ Session* topSession = nullptr;
+
+ // Delayed init of transcoder and watchdog.
+ if (mTranscoder == nullptr) {
+ mTranscoder = mTranscoderFactory(shared_from_this());
+ mWatchdog = std::make_shared<Watchdog>(this, mConfig.watchdogTimeoutUs);
+ }
+
+ // If we found a different top session, or the top session's running state is not
+ // correct. Take some actions to ensure it's correct.
+ while ((topSession = getTopSession_l()) != curSession ||
+ (topSession != nullptr && !topSession->isRunning())) {
+ ALOGV("updateCurrentSession_l: topSession is %s, curSession is %s",
+ topSession == nullptr ? "null" : sessionToString(topSession->key).c_str(),
+ curSession == nullptr ? "null" : sessionToString(curSession->key).c_str());
+
+ // If current session is running, pause it first. Note this is needed for either
+ // cases: 1) Top session is changing to another session, or 2) Top session is
+ // changing to null (which means we should be globally paused).
+ if (curSession != nullptr && curSession->getState() == Session::RUNNING) {
+ mTranscoder->pause(curSession->key.first, curSession->key.second);
+ setSessionState_l(curSession, Session::PAUSED);
+ }
+
+ if (topSession == nullptr) {
+ // Nothing more to run (either no session or globally paused).
+ break;
+ }
+
+ // Otherwise, ensure topSession is running.
+ if (topSession->getState() == Session::NOT_STARTED) {
+ // Check if at least one client has quota to start the session.
+ bool keepForClient = false;
+ for (uid_t uid : topSession->allClientUids) {
+ if (mPacer->onSessionStarted(uid, topSession->callingUid)) {
+ keepForClient = true;
+ // DO NOT break here, because book-keeping still needs to happen
+ // for the other uids.
+ }
+ }
+ if (!keepForClient) {
+ // Unfortunately all uids requesting this session are out of quota.
+ // Drop this session and try the next one.
+ {
+ auto clientCallback = mSessionMap[topSession->key].callback.lock();
+ if (clientCallback != nullptr) {
+ clientCallback->onTranscodingFailed(
+ topSession->key.second, TranscodingErrorCode::kDroppedByService);
+ }
+ }
+ removeSession_l(topSession->key, Session::DROPPED_BY_PACER);
+ continue;
+ }
+ mTranscoder->start(topSession->key.first, topSession->key.second, topSession->request,
+ topSession->callingUid, topSession->callback.lock());
+ setSessionState_l(topSession, Session::RUNNING);
+ } else if (topSession->getState() == Session::PAUSED) {
+ mTranscoder->resume(topSession->key.first, topSession->key.second, topSession->request,
+ topSession->callingUid, topSession->callback.lock());
+ setSessionState_l(topSession, Session::RUNNING);
+ }
+ break;
+ }
+ mCurrentSession = topSession;
+}
+
+void TranscodingSessionController::addUidToSession_l(uid_t clientUid,
+ const SessionKeyType& sessionKey) {
+ // If it's an offline session, the queue was already added in constructor.
+ // If it's a real-time sessions, check if a queue is already present for the uid,
+ // and add a new queue if needed.
+ if (clientUid != OFFLINE_UID) {
+ if (mSessionQueues.count(clientUid) == 0) {
+ mUidPolicy->registerMonitorUid(clientUid);
+ if (mUidPolicy->isUidOnTop(clientUid)) {
+ mUidSortedList.push_front(clientUid);
+ } else {
+ // Shouldn't be submitting real-time requests from non-top app,
+ // put it in front of the offline queue.
+ mUidSortedList.insert(mOfflineUidIterator, clientUid);
+ }
+ } else if (clientUid != *mUidSortedList.begin()) {
+ if (mUidPolicy->isUidOnTop(clientUid)) {
+ mUidSortedList.remove(clientUid);
+ mUidSortedList.push_front(clientUid);
+ }
+ }
+ }
+ // Append this session to the uid's queue.
+ mSessionQueues[clientUid].push_back(sessionKey);
+}
+
+void TranscodingSessionController::removeSession_l(
+ const SessionKeyType& sessionKey, Session::State finalState,
+ const std::shared_ptr<std::function<bool(uid_t uid)>>& keepUid) {
+ ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
+
+ if (mSessionMap.count(sessionKey) == 0) {
+ ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
+ return;
+ }
+
+ // Remove session from uid's queue.
+ bool uidQueueRemoved = false;
+ std::unordered_set<uid_t> remainingUids;
+ for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
+ if (keepUid != nullptr) {
+ if ((*keepUid)(uid)) {
+ remainingUids.insert(uid);
+ continue;
+ }
+ // If we have uids to keep, the session is not going to any final
+ // state we can't use onSessionCompleted as the running time will
+ // not be valid. Only notify pacer to stop tracking this session.
+ mPacer->onSessionCancelled(uid);
+ }
+ SessionQueueType& sessionQueue = mSessionQueues[uid];
+ auto it = std::find(sessionQueue.begin(), sessionQueue.end(), sessionKey);
+ if (it == sessionQueue.end()) {
+ ALOGW("couldn't find session %s in queue for uid %d",
+ sessionToString(sessionKey).c_str(), uid);
+ continue;
+ }
+ sessionQueue.erase(it);
+
+ // If this is the last session in a real-time queue, remove this uid's queue.
+ if (uid != OFFLINE_UID && sessionQueue.empty()) {
+ mUidSortedList.remove(uid);
+ mSessionQueues.erase(uid);
+ mUidPolicy->unregisterMonitorUid(uid);
+
+ uidQueueRemoved = true;
+ }
+ }
+
+ if (uidQueueRemoved) {
+ std::unordered_set<uid_t> topUids = mUidPolicy->getTopUids();
+ moveUidsToTop_l(topUids, false /*preserveTopUid*/);
+ }
+
+ if (keepUid != nullptr) {
+ mSessionMap[sessionKey].allClientUids = remainingUids;
+ return;
+ }
+
+ // Clear current session.
+ if (mCurrentSession == &mSessionMap[sessionKey]) {
+ mCurrentSession = nullptr;
+ }
+
+ setSessionState_l(&mSessionMap[sessionKey], finalState);
+
+ // We can use onSessionCompleted() even for CANCELLED, because runningTime is
+ // now updated by setSessionState_l().
+ for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
+ mPacer->onSessionCompleted(uid, mSessionMap[sessionKey].runningTime);
+ }
+
+ mSessionHistory.push_back(mSessionMap[sessionKey]);
+ if (mSessionHistory.size() > kSessionHistoryMax) {
+ mSessionHistory.erase(mSessionHistory.begin());
+ }
+
+ // Remove session from session map.
+ mSessionMap.erase(sessionKey);
+}
+
+/**
+ * Moves the set of uids to the front of mUidSortedList (which is used to pick
+ * the next session to run).
+ *
+ * This is called when 1) we received a onTopUidsChanged() callback from UidPolicy,
+ * or 2) we removed the session queue for a uid because it becomes empty.
+ *
+ * In case of 1), if there are multiple uids in the set, and the current front
+ * uid in mUidSortedList is still in the set, we try to keep that uid at front
+ * so that current session run is not interrupted. (This is not a concern for case 2)
+ * because the queue for a uid was just removed entirely.)
+ */
+void TranscodingSessionController::moveUidsToTop_l(const std::unordered_set<uid_t>& uids,
+ bool preserveTopUid) {
+ // If uid set is empty, nothing to do. Do not change the queue status.
+ if (uids.empty()) {
+ return;
+ }
+
+ // Save the current top uid.
+ uid_t curTopUid = *mUidSortedList.begin();
+ bool pushCurTopToFront = false;
+ int32_t numUidsMoved = 0;
+
+ // Go through the sorted uid list once, and move the ones in top set to front.
+ for (auto it = mUidSortedList.begin(); it != mUidSortedList.end();) {
+ uid_t uid = *it;
+
+ if (uid != OFFLINE_UID && uids.count(uid) > 0) {
+ it = mUidSortedList.erase(it);
+
+ // If this is the top we're preserving, don't push it here, push
+ // it after the for-loop.
+ if (uid == curTopUid && preserveTopUid) {
+ pushCurTopToFront = true;
+ } else {
+ mUidSortedList.push_front(uid);
+ }
+
+ // If we found all uids in the set, break out.
+ if (++numUidsMoved == uids.size()) {
+ break;
+ }
+ } else {
+ ++it;
+ }
+ }
+
+ if (pushCurTopToFront) {
+ mUidSortedList.push_front(curTopUid);
+ }
+}
+
+bool TranscodingSessionController::submit(
+ ClientIdType clientId, SessionIdType sessionId, uid_t callingUid, uid_t clientUid,
+ const TranscodingRequestParcel& request,
+ const std::weak_ptr<ITranscodingClientCallback>& callback) {
+ SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+
+ ALOGV("%s: session %s, uid %d, prioirty %d", __FUNCTION__, sessionToString(sessionKey).c_str(),
+ clientUid, (int32_t)request.priority);
+
+ std::scoped_lock lock{mLock};
+
+ if (mSessionMap.count(sessionKey) > 0) {
+ ALOGE("session %s already exists", sessionToString(sessionKey).c_str());
+ return false;
+ }
+
+ // Add the uid package name to the store of package names we already know.
+ if (mUidPackageNames.count(clientUid) == 0) {
+ mUidPackageNames.emplace(clientUid, request.clientPackageName);
+ }
+
+ // TODO(chz): only support offline vs real-time for now. All kUnspecified sessions
+ // go to offline queue.
+ if (request.priority == TranscodingSessionPriority::kUnspecified) {
+ clientUid = OFFLINE_UID;
+ }
+
+ // Add session to session map.
+ mSessionMap[sessionKey].key = sessionKey;
+ mSessionMap[sessionKey].callingUid = callingUid;
+ mSessionMap[sessionKey].allClientUids.insert(clientUid);
+ mSessionMap[sessionKey].request = request;
+ mSessionMap[sessionKey].callback = callback;
+ setSessionState_l(&mSessionMap[sessionKey], Session::NOT_STARTED);
+
+ addUidToSession_l(clientUid, sessionKey);
+
+ updateCurrentSession_l();
+
+ validateState_l();
+ return true;
+}
+
+bool TranscodingSessionController::cancel(ClientIdType clientId, SessionIdType sessionId) {
+ SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+
+ ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());
+
+ std::list<SessionKeyType> sessionsToRemove, sessionsForOffline;
+
+ std::scoped_lock lock{mLock};
+
+ if (sessionId < 0) {
+ for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
+ if (it->first.first == clientId) {
+ // If there is offline request, only keep the offline client;
+ // otherwise remove the session.
+ if (it->second.allClientUids.count(OFFLINE_UID) > 0) {
+ sessionsForOffline.push_back(it->first);
+ } else {
+ sessionsToRemove.push_back(it->first);
+ }
+ }
+ }
+ } else {
+ if (mSessionMap.count(sessionKey) == 0) {
+ ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
+ return false;
+ }
+ sessionsToRemove.push_back(sessionKey);
+ }
+
+ for (auto it = sessionsToRemove.begin(); it != sessionsToRemove.end(); ++it) {
+ // If the session has ever been started, stop it now.
+ // Note that stop() is needed even if the session is currently paused. This instructs
+ // the transcoder to discard any states for the session, otherwise the states may
+ // never be discarded.
+ if (mSessionMap[*it].getState() != Session::NOT_STARTED) {
+ mTranscoder->stop(it->first, it->second);
+ }
+
+ // Remove the session.
+ removeSession_l(*it, Session::CANCELED);
+ }
+
+ auto keepUid = std::make_shared<std::function<bool(uid_t)>>(
+ [](uid_t uid) { return uid == OFFLINE_UID; });
+ for (auto it = sessionsForOffline.begin(); it != sessionsForOffline.end(); ++it) {
+ removeSession_l(*it, Session::CANCELED, keepUid);
+ }
+
+ // Start next session.
+ updateCurrentSession_l();
+
+ validateState_l();
+ return true;
+}
+
+bool TranscodingSessionController::addClientUid(ClientIdType clientId, SessionIdType sessionId,
+ uid_t clientUid) {
+ SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+
+ std::scoped_lock lock{mLock};
+
+ if (mSessionMap.count(sessionKey) == 0) {
+ ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
+ return false;
+ }
+
+ if (mSessionMap[sessionKey].allClientUids.count(clientUid) > 0) {
+ ALOGE("session %s already has uid %d", sessionToString(sessionKey).c_str(), clientUid);
+ return false;
+ }
+
+ mSessionMap[sessionKey].allClientUids.insert(clientUid);
+ addUidToSession_l(clientUid, sessionKey);
+
+ updateCurrentSession_l();
+
+ validateState_l();
+ return true;
+}
+
+bool TranscodingSessionController::getClientUids(ClientIdType clientId, SessionIdType sessionId,
+ std::vector<int32_t>* out_clientUids) {
+ SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+
+ std::scoped_lock lock{mLock};
+
+ if (mSessionMap.count(sessionKey) == 0) {
+ ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
+ return false;
+ }
+
+ out_clientUids->clear();
+ for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
+ if (uid != OFFLINE_UID) {
+ out_clientUids->push_back(uid);
+ }
+ }
+ return true;
+}
+
+bool TranscodingSessionController::getSession(ClientIdType clientId, SessionIdType sessionId,
+ TranscodingRequestParcel* request) {
+ SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+
+ std::scoped_lock lock{mLock};
+
+ if (mSessionMap.count(sessionKey) == 0) {
+ ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
+ return false;
+ }
+
+ *(TranscodingRequest*)request = mSessionMap[sessionKey].request;
+ return true;
+}
+
+void TranscodingSessionController::notifyClient(ClientIdType clientId, SessionIdType sessionId,
+ const char* reason,
+ std::function<void(const SessionKeyType&)> func) {
+ SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+
+ std::scoped_lock lock{mLock};
+
+ if (mSessionMap.count(sessionKey) == 0) {
+ ALOGW("%s: ignoring %s for session %s that doesn't exist", __FUNCTION__, reason,
+ sessionToString(sessionKey).c_str());
+ return;
+ }
+
+ // Only ignore if session was never started. In particular, propagate the status
+ // to client if the session is paused. Transcoder could have posted finish when
+ // we're pausing it, and the finish arrived after we changed current session.
+ if (mSessionMap[sessionKey].getState() == Session::NOT_STARTED) {
+ ALOGW("%s: ignoring %s for session %s that was never started", __FUNCTION__, reason,
+ sessionToString(sessionKey).c_str());
+ return;
+ }
+
+ ALOGV("%s: session %s %s", __FUNCTION__, sessionToString(sessionKey).c_str(), reason);
+ func(sessionKey);
+}
+
+void TranscodingSessionController::onStarted(ClientIdType clientId, SessionIdType sessionId) {
+ notifyClient(clientId, sessionId, "started", [=](const SessionKeyType& sessionKey) {
+ auto callback = mSessionMap[sessionKey].callback.lock();
+ if (callback != nullptr) {
+ callback->onTranscodingStarted(sessionId);
+ }
+ });
+}
+
+void TranscodingSessionController::onPaused(ClientIdType clientId, SessionIdType sessionId) {
+ notifyClient(clientId, sessionId, "paused", [=](const SessionKeyType& sessionKey) {
+ auto callback = mSessionMap[sessionKey].callback.lock();
+ if (callback != nullptr) {
+ callback->onTranscodingPaused(sessionId);
+ }
+ });
+}
+
+void TranscodingSessionController::onResumed(ClientIdType clientId, SessionIdType sessionId) {
+ notifyClient(clientId, sessionId, "resumed", [=](const SessionKeyType& sessionKey) {
+ auto callback = mSessionMap[sessionKey].callback.lock();
+ if (callback != nullptr) {
+ callback->onTranscodingResumed(sessionId);
+ }
+ });
+}
+
+void TranscodingSessionController::onFinish(ClientIdType clientId, SessionIdType sessionId) {
+ notifyClient(clientId, sessionId, "finish", [=](const SessionKeyType& sessionKey) {
+ {
+ auto clientCallback = mSessionMap[sessionKey].callback.lock();
+ if (clientCallback != nullptr) {
+ clientCallback->onTranscodingFinished(
+ sessionId, TranscodingResultParcel({sessionId, -1 /*actualBitrateBps*/,
+ std::nullopt /*sessionStats*/}));
+ }
+ }
+
+ // Remove the session.
+ removeSession_l(sessionKey, Session::FINISHED);
+
+ // Start next session.
+ updateCurrentSession_l();
+
+ validateState_l();
+ });
+}
+
+void TranscodingSessionController::onError(ClientIdType clientId, SessionIdType sessionId,
+ TranscodingErrorCode err) {
+ notifyClient(clientId, sessionId, "error", [=](const SessionKeyType& sessionKey) {
+ if (err == TranscodingErrorCode::kWatchdogTimeout) {
+ // Abandon the transcoder, as its handler thread might be stuck in some call to
+ // MediaTranscoder altogether, and may not be able to handle any new tasks.
+ mTranscoder->stop(clientId, sessionId, true /*abandon*/);
+ // Clear the last ref count before we create new transcoder.
+ mTranscoder = nullptr;
+ mTranscoder = mTranscoderFactory(shared_from_this());
+ }
+
+ {
+ auto clientCallback = mSessionMap[sessionKey].callback.lock();
+ if (clientCallback != nullptr) {
+ clientCallback->onTranscodingFailed(sessionId, err);
+ }
+ }
+
+ // Remove the session.
+ removeSession_l(sessionKey, Session::ERROR);
+
+ // Start next session.
+ updateCurrentSession_l();
+
+ validateState_l();
+ });
+}
+
+void TranscodingSessionController::onProgressUpdate(ClientIdType clientId, SessionIdType sessionId,
+ int32_t progress) {
+ notifyClient(clientId, sessionId, "progress", [=](const SessionKeyType& sessionKey) {
+ auto callback = mSessionMap[sessionKey].callback.lock();
+ if (callback != nullptr) {
+ callback->onProgressUpdate(sessionId, progress);
+ }
+ mSessionMap[sessionKey].lastProgress = progress;
+ });
+}
+
+void TranscodingSessionController::onHeartBeat(ClientIdType clientId, SessionIdType sessionId) {
+ notifyClient(clientId, sessionId, "heart-beat",
+ [=](const SessionKeyType& /*sessionKey*/) { mWatchdog->keepAlive(); });
+}
+
+void TranscodingSessionController::onResourceLost(ClientIdType clientId, SessionIdType sessionId) {
+ ALOGI("%s", __FUNCTION__);
+
+ notifyClient(clientId, sessionId, "resource_lost", [=](const SessionKeyType& sessionKey) {
+ if (mResourceLost) {
+ return;
+ }
+
+ Session* resourceLostSession = &mSessionMap[sessionKey];
+ if (resourceLostSession->getState() != Session::RUNNING) {
+ ALOGW("session %s lost resource but is no longer running",
+ sessionToString(sessionKey).c_str());
+ return;
+ }
+ // If we receive a resource loss event, the transcoder already paused the transcoding,
+ // so we don't need to call onPaused() to pause it. However, we still need to notify
+ // the client and update the session state here.
+ setSessionState_l(resourceLostSession, Session::PAUSED);
+ // Notify the client as a paused event.
+ auto clientCallback = resourceLostSession->callback.lock();
+ if (clientCallback != nullptr) {
+ clientCallback->onTranscodingPaused(sessionKey.second);
+ }
+ if (mResourcePolicy != nullptr) {
+ mResourcePolicy->setPidResourceLost(resourceLostSession->request.clientPid);
+ }
+ mResourceLost = true;
+
+ validateState_l();
+ });
+}
+
+void TranscodingSessionController::onTopUidsChanged(const std::unordered_set<uid_t>& uids) {
+ if (uids.empty()) {
+ ALOGW("%s: ignoring empty uids", __FUNCTION__);
+ return;
+ }
+
+ std::string uidStr;
+ for (auto it = uids.begin(); it != uids.end(); it++) {
+ if (!uidStr.empty()) {
+ uidStr += ", ";
+ }
+ uidStr += std::to_string(*it);
+ }
+
+ ALOGD("%s: topUids: size %zu, uids: %s", __FUNCTION__, uids.size(), uidStr.c_str());
+
+ std::scoped_lock lock{mLock};
+
+ moveUidsToTop_l(uids, true /*preserveTopUid*/);
+
+ updateCurrentSession_l();
+
+ validateState_l();
+}
+
+void TranscodingSessionController::onUidGone(uid_t goneUid) {
+ ALOGD("%s: gone uid %u", __FUNCTION__, goneUid);
+
+ std::list<SessionKeyType> sessionsToRemove, sessionsForOtherUids;
+
+ std::scoped_lock lock{mLock};
+
+ for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
+ if (it->second.allClientUids.count(goneUid) > 0) {
+ // If goneUid is the only uid, remove the session; otherwise, only
+ // remove the uid from the session.
+ if (it->second.allClientUids.size() > 1) {
+ sessionsForOtherUids.push_back(it->first);
+ } else {
+ sessionsToRemove.push_back(it->first);
+ }
+ }
+ }
+
+ for (auto it = sessionsToRemove.begin(); it != sessionsToRemove.end(); ++it) {
+ // If the session has ever been started, stop it now.
+ // Note that stop() is needed even if the session is currently paused. This instructs
+ // the transcoder to discard any states for the session, otherwise the states may
+ // never be discarded.
+ if (mSessionMap[*it].getState() != Session::NOT_STARTED) {
+ mTranscoder->stop(it->first, it->second);
+ }
+
+ {
+ auto clientCallback = mSessionMap[*it].callback.lock();
+ if (clientCallback != nullptr) {
+ clientCallback->onTranscodingFailed(it->second,
+ TranscodingErrorCode::kUidGoneCancelled);
+ }
+ }
+
+ // Remove the session.
+ removeSession_l(*it, Session::CANCELED);
+ }
+
+ auto keepUid = std::make_shared<std::function<bool(uid_t)>>(
+ [goneUid](uid_t uid) { return uid != goneUid; });
+ for (auto it = sessionsForOtherUids.begin(); it != sessionsForOtherUids.end(); ++it) {
+ removeSession_l(*it, Session::CANCELED, keepUid);
+ }
+
+ // Start next session.
+ updateCurrentSession_l();
+
+ validateState_l();
+}
+
+void TranscodingSessionController::onResourceAvailable() {
+ std::scoped_lock lock{mLock};
+
+ if (!mResourceLost) {
+ return;
+ }
+
+ ALOGI("%s", __FUNCTION__);
+
+ mResourceLost = false;
+ updateCurrentSession_l();
+
+ validateState_l();
+}
+
+void TranscodingSessionController::onThrottlingStarted() {
+ std::scoped_lock lock{mLock};
+
+ if (mThermalThrottling) {
+ return;
+ }
+
+ ALOGI("%s", __FUNCTION__);
+
+ mThermalThrottling = true;
+ updateCurrentSession_l();
+
+ validateState_l();
+}
+
+void TranscodingSessionController::onThrottlingStopped() {
+ std::scoped_lock lock{mLock};
+
+ if (!mThermalThrottling) {
+ return;
+ }
+
+ ALOGI("%s", __FUNCTION__);
+
+ mThermalThrottling = false;
+ updateCurrentSession_l();
+
+ validateState_l();
+}
+
+void TranscodingSessionController::validateState_l() {
+#ifdef VALIDATE_STATE
+ LOG_ALWAYS_FATAL_IF(mSessionQueues.count(OFFLINE_UID) != 1,
+ "mSessionQueues offline queue number is not 1");
+ LOG_ALWAYS_FATAL_IF(*mOfflineUidIterator != OFFLINE_UID,
+ "mOfflineUidIterator not pointing to offline uid");
+ LOG_ALWAYS_FATAL_IF(mUidSortedList.size() != mSessionQueues.size(),
+ "mUidSortedList and mSessionQueues size mismatch, %zu vs %zu",
+ mUidSortedList.size(), mSessionQueues.size());
+
+ int32_t totalSessions = 0;
+ for (auto uid : mUidSortedList) {
+ LOG_ALWAYS_FATAL_IF(mSessionQueues.count(uid) != 1,
+ "mSessionQueues count for uid %d is not 1", uid);
+ for (auto& sessionKey : mSessionQueues[uid]) {
+ LOG_ALWAYS_FATAL_IF(mSessionMap.count(sessionKey) != 1,
+ "mSessions count for session %s is not 1",
+ sessionToString(sessionKey).c_str());
+ }
+
+ totalSessions += mSessionQueues[uid].size();
+ }
+ int32_t totalSessionsAlternative = 0;
+ for (auto const& s : mSessionMap) {
+ totalSessionsAlternative += s.second.allClientUids.size();
+ }
+ LOG_ALWAYS_FATAL_IF(totalSessions != totalSessionsAlternative,
+ "session count (including dup) from mSessionQueues doesn't match that from "
+ "mSessionMap, %d vs %d",
+ totalSessions, totalSessionsAlternative);
+#endif // VALIDATE_STATE
+}
+
+} // namespace android
diff --git a/media/libmediatranscoding/TranscodingThermalPolicy.cpp b/media/libmediatranscoding/TranscodingThermalPolicy.cpp
new file mode 100644
index 0000000..9984abe
--- /dev/null
+++ b/media/libmediatranscoding/TranscodingThermalPolicy.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "TranscodingThermalPolicy"
+
+#include <media/TranscodingThermalPolicy.h>
+#include <media/TranscodingUidPolicy.h>
+#include <utils/Log.h>
+
+namespace android {
+
+static bool needThrottling(AThermalStatus status) {
+ return (status >= ATHERMAL_STATUS_SEVERE);
+}
+
+//static
+void TranscodingThermalPolicy::onStatusChange(void* data, AThermalStatus status) {
+ TranscodingThermalPolicy* policy = static_cast<TranscodingThermalPolicy*>(data);
+ policy->onStatusChange(status);
+}
+
+TranscodingThermalPolicy::TranscodingThermalPolicy()
+ : mRegistered(false), mThermalManager(nullptr), mIsThrottling(false) {
+ registerSelf();
+}
+
+TranscodingThermalPolicy::~TranscodingThermalPolicy() {
+ unregisterSelf();
+}
+
+void TranscodingThermalPolicy::registerSelf() {
+ ALOGI("TranscodingThermalPolicy: registerSelf");
+
+ std::scoped_lock lock{mRegisteredLock};
+
+ if (mRegistered) {
+ return;
+ }
+
+ if (__builtin_available(android __TRANSCODING_MIN_API__, *)) {
+ AThermalManager* thermalManager = AThermal_acquireManager();
+ if (thermalManager == nullptr) {
+ ALOGE("Failed to acquire thermal manager");
+ return;
+ }
+
+ int ret = AThermal_registerThermalStatusListener(thermalManager, onStatusChange, this);
+ if (ret != 0) {
+ ALOGE("Failed to register thermal status listener");
+ AThermal_releaseManager(thermalManager);
+ return;
+ }
+
+ mIsThrottling = needThrottling(AThermal_getCurrentThermalStatus(thermalManager));
+ mThermalManager = thermalManager;
+ }
+
+ mRegistered = true;
+}
+
+void TranscodingThermalPolicy::unregisterSelf() {
+ ALOGI("TranscodingThermalPolicy: unregisterSelf");
+
+ std::scoped_lock lock{mRegisteredLock};
+
+ if (!mRegistered) {
+ return;
+ }
+
+ if (__builtin_available(android __TRANSCODING_MIN_API__, *)) {
+ if (mThermalManager != nullptr) {
+ // Unregister listener
+ int ret =
+ AThermal_unregisterThermalStatusListener(mThermalManager, onStatusChange, this);
+ if (ret != 0) {
+ ALOGW("Failed to unregister thermal status listener");
+ }
+ AThermal_releaseManager(mThermalManager);
+ mThermalManager = nullptr;
+ }
+ }
+
+ mRegistered = false;
+}
+
+void TranscodingThermalPolicy::setCallback(
+ const std::shared_ptr<ThermalPolicyCallbackInterface>& cb) {
+ std::scoped_lock lock{mCallbackLock};
+ mThermalPolicyCallback = cb;
+}
+
+bool TranscodingThermalPolicy::getThrottlingStatus() {
+ std::scoped_lock lock{mRegisteredLock};
+ return mIsThrottling;
+}
+
+void TranscodingThermalPolicy::onStatusChange(AThermalStatus status) {
+ bool isThrottling = needThrottling(status);
+
+ {
+ std::scoped_lock lock{mRegisteredLock};
+ if (isThrottling == mIsThrottling) {
+ return;
+ }
+ ALOGI("Transcoding thermal throttling changed: %d", isThrottling);
+ mIsThrottling = isThrottling;
+ }
+
+ std::scoped_lock lock{mCallbackLock};
+ std::shared_ptr<ThermalPolicyCallbackInterface> cb;
+ if ((cb = mThermalPolicyCallback.lock()) != nullptr) {
+ if (isThrottling) {
+ cb->onThrottlingStarted();
+ } else {
+ cb->onThrottlingStopped();
+ }
+ }
+}
+} // namespace android
diff --git a/media/libmediatranscoding/TranscodingUidPolicy.cpp b/media/libmediatranscoding/TranscodingUidPolicy.cpp
new file mode 100644
index 0000000..0a1ffbc
--- /dev/null
+++ b/media/libmediatranscoding/TranscodingUidPolicy.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "TranscodingUidPolicy"
+
+#include <android/activity_manager.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <inttypes.h>
+#include <media/TranscodingUidPolicy.h>
+#include <utils/Log.h>
+
+#include <utility>
+
+namespace android {
+
+constexpr static uid_t OFFLINE_UID = -1;
+constexpr static int32_t IMPORTANCE_UNKNOWN = INT32_MAX;
+
+TranscodingUidPolicy::TranscodingUidPolicy()
+ : mUidObserver(nullptr), mRegistered(false), mTopUidState(IMPORTANCE_UNKNOWN) {
+ registerSelf();
+}
+
+TranscodingUidPolicy::~TranscodingUidPolicy() {
+ unregisterSelf();
+}
+
+void TranscodingUidPolicy::OnUidImportance(uid_t uid, int32_t uidImportance, void* cookie) {
+ TranscodingUidPolicy* owner = reinterpret_cast<TranscodingUidPolicy*>(cookie);
+ owner->onUidStateChanged(uid, uidImportance);
+}
+
+void TranscodingUidPolicy::registerSelf() {
+ if (__builtin_available(android __TRANSCODING_MIN_API__, *)) {
+ mUidObserver = AActivityManager_addUidImportanceListener(&OnUidImportance, -1, (void*)this);
+ }
+
+ if (mUidObserver == nullptr) {
+ ALOGE("Failed to register uid observer");
+ return;
+ }
+
+ Mutex::Autolock _l(mUidLock);
+ mRegistered = true;
+ ALOGI("Registered uid observer");
+}
+
+void TranscodingUidPolicy::unregisterSelf() {
+ if (__builtin_available(android __TRANSCODING_MIN_API__, *)) {
+ AActivityManager_removeUidImportanceListener(mUidObserver);
+ mUidObserver = nullptr;
+
+ Mutex::Autolock _l(mUidLock);
+ mRegistered = false;
+ ALOGI("Unregistered uid observer");
+ } else {
+ ALOGE("Failed to unregister uid observer");
+ }
+}
+
+void TranscodingUidPolicy::setCallback(const std::shared_ptr<UidPolicyCallbackInterface>& cb) {
+ mUidPolicyCallback = cb;
+}
+
+void TranscodingUidPolicy::registerMonitorUid(uid_t uid) {
+ Mutex::Autolock _l(mUidLock);
+ if (uid == OFFLINE_UID) {
+ ALOGW("Ignoring the offline uid");
+ return;
+ }
+ if (mUidStateMap.find(uid) != mUidStateMap.end()) {
+ ALOGE("%s: Trying to register uid: %d which is already monitored!", __FUNCTION__, uid);
+ return;
+ }
+
+ int32_t state = IMPORTANCE_UNKNOWN;
+ if (__builtin_available(android __TRANSCODING_MIN_API__, *)) {
+ if (mRegistered && AActivityManager_isUidActive(uid)) {
+ state = AActivityManager_getUidImportance(uid);
+ }
+ }
+
+ ALOGV("%s: inserting new uid: %u, procState %d", __FUNCTION__, uid, state);
+
+ mUidStateMap.emplace(std::pair<uid_t, int32_t>(uid, state));
+ mStateUidMap[state].insert(uid);
+
+ updateTopUid_l();
+}
+
+void TranscodingUidPolicy::unregisterMonitorUid(uid_t uid) {
+ Mutex::Autolock _l(mUidLock);
+
+ auto it = mUidStateMap.find(uid);
+ if (it == mUidStateMap.end()) {
+ ALOGE("%s: Trying to unregister uid: %d which is not monitored!", __FUNCTION__, uid);
+ return;
+ }
+
+ auto stateIt = mStateUidMap.find(it->second);
+ if (stateIt != mStateUidMap.end()) {
+ stateIt->second.erase(uid);
+ if (stateIt->second.empty()) {
+ mStateUidMap.erase(stateIt);
+ }
+ }
+ mUidStateMap.erase(it);
+
+ updateTopUid_l();
+}
+
+bool TranscodingUidPolicy::isUidOnTop(uid_t uid) {
+ Mutex::Autolock _l(mUidLock);
+
+ return mTopUidState != IMPORTANCE_UNKNOWN && mTopUidState == getProcState_l(uid);
+}
+
+std::unordered_set<uid_t> TranscodingUidPolicy::getTopUids() const {
+ Mutex::Autolock _l(mUidLock);
+
+ if (mTopUidState == IMPORTANCE_UNKNOWN) {
+ return std::unordered_set<uid_t>();
+ }
+
+ return mStateUidMap.at(mTopUidState);
+}
+
+void TranscodingUidPolicy::onUidStateChanged(uid_t uid, int32_t procState) {
+ ALOGV("onUidStateChanged: uid %u, procState %d", uid, procState);
+
+ bool topUidSetChanged = false;
+ bool isUidGone = false;
+ std::unordered_set<uid_t> topUids;
+ {
+ Mutex::Autolock _l(mUidLock);
+ auto it = mUidStateMap.find(uid);
+ if (it != mUidStateMap.end() && it->second != procState) {
+ isUidGone = (procState == AACTIVITYMANAGER_IMPORTANCE_GONE);
+
+ topUids = mStateUidMap[mTopUidState];
+
+ // Move uid to the new procState.
+ mStateUidMap[it->second].erase(uid);
+ mStateUidMap[procState].insert(uid);
+ it->second = procState;
+
+ updateTopUid_l();
+ if (topUids != mStateUidMap[mTopUidState]) {
+ // Make a copy of the uid set for callback.
+ topUids = mStateUidMap[mTopUidState];
+ topUidSetChanged = true;
+ }
+ }
+ }
+
+ ALOGV("topUidSetChanged: %d, isUidGone %d", topUidSetChanged, isUidGone);
+
+ if (topUidSetChanged) {
+ auto callback = mUidPolicyCallback.lock();
+ if (callback != nullptr) {
+ callback->onTopUidsChanged(topUids);
+ }
+ }
+ if (isUidGone) {
+ auto callback = mUidPolicyCallback.lock();
+ if (callback != nullptr) {
+ callback->onUidGone(uid);
+ }
+ }
+}
+
+void TranscodingUidPolicy::updateTopUid_l() {
+ mTopUidState = IMPORTANCE_UNKNOWN;
+
+ // Find the lowest uid state (ignoring PROCESS_STATE_UNKNOWN) with some monitored uids.
+ for (auto stateIt = mStateUidMap.begin(); stateIt != mStateUidMap.end(); stateIt++) {
+ if (stateIt->first != IMPORTANCE_UNKNOWN && !stateIt->second.empty()) {
+ mTopUidState = stateIt->first;
+ break;
+ }
+ }
+
+ ALOGV("%s: top uid state is %d", __FUNCTION__, mTopUidState);
+}
+
+int32_t TranscodingUidPolicy::getProcState_l(uid_t uid) {
+ auto it = mUidStateMap.find(uid);
+ if (it != mUidStateMap.end()) {
+ return it->second;
+ }
+ return IMPORTANCE_UNKNOWN;
+}
+
+} // namespace android
diff --git a/media/libmediatranscoding/aidl/android/media/IMediaTranscodingService.aidl b/media/libmediatranscoding/aidl/android/media/IMediaTranscodingService.aidl
index 07b6c1a..ad2358e 100644
--- a/media/libmediatranscoding/aidl/android/media/IMediaTranscodingService.aidl
+++ b/media/libmediatranscoding/aidl/android/media/IMediaTranscodingService.aidl
@@ -16,9 +16,10 @@
package android.media;
-import android.media.TranscodingJobParcel;
+import android.media.ITranscodingClient;
+import android.media.ITranscodingClientCallback;
+import android.media.TranscodingSessionParcel;
import android.media.TranscodingRequestParcel;
-import android.media.ITranscodingServiceClient;
/**
* Binder interface for MediaTranscodingService.
@@ -48,64 +49,25 @@
/**
* 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.
+ * Client must call this function to register itself with the service in
+ * order to perform transcoding tasks. This function will return an
+ * ITranscodingClient interface object. The client should save and use it
+ * for all future transactions with the service.
*
- * @param client interface for the MediaTranscodingService to call the client.
+ * @param callback client interface for the MediaTranscodingService to call
+ * the client.
+ * @param clientName name of the client.
* @param opPackageName op package name of the client.
- * @param clientUid user id of the client.
- * @param clientPid process id of the client.
- * @return a unique positive Id assigned to the client by the service, -1 means failed to
- * register.
+ * @return an ITranscodingClient interface object, with nullptr indicating
+ * failure to register.
*/
- int registerClient(in ITranscodingServiceClient client,
- in String opPackageName,
- in int clientUid,
- in int clientPid);
-
- /**
- * 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);
+ ITranscodingClient registerClient(
+ in ITranscodingClientCallback callback,
+ in String clientName,
+ in String opPackageName);
/**
* Returns the number of clients. This is used for debugging.
*/
int getNumOfClients();
-
- /**
- * Submits a transcoding request to MediaTranscodingService.
- *
- * @param clientId assigned Id of the client.
- * @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 TranscodingRequestParcel request,
- out TranscodingJobParcel 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 TranscodingJobParcel job);
}
diff --git a/media/libmediatranscoding/aidl/android/media/ITranscodingClient.aidl b/media/libmediatranscoding/aidl/android/media/ITranscodingClient.aidl
new file mode 100644
index 0000000..9ef9052
--- /dev/null
+++ b/media/libmediatranscoding/aidl/android/media/ITranscodingClient.aidl
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.TranscodingSessionParcel;
+import android.media.TranscodingRequestParcel;
+
+/**
+ * ITranscodingClient
+ *
+ * Interface for a client to communicate with MediaTranscodingService.
+ *
+ * {@hide}
+ */
+interface ITranscodingClient {
+ /**
+ * Submits a transcoding request to MediaTranscodingService.
+ *
+ * @param request a TranscodingRequest contains transcoding configuration.
+ * @param session(output variable) a TranscodingSession generated by MediaTranscodingService.
+ * @return true if success, false otherwise.
+ */
+ boolean submitRequest(in TranscodingRequestParcel request,
+ out TranscodingSessionParcel session);
+
+ /**
+ * Cancels a transcoding session.
+ *
+ * @param sessionId a TranscodingSession generated by the MediaTranscodingService.
+ * @return true if succeeds, false otherwise.
+ */
+ boolean cancelSession(in int sessionId);
+
+ /**
+ * Queries the session detail associated with a sessionId.
+ *
+ * @param sessionId a TranscodingSession generated by the MediaTranscodingService.
+ * @param session(output variable) the TranscodingSession associated with the sessionId.
+ * @return true if succeeds, false otherwise.
+ */
+ boolean getSessionWithId(in int sessionId, out TranscodingSessionParcel session);
+
+ /**
+ * Add an additional client uid requesting a session.
+ *
+ * @sessionId the session id to which to add the additional client uid.
+ * @clientUid the additional client uid to be added.
+ * @return false if the session doesn't exist or the client is already requesting the
+ * session, true otherwise.
+ */
+ boolean addClientUid(in int sessionId, int clientUid);
+
+ /**
+ * Retrieves the (unsorted) list of all clients requesting a session.
+ *
+ * Note that if a session was submitted with offline priority (
+ * TranscodingSessionPriority::kUnspecified), it initially will not be considered requested
+ * by any particular client, because the client could go away any time after the submission.
+ * However, additional uids could be added via addClientUid() after the submission, which
+ * essentially make the request a real-time request instead of an offline request.
+ *
+ * @sessionId the session id for which to retrieve the client uid list.
+ * @clientUids array to hold the retrieved client uid list.
+ * @return false if the session doesn't exist, true otherwise.
+ */
+ @nullable
+ int[] getClientUids(in int sessionId);
+
+ /**
+ * Unregister the client with the MediaTranscodingService.
+ *
+ * Client will not be able to perform any more transcoding after unregister.
+ */
+ void unregister();
+}
diff --git a/media/libmediatranscoding/aidl/android/media/ITranscodingClientCallback.aidl b/media/libmediatranscoding/aidl/android/media/ITranscodingClientCallback.aidl
new file mode 100644
index 0000000..d7d9b6f
--- /dev/null
+++ b/media/libmediatranscoding/aidl/android/media/ITranscodingClientCallback.aidl
@@ -0,0 +1,107 @@
+/**
+ * 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.TranscodingSessionParcel;
+import android.media.TranscodingResultParcel;
+import android.os.ParcelFileDescriptor;
+
+/**
+ * ITranscodingClientCallback
+ *
+ * Interface for the MediaTranscodingService to communicate with the client.
+ *
+ * {@hide}
+ */
+interface ITranscodingClientCallback {
+ /**
+ * Called to open a raw file descriptor to access data under a URI
+ *
+ * @param fileUri The path of the filename.
+ * @param mode The file mode to use. Must be one of ("r, "w", "rw")
+ * @return ParcelFileDescriptor if open the file successfully, null otherwise.
+ */
+ ParcelFileDescriptor openFileDescriptor(in @utf8InCpp String fileUri,
+ in @utf8InCpp String mode);
+
+ /**
+ * Called when the transcoding associated with the sessionId finished.
+ * This will only be called if client request to get all the status of the session.
+ *
+ * @param sessionId sessionId assigned by the MediaTranscodingService upon receiving request.
+ */
+ oneway void onTranscodingStarted(in int sessionId);
+
+ /**
+ * Called when the transcoding associated with the sessionId is paused.
+ * This will only be called if client request to get all the status of the session.
+ *
+ * @param sessionId sessionId assigned by the MediaTranscodingService upon receiving request.
+ */
+ oneway void onTranscodingPaused(in int sessionId);
+
+ /**
+ * Called when the transcoding associated with the sessionId is resumed.
+ * This will only be called if client request to get all the status of the session.
+ *
+ * @param sessionId sessionId assigned by the MediaTranscodingService upon receiving request.
+ */
+ oneway void onTranscodingResumed(in int sessionId);
+
+ /**
+ * Called when the transcoding associated with the sessionId finished.
+ *
+ * @param sessionId sessionId 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 sessionId, in TranscodingResultParcel result);
+
+ /**
+ * Called when the transcoding associated with the sessionId failed.
+ *
+ * @param sessionId sessionId assigned by the MediaTranscodingService upon receiving request.
+ * @param errorCode error code that indicates the error.
+ */
+ oneway void onTranscodingFailed(in int sessionId, in TranscodingErrorCode errorCode);
+
+ /**
+ * Called when the transcoding configuration associated with the sessionId gets updated, i.e. wait
+ * number in the session queue.
+ *
+ * <p> This will only be called if client set requestUpdate to be true in the TranscodingRequest
+ * submitted to the MediaTranscodingService.
+ *
+ * @param sessionId sessionId assigned by the MediaTranscodingService upon receiving request.
+ * @param oldAwaitNumber previous number of sessions ahead of current session.
+ * @param newAwaitNumber updated number of sessions ahead of current session.
+ */
+ oneway void onAwaitNumberOfSessionsChanged(in int sessionId,
+ in int oldAwaitNumber,
+ in int newAwaitNumber);
+
+ /**
+ * Called when there is an update on the progress of the TranscodingSession.
+ *
+ * <p> This will only be called if client set requestUpdate to be true in the TranscodingRequest
+ * submitted to the MediaTranscodingService.
+ *
+ * @param sessionId sessionId assigned by the MediaTranscodingService upon receiving request.
+ * @param progress an integer number ranging from 0 ~ 100 inclusive.
+ */
+ oneway void onProgressUpdate(in int sessionId, in int progress);
+}
diff --git a/media/libmediatranscoding/aidl/android/media/ITranscodingServiceClient.aidl b/media/libmediatranscoding/aidl/android/media/ITranscodingServiceClient.aidl
deleted file mode 100644
index e23c833..0000000
--- a/media/libmediatranscoding/aidl/android/media/ITranscodingServiceClient.aidl
+++ /dev/null
@@ -1,77 +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.
- */
-
-package android.media;
-
-import android.media.TranscodingErrorCode;
-import android.media.TranscodingJobParcel;
-import android.media.TranscodingResultParcel;
-
-/**
- * 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 TranscodingResultParcel 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
index 7f47fdc..fdd86c7 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl
@@ -23,11 +23,20 @@
*/
@Backing(type = "int")
enum TranscodingErrorCode {
- kUnknown = 0,
- kUnsupported = 1,
- kDecoderError = 2,
- kEncoderError = 3,
- kExtractorError = 4,
- kMuxerError = 5,
- kInvalidBitstream = 6
+ // Errors exposed to client side.
+ kNoError = 0,
+ kDroppedByService = 1,
+ kServiceUnavailable = 2,
+
+ // Other private errors.
+ kPrivateErrorFirst = 1000,
+ kUnknown = kPrivateErrorFirst + 0,
+ kMalformed = kPrivateErrorFirst + 1,
+ kUnsupported = kPrivateErrorFirst + 2,
+ kInvalidParameter = kPrivateErrorFirst + 3,
+ kInvalidOperation = kPrivateErrorFirst + 4,
+ kErrorIO = kPrivateErrorFirst + 5,
+ kInsufficientResources = kPrivateErrorFirst + 6,
+ kWatchdogTimeout = kPrivateErrorFirst + 7,
+ kUidGoneCancelled = kPrivateErrorFirst + 8,
}
\ No newline at end of file
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingJobParcel.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingJobParcel.aidl
deleted file mode 100644
index d912c38..0000000
--- a/media/libmediatranscoding/aidl/android/media/TranscodingJobParcel.aidl
+++ /dev/null
@@ -1,46 +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.
- */
-
-package android.media;
-
-import android.media.TranscodingRequestParcel;
-
-/**
- * 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 TranscodingJobParcel {
- /**
- * A unique positive Id generated by the MediaTranscodingService.
- */
- int jobId;
-
- /**
- * The request associated with the TranscodingJob.
- */
- TranscodingRequestParcel 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/TranscodingJobPriority.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
deleted file mode 100644
index 1a5d81a..0000000
--- a/media/libmediatranscoding/aidl/android/media/TranscodingJobPriority.aidl
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-/**
- * Priority of a transcoding job.
- *
- * {@hide}
- */
-@Backing(type="int")
-enum TranscodingJobPriority {
- // TODO(hkuang): define what each priority level actually mean.
- kUnspecified = 0,
- kLow = 1,
- /**
- * 2 ~ 20 is reserved for future use.
- */
- kNormal = 21,
- /**
- * 22 ~ 30 is reserved for future use.
- */
- kHigh = 31,
-}
\ No newline at end of file
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingRequestParcel.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingRequestParcel.aidl
index 7b7986d..71f82b8 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingRequestParcel.aidl
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingRequestParcel.aidl
@@ -16,8 +16,11 @@
package android.media;
-import android.media.TranscodingJobPriority;
+import android.os.ParcelFileDescriptor;
+import android.media.TranscodingSessionPriority;
+import android.media.TranscodingTestConfig;
import android.media.TranscodingType;
+import android.media.TranscodingVideoTrackFormat;
/**
* TranscodingRequest contains the desired configuration for the transcoding.
@@ -27,9 +30,48 @@
//TODO(hkuang): Implement the parcelable.
parcelable TranscodingRequestParcel {
/**
- * Name of file to be transcoded.
+ * The absolute file path of the source file.
*/
- @utf8InCpp String fileName;
+ @utf8InCpp String sourceFilePath;
+
+ /*
+ * The filedescrptor of the sourceFilePath. If the source Fd is provided, transcoding service
+ * will use this fd instead of calling back to client side to open the sourceFilePath. It is
+ * client's responsibility to make sure sourceFd is opened from sourceFilePath.
+ */
+ @nullable ParcelFileDescriptor sourceFd;
+
+ /**
+ * The absolute file path of the destination file.
+ */
+ @utf8InCpp String destinationFilePath;
+
+ /*
+ * The filedescrptor of the destinationFilePath. If the destination Fd is provided, transcoding
+ * service will use this fd instead of calling back to client side to open the
+ * destinationFilePath. It is client's responsibility to make sure destinationFd is opened
+ * from destinationFilePath.
+ */
+ @nullable ParcelFileDescriptor destinationFd;
+
+ /**
+ * The UID of the client that this transcoding request is for. Only privileged caller could
+ * set this Uid as only they could do the transcoding on behalf of the client.
+ * -1 means not available.
+ */
+ int clientUid = -1;
+
+ /**
+ * The PID of the client that this transcoding request is for. Only privileged caller could
+ * set this Uid as only they could do the transcoding on behalf of the client.
+ * -1 means not available.
+ */
+ int clientPid = -1;
+
+ /**
+ * The package name of the client whom this transcoding request is for.
+ */
+ @utf8InCpp String clientPackageName;
/**
* Type of the transcoding.
@@ -37,22 +79,44 @@
TranscodingType transcodingType;
/**
- * Input source file descriptor.
+ * Requested video track format for the transcoding.
+ * Note that the transcoding service will try to fulfill the requested format as much as
+ * possbile, while subject to hardware and software limitation. The final video track format
+ * will be available in the TranscodingSessionParcel when the session is finished.
*/
- ParcelFileDescriptor inFd;
-
- /**
- * Output transcoded file descriptor.
- */
- ParcelFileDescriptor outFd;
+ @nullable TranscodingVideoTrackFormat requestedVideoTrackFormat;
/**
* Priority of this transcoding. Service will schedule the transcoding based on the priority.
*/
- TranscodingJobPriority priority;
+ TranscodingSessionPriority priority;
/**
- * Whether to receive update on progress and change of awaitNumJobs.
+ * Whether to receive update on progress and change of awaitNumSessions.
+ * Default to false.
*/
- boolean requestUpdate;
+ boolean requestProgressUpdate = false;
+
+ /**
+ * Whether to receive update on session's start/stop/pause/resume.
+ * Default to false.
+ */
+ boolean requestSessionEventUpdate = false;
+
+ /**
+ * Whether this request is for testing.
+ */
+ boolean isForTesting = false;
+
+ /**
+ * Test configuration. This will be available only when isForTesting is set to true.
+ */
+ @nullable TranscodingTestConfig testConfig;
+
+ /**
+ * Whether to get the stats of the transcoding.
+ * If this is enabled, the TranscodingSessionStats will be returned in TranscodingResultParcel
+ * upon transcoding finishes.
+ */
+ boolean enableStats = false;
}
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingResultParcel.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingResultParcel.aidl
index 65c49e7..7826e25 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingResultParcel.aidl
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingResultParcel.aidl
@@ -16,6 +16,8 @@
package android.media;
+import android.media.TranscodingSessionStats;
+
/**
* Result of the transcoding.
*
@@ -24,9 +26,9 @@
//TODO(hkuang): Implement the parcelable.
parcelable TranscodingResultParcel {
/**
- * The jobId associated with the TranscodingResult.
+ * The sessionId associated with the TranscodingResult.
*/
- int jobId;
+ int sessionId;
/**
* Actual bitrate of the transcoded video in bits per second. This will only present for video
@@ -34,5 +36,9 @@
*/
int actualBitrateBps;
- // TODO(hkuang): Add more fields.
+ /**
+ * Stats of the transcoding session. This will only be available when client requests to get the
+ * stats in TranscodingRequestParcel.
+ */
+ @nullable TranscodingSessionStats sessionStats;
}
\ No newline at end of file
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingSessionParcel.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingSessionParcel.aidl
new file mode 100644
index 0000000..3a4a500
--- /dev/null
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingSessionParcel.aidl
@@ -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.
+ */
+
+package android.media;
+
+import android.media.TranscodingRequestParcel;
+import android.media.TranscodingVideoTrackFormat;
+
+/**
+ * TranscodingSession 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 TranscodingSessionParcel {
+ /**
+ * A unique positive Id generated by the MediaTranscodingService.
+ */
+ int sessionId;
+
+ /**
+ * The request associated with the TranscodingSession.
+ */
+ TranscodingRequestParcel request;
+
+ /**
+ * Output video track's format. This will only be avaiable for video transcoding and it will
+ * be avaiable when the session is finished.
+ */
+ @nullable TranscodingVideoTrackFormat videoTrackFormat;
+
+ /**
+ * Current number of sessions ahead of this session. The service schedules the session based on
+ * the priority passed from the client. Client could specify whether to receive updates when the
+ * awaitNumberOfSessions changes through setting requestProgressUpdate in the TranscodingRequest.
+ */
+ int awaitNumberOfSessions;
+}
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingSessionPriority.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingSessionPriority.aidl
new file mode 100644
index 0000000..f001484
--- /dev/null
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingSessionPriority.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * Priority of a transcoding session.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+enum TranscodingSessionPriority {
+ // TODO(hkuang): define what each priority level actually mean.
+ kUnspecified = 0,
+ kLow = 1,
+ /**
+ * 2 ~ 20 is reserved for future use.
+ */
+ kNormal = 21,
+ /**
+ * 22 ~ 30 is reserved for future use.
+ */
+ kHigh = 31,
+}
\ No newline at end of file
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingSessionStats.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingSessionStats.aidl
new file mode 100644
index 0000000..b3e7eea
--- /dev/null
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingSessionStats.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * TranscodingSessionStats encapsulated the stats of the a TranscodingSession.
+ *
+ * {@hide}
+ */
+parcelable TranscodingSessionStats {
+ /**
+ * System time of when the session is created.
+ */
+ long sessionCreatedTimeUs;
+
+ /**
+ * System time of when the session is finished.
+ */
+ long sessionFinishedTimeUs;
+
+ /**
+ * Total time spend on transcoding, exclude the time in pause.
+ */
+ long totalProcessingTimeUs;
+
+ /**
+ * Total time spend on handling the session, include the time in pause.
+ * The totaltimeUs is actually the same as sessionFinishedTimeUs - sessionCreatedTimeUs.
+ */
+ long totalTimeUs;
+}
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingTestConfig.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingTestConfig.aidl
new file mode 100644
index 0000000..6727974
--- /dev/null
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingTestConfig.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ package android.media;
+
+ /**
+ * TranscodingTestConfig contains the test configureation used in testing.
+ *
+ * {@hide}
+ */
+parcelable TranscodingTestConfig {
+ /**
+ * Passthrough mode used for testing. The transcoding service will assume the destination
+ * path already contains the transcoding of the source file and return it to client directly.
+ */
+ boolean passThroughMode = false;
+
+ /**
+ * Time of processing the session in milliseconds. Service will return the session result at
+ * least after processingTotalTimeMs from the time it starts to process the session. Note that
+ * if service uses real MediaTranscoder to do transcoding, the time spent on transcoding may be
+ * more than that.
+ */
+ int processingTotalTimeMs = 0;
+}
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingVideoTrackFormat.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingVideoTrackFormat.aidl
new file mode 100644
index 0000000..8ed241a
--- /dev/null
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingVideoTrackFormat.aidl
@@ -0,0 +1,84 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.TranscodingVideoCodecType;
+
+/**
+ * TranscodingVideoTrackFormat contains the video track format of a video.
+ *
+ * TODO(hkuang): Switch to PersistableBundle when b/156428735 is fixed or after we remove
+ * aidl_interface
+ *
+ * Note that TranscodingVideoTrackFormat is used in TranscodingRequestParcel for the client to
+ * specify the desired transcoded video format, and is also used in TranscodingSessionParcel for the
+ * service to notify client of the final video format for transcoding.
+ * When used as input in TranscodingRequestParcel, the client only needs to specify the config that
+ * they want to change, e.g. codec or resolution, and all the missing configs will be extracted
+ * from the source video and applied to the destination video.
+ * When used as output in TranscodingSessionParcel, all the configs will be populated to indicate
+ * the final encoder configs used for transcoding.
+ *
+ * {@hide}
+ */
+parcelable TranscodingVideoTrackFormat {
+ /**
+ * Video Codec type.
+ */
+ TranscodingVideoCodecType codecType; // TranscodingVideoCodecType::kUnspecified;
+
+ /**
+ * Width of the video in pixels. -1 means unavailable.
+ */
+ int width = -1;
+
+ /**
+ * Height of the video in pixels. -1 means unavailable.
+ */
+ int height = -1;
+
+ /**
+ * Bitrate in bits per second. -1 means unavailable.
+ */
+ int bitrateBps = -1;
+
+ /**
+ * Codec profile. This must be the same constant as used in MediaCodecInfo.CodecProfileLevel.
+ * -1 means unavailable.
+ */
+ int profile = -1;
+
+ /**
+ * Codec level. This must be the same constant as used in MediaCodecInfo.CodecProfileLevel.
+ * -1 means unavailable.
+ */
+ int level = -1;
+
+ /**
+ * Decoder operating rate. This is used to work around the fact that vendor does not boost the
+ * hardware to maximum speed in transcoding usage case. This operating rate will be applied
+ * to decoder inside MediaTranscoder. -1 means unavailable.
+ */
+ int decoderOperatingRate = -1;
+
+ /**
+ * Encoder operating rate. This is used to work around the fact that vendor does not boost the
+ * hardware to maximum speed in transcoding usage case. This operating rate will be applied
+ * to encoder inside MediaTranscoder. -1 means unavailable.
+ */
+ int encoderOperatingRate = -1;
+}
diff --git a/media/libmediatranscoding/build_and_run_all_unit_tests.sh b/media/libmediatranscoding/build_and_run_all_unit_tests.sh
new file mode 100755
index 0000000..388e2ea
--- /dev/null
+++ b/media/libmediatranscoding/build_and_run_all_unit_tests.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# Script to run all transcoding related tests from subfolders.
+# Run script from this folder.
+#
+
+if [ -z "$ANDROID_BUILD_TOP" ]; then
+ echo "Android build environment not set"
+ exit -1
+fi
+
+# ensure we have mm
+. $ANDROID_BUILD_TOP/build/envsetup.sh
+
+mm
+
+echo "waiting for device"
+
+adb root && adb wait-for-device remount && adb sync
+SYNC_FINISHED=true
+
+# Run the transcoding service tests.
+pushd tests
+. build_and_run_all_unit_tests.sh
+popd
+
+# Run the transcoder tests.
+pushd transcoder/tests/
+. build_and_run_all_unit_tests.sh
+popd
+
diff --git a/media/libmediatranscoding/include/media/AdjustableMaxPriorityQueue.h b/media/libmediatranscoding/include/media/AdjustableMaxPriorityQueue.h
index 0e8dcfd..5ba1ee2 100644
--- a/media/libmediatranscoding/include/media/AdjustableMaxPriorityQueue.h
+++ b/media/libmediatranscoding/include/media/AdjustableMaxPriorityQueue.h
@@ -26,7 +26,7 @@
namespace android {
/*
- * AdjustableMaxPriorityQueue is a custom max priority queue that helps managing jobs for
+ * AdjustableMaxPriorityQueue is a custom max priority queue that helps managing sessions for
* MediaTranscodingService.
*
* AdjustableMaxPriorityQueue is a wrapper template around the STL's *_heap() functions.
@@ -38,7 +38,7 @@
*/
template <class T, class Comparator = std::less<T>>
class AdjustableMaxPriorityQueue {
- public:
+public:
typedef typename std::vector<T>::iterator iterator;
typedef typename std::vector<T>::const_iterator const_iterator;
@@ -104,7 +104,7 @@
/* Return the backbone storage of this PriorityQueue. Mainly used for debugging. */
const std::vector<T>& getStorage() const { return mHeap; };
- private:
+private:
std::vector<T> mHeap;
/* Implementation shared by both public push() methods. */
diff --git a/media/libmediatranscoding/include/media/ControllerClientInterface.h b/media/libmediatranscoding/include/media/ControllerClientInterface.h
new file mode 100644
index 0000000..9311e2e
--- /dev/null
+++ b/media/libmediatranscoding/include/media/ControllerClientInterface.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_CONTROLLER_CLIENT_INTERFACE_H
+#define ANDROID_MEDIA_CONTROLLER_CLIENT_INTERFACE_H
+
+#include <aidl/android/media/ITranscodingClientCallback.h>
+#include <aidl/android/media/TranscodingRequestParcel.h>
+#include <media/TranscodingDefs.h>
+
+namespace android {
+
+using ::aidl::android::media::ITranscodingClientCallback;
+using ::aidl::android::media::TranscodingRequestParcel;
+
+// Interface for a client to call the controller to schedule or retrieve
+// the status of a session.
+class ControllerClientInterface {
+public:
+ /**
+ * Submits one request to the controller.
+ *
+ * Returns true on success and false on failure. This call will fail is a session identified
+ * by <clientId, sessionId> already exists.
+ */
+ virtual bool submit(ClientIdType clientId, SessionIdType sessionId, uid_t callingUid,
+ uid_t clientUid, const TranscodingRequestParcel& request,
+ const std::weak_ptr<ITranscodingClientCallback>& clientCallback) = 0;
+
+ /**
+ * Cancels a session identified by <clientId, sessionId>.
+ *
+ * If sessionId is negative (<0), all sessions with a specified priority (that's not
+ * TranscodingSessionPriority::kUnspecified) will be cancelled. Otherwise, only the single
+ * session <clientId, sessionId> will be cancelled.
+ *
+ * Returns false if a single session is being cancelled but it doesn't exist. Returns
+ * true otherwise.
+ */
+ virtual bool cancel(ClientIdType clientId, SessionIdType sessionId) = 0;
+
+ /**
+ * Retrieves information about a session.
+ *
+ * Returns true and the session if it exists, and false otherwise.
+ */
+ virtual bool getSession(ClientIdType clientId, SessionIdType sessionId,
+ TranscodingRequestParcel* request) = 0;
+
+ /**
+ * Add an additional client uid requesting the session identified by <clientId, sessionId>.
+ *
+ * Returns false if the session doesn't exist, or the client is already requesting the
+ * session. Returns true otherwise.
+ */
+ virtual bool addClientUid(ClientIdType clientId, SessionIdType sessionId, uid_t clientUid);
+
+ /**
+ * Retrieves the (unsorted) list of all clients requesting the session identified by
+ * <clientId, sessionId>.
+ *
+ * Note that if a session was submitted with offline priority (
+ * TranscodingSessionPriority::kUnspecified), it initially will not be considered requested
+ * by any particular client, because the client could go away any time after the submission.
+ * However, additional uids could be added via addClientUid() after the submission, which
+ * essentially make the request a real-time request instead of an offline request.
+ *
+ * Returns false if the session doesn't exist. Returns true otherwise.
+ */
+ virtual bool getClientUids(ClientIdType clientId, SessionIdType sessionId,
+ std::vector<int32_t>* out_clientUids);
+
+protected:
+ virtual ~ControllerClientInterface() = default;
+};
+
+} // namespace android
+#endif // ANDROID_MEDIA_CONTROLLER_CLIENT_INTERFACE_H
diff --git a/media/libmediatranscoding/include/media/ResourcePolicyInterface.h b/media/libmediatranscoding/include/media/ResourcePolicyInterface.h
new file mode 100644
index 0000000..ecce252
--- /dev/null
+++ b/media/libmediatranscoding/include/media/ResourcePolicyInterface.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_RESOURCE_POLICY_INTERFACE_H
+#define ANDROID_MEDIA_RESOURCE_POLICY_INTERFACE_H
+#include <memory>
+namespace android {
+
+class ResourcePolicyCallbackInterface;
+
+// Interface for the SessionController to control the resource status updates.
+class ResourcePolicyInterface {
+public:
+ // Set the associated callback interface to send the events when resource
+ // status changes. (Set to nullptr will stop the updates.)
+ virtual void setCallback(const std::shared_ptr<ResourcePolicyCallbackInterface>& cb) = 0;
+ virtual void setPidResourceLost(pid_t pid) = 0;
+
+protected:
+ virtual ~ResourcePolicyInterface() = default;
+};
+
+// Interface for notifying the SessionController of a change in resource status.
+class ResourcePolicyCallbackInterface {
+public:
+ // Called when codec resources become available. The controller may use this
+ // as a signal to attempt restart transcoding sessions that were previously
+ // paused due to temporary resource loss.
+ virtual void onResourceAvailable() = 0;
+
+protected:
+ virtual ~ResourcePolicyCallbackInterface() = default;
+};
+
+} // namespace android
+#endif // ANDROID_MEDIA_RESOURCE_POLICY_INTERFACE_H
diff --git a/media/libmediatranscoding/include/media/ThermalPolicyInterface.h b/media/libmediatranscoding/include/media/ThermalPolicyInterface.h
new file mode 100644
index 0000000..1cc0cc1
--- /dev/null
+++ b/media/libmediatranscoding/include/media/ThermalPolicyInterface.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_THERMAL_POLICY_INTERFACE_H
+#define ANDROID_MEDIA_THERMAL_POLICY_INTERFACE_H
+#include <memory>
+
+namespace android {
+
+class ThermalPolicyCallbackInterface;
+
+// Interface for the SessionController to control the thermal policy.
+class ThermalPolicyInterface {
+public:
+ // Set the associated callback interface to send the events when the thermal
+ // status changes.
+ virtual void setCallback(const std::shared_ptr<ThermalPolicyCallbackInterface>& cb) = 0;
+
+ // Get the current thermal throttling status. Returns true if throttling is on,
+ // false otherwise.
+ virtual bool getThrottlingStatus() = 0;
+
+protected:
+ virtual ~ThermalPolicyInterface() = default;
+};
+
+// Interface for notifying the SessionController of thermal throttling status.
+class ThermalPolicyCallbackInterface {
+public:
+ // Called when the session controller should start or stop thermal throttling.
+ virtual void onThrottlingStarted() = 0;
+ virtual void onThrottlingStopped() = 0;
+
+protected:
+ virtual ~ThermalPolicyCallbackInterface() = default;
+};
+
+} // namespace android
+#endif // ANDROID_MEDIA_THERMAL_POLICY_INTERFACE_H
diff --git a/media/libmediatranscoding/include/media/TranscoderInterface.h b/media/libmediatranscoding/include/media/TranscoderInterface.h
new file mode 100644
index 0000000..3b0bd3b
--- /dev/null
+++ b/media/libmediatranscoding/include/media/TranscoderInterface.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TRANSCODER_INTERFACE_H
+#define ANDROID_MEDIA_TRANSCODER_INTERFACE_H
+
+#include <aidl/android/media/ITranscodingClientCallback.h>
+#include <aidl/android/media/TranscodingErrorCode.h>
+#include <aidl/android/media/TranscodingRequestParcel.h>
+#include <media/TranscodingDefs.h>
+
+namespace android {
+
+using ::aidl::android::media::ITranscodingClientCallback;
+using ::aidl::android::media::TranscodingErrorCode;
+using ::aidl::android::media::TranscodingRequestParcel;
+class TranscoderCallbackInterface;
+
+// Interface for the controller to call the transcoder to take actions.
+class TranscoderInterface {
+public:
+ virtual void start(ClientIdType clientId, SessionIdType sessionId,
+ const TranscodingRequestParcel& request, uid_t callingUid,
+ const std::shared_ptr<ITranscodingClientCallback>& clientCallback) = 0;
+ virtual void pause(ClientIdType clientId, SessionIdType sessionId) = 0;
+ virtual void resume(ClientIdType clientId, SessionIdType sessionId,
+ const TranscodingRequestParcel& request, uid_t callingUid,
+ const std::shared_ptr<ITranscodingClientCallback>& clientCallback) = 0;
+ // Stop the specified session. If abandon is true, the transcoder wrapper will be discarded
+ // after the session stops.
+ virtual void stop(ClientIdType clientId, SessionIdType sessionId, bool abandon = false) = 0;
+
+protected:
+ virtual ~TranscoderInterface() = default;
+};
+
+// Interface for the transcoder to notify the controller of the status of
+// the currently running session, or temporary loss of transcoding resources.
+class TranscoderCallbackInterface {
+public:
+ // TODO(chz): determine what parameters are needed here.
+ virtual void onStarted(ClientIdType clientId, SessionIdType sessionId) = 0;
+ virtual void onPaused(ClientIdType clientId, SessionIdType sessionId) = 0;
+ virtual void onResumed(ClientIdType clientId, SessionIdType sessionId) = 0;
+ virtual void onFinish(ClientIdType clientId, SessionIdType sessionId) = 0;
+ virtual void onError(ClientIdType clientId, SessionIdType sessionId,
+ TranscodingErrorCode err) = 0;
+ virtual void onProgressUpdate(ClientIdType clientId, SessionIdType sessionId,
+ int32_t progress) = 0;
+ virtual void onHeartBeat(ClientIdType clientId, SessionIdType sessionId) = 0;
+
+ // Called when transcoding becomes temporarily inaccessible due to loss of resource.
+ // If there is any session currently running, it will be paused. When resource contention
+ // is solved, the controller should call TranscoderInterface's to either start a new session,
+ // or resume a paused session.
+ virtual void onResourceLost(ClientIdType clientId, SessionIdType sessionId) = 0;
+
+protected:
+ virtual ~TranscoderCallbackInterface() = default;
+};
+
+} // namespace android
+#endif // ANDROID_MEDIA_TRANSCODER_INTERFACE_H
diff --git a/media/libmediatranscoding/include/media/TranscoderWrapper.h b/media/libmediatranscoding/include/media/TranscoderWrapper.h
new file mode 100644
index 0000000..d3d4c86
--- /dev/null
+++ b/media/libmediatranscoding/include/media/TranscoderWrapper.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_TRANSCODER_WRAPPER_H
+#define ANDROID_TRANSCODER_WRAPPER_H
+
+#include <media/NdkMediaError.h>
+#include <media/NdkMediaFormat.h>
+#include <media/TranscoderInterface.h>
+#include <media/TranscodingLogger.h>
+
+#include <chrono>
+#include <list>
+#include <map>
+#include <mutex>
+
+namespace android {
+
+class MediaTranscoder;
+class Parcelable;
+
+/*
+ * Wrapper class around MediaTranscoder.
+ * Implements TranscoderInterface for TranscodingSessionController to use.
+ */
+class TranscoderWrapper : public TranscoderInterface,
+ public std::enable_shared_from_this<TranscoderWrapper> {
+public:
+ TranscoderWrapper(const std::shared_ptr<TranscoderCallbackInterface>& cb,
+ const std::shared_ptr<TranscodingLogger>& logger,
+ int64_t heartBeatIntervalUs);
+ ~TranscoderWrapper();
+
+ // TranscoderInterface
+ void start(ClientIdType clientId, SessionIdType sessionId,
+ const TranscodingRequestParcel& request, uid_t callingUid,
+ const std::shared_ptr<ITranscodingClientCallback>& clientCallback) override;
+ void pause(ClientIdType clientId, SessionIdType sessionId) override;
+ void resume(ClientIdType clientId, SessionIdType sessionId,
+ const TranscodingRequestParcel& request, uid_t callingUid,
+ const std::shared_ptr<ITranscodingClientCallback>& clientCallback) override;
+ void stop(ClientIdType clientId, SessionIdType sessionId, bool abandon = false) override;
+ // ~TranscoderInterface
+
+private:
+ class CallbackImpl;
+ struct Event {
+ enum Type {
+ NoEvent,
+ Start,
+ Pause,
+ Resume,
+ Stop,
+ Finish,
+ Error,
+ Progress,
+ HeartBeat,
+ Abandon
+ } type;
+ ClientIdType clientId;
+ SessionIdType sessionId;
+ std::function<void()> runnable;
+ int32_t arg;
+ };
+ using SessionKeyType = std::pair<ClientIdType, SessionIdType>;
+
+ std::shared_ptr<CallbackImpl> mTranscoderCb;
+ std::shared_ptr<MediaTranscoder> mTranscoder;
+ std::weak_ptr<TranscoderCallbackInterface> mCallback;
+ std::shared_ptr<TranscodingLogger> mLogger;
+ std::shared_ptr<AMediaFormat> mSrcFormat;
+ std::shared_ptr<AMediaFormat> mDstFormat;
+ int64_t mHeartBeatIntervalUs;
+ std::mutex mLock;
+ std::condition_variable mCondition;
+ std::list<Event> mQueue; // GUARDED_BY(mLock);
+ std::map<SessionKeyType, std::shared_ptr<ndk::ScopedAParcel>> mPausedStateMap;
+ ClientIdType mCurrentClientId;
+ SessionIdType mCurrentSessionId;
+ uid_t mCurrentCallingUid;
+ std::chrono::steady_clock::time_point mTranscodeStartTime;
+
+ // Whether the looper has been created.
+ bool mLooperReady;
+
+ static std::string toString(const Event& event);
+ void onFinish(ClientIdType clientId, SessionIdType sessionId);
+ void onError(ClientIdType clientId, SessionIdType sessionId, media_status_t status);
+ void onProgress(ClientIdType clientId, SessionIdType sessionId, int32_t progress);
+ void onHeartBeat(ClientIdType clientId, SessionIdType sessionId);
+
+ media_status_t handleStart(ClientIdType clientId, SessionIdType sessionId,
+ const TranscodingRequestParcel& request, uid_t callingUid,
+ const std::shared_ptr<ITranscodingClientCallback>& callback);
+ media_status_t handlePause(ClientIdType clientId, SessionIdType sessionId);
+ media_status_t handleResume(ClientIdType clientId, SessionIdType sessionId,
+ const TranscodingRequestParcel& request, uid_t callingUid,
+ const std::shared_ptr<ITranscodingClientCallback>& callback);
+ media_status_t setupTranscoder(
+ ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& request,
+ uid_t callingUid, const std::shared_ptr<ITranscodingClientCallback>& callback,
+ TranscodingLogger::SessionEndedReason* failureReason /* nonnull */,
+ const std::shared_ptr<ndk::ScopedAParcel>& pausedState = nullptr);
+
+ void cleanup();
+ void logSessionEnded(const TranscodingLogger::SessionEndedReason& reason, int error);
+ void reportError(ClientIdType clientId, SessionIdType sessionId, media_status_t err);
+ void queueEvent(Event::Type type, ClientIdType clientId, SessionIdType sessionId,
+ const std::function<void()> runnable, int32_t arg = 0);
+ void threadLoop();
+};
+
+} // namespace android
+#endif // ANDROID_TRANSCODER_WRAPPER_H
diff --git a/media/libmediatranscoding/include/media/TranscodingClientManager.h b/media/libmediatranscoding/include/media/TranscodingClientManager.h
index eec120a..be55c78 100644
--- a/media/libmediatranscoding/include/media/TranscodingClientManager.h
+++ b/media/libmediatranscoding/include/media/TranscodingClientManager.h
@@ -17,73 +17,77 @@
#ifndef ANDROID_MEDIA_TRANSCODING_CLIENT_MANAGER_H
#define ANDROID_MEDIA_TRANSCODING_CLIENT_MANAGER_H
-#include <aidl/android/media/BnTranscodingServiceClient.h>
-#include <android/binder_ibinder.h>
+#include <aidl/android/media/ITranscodingClient.h>
+#include <aidl/android/media/ITranscodingClientCallback.h>
#include <sys/types.h>
#include <utils/Condition.h>
-#include <utils/RefBase.h>
#include <utils/String8.h>
#include <utils/Vector.h>
+#include <map>
#include <mutex>
#include <unordered_map>
+#include <unordered_set>
+
+#include "ControllerClientInterface.h"
namespace android {
-using ::aidl::android::media::ITranscodingServiceClient;
-
-class MediaTranscodingService;
+using ::aidl::android::media::ITranscodingClient;
+using ::aidl::android::media::ITranscodingClientCallback;
/*
* TranscodingClientManager manages all the transcoding clients across different processes.
*
- * TranscodingClientManager is a global singleton that could only acquired by
- * MediaTranscodingService. It manages all the clients's registration/unregistration and clients'
- * information. It also bookkeeps all the clients' information. It also monitors to the death of the
+ * TranscodingClientManager manages all the clients's registration/unregistration and clients'
+ * information. It also bookkeeps all the clients' information. It also monitors the death of the
* clients. Upon client's death, it will remove the client from it.
*
* TODO(hkuang): Hook up with ResourceManager for resource management.
* TODO(hkuang): Hook up with MediaMetrics to log all the transactions.
*/
-class TranscodingClientManager {
- public:
+class TranscodingClientManager : public std::enable_shared_from_this<TranscodingClientManager> {
+public:
virtual ~TranscodingClientManager();
/**
- * ClientInfo contains a single client's information.
- */
- struct ClientInfo {
- /* The remote client that this ClientInfo is associated with. */
- std::shared_ptr<ITranscodingServiceClient> mClient;
- /* A unique positive Id assigned to the client by the service. */
- int32_t mClientId;
- /* Process id of the client */
- int32_t mClientPid;
- /* User id of the client. */
- int32_t mClientUid;
- /* Package name of the client. */
- std::string mClientOpPackageName;
-
- ClientInfo(const std::shared_ptr<ITranscodingServiceClient>& client, int64_t clientId,
- int32_t pid, int32_t uid, const std::string& opPackageName)
- : mClient(client),
- mClientId(clientId),
- mClientPid(pid),
- mClientUid(uid),
- mClientOpPackageName(opPackageName) {}
- };
-
- /**
* Adds a new client to the manager.
*
- * The client must have valid clientId, pid, uid and opPackageName, otherwise, this will return
- * a non-zero errorcode. If the client has already been added, it will also return non-zero
- * errorcode.
+ * The client must have valid callback, pid, uid, clientName and opPackageName.
+ * Otherwise, this will return a non-zero errorcode. If the client callback has
+ * already been added, it will also return non-zero errorcode.
*
- * @param client to be added to the manager.
+ * @param callback client callback for the service to call this client.
+ * @param clientName client's name.
+ * @param opPackageName client's package name.
+ * @param client output holding the ITranscodingClient interface for the client
+ * to use for subsequent communications with the service.
* @return 0 if client is added successfully, non-zero errorcode otherwise.
*/
- status_t addClient(std::unique_ptr<ClientInfo> client);
+ status_t addClient(const std::shared_ptr<ITranscodingClientCallback>& callback,
+ const std::string& clientName, const std::string& opPackageName,
+ std::shared_ptr<ITranscodingClient>* client);
+
+ /**
+ * Gets the number of clients.
+ */
+ size_t getNumOfClients() const;
+
+ /**
+ * Dump all the client information to the fd.
+ */
+ void dumpAllClients(int fd, const Vector<String16>& args);
+
+private:
+ friend class MediaTranscodingService;
+ friend class TranscodingClientManagerTest;
+ struct ClientImpl;
+
+ // Only allow MediaTranscodingService and unit tests to instantiate.
+ TranscodingClientManager(const std::shared_ptr<ControllerClientInterface>& controller);
+
+ // Checks if a user is trusted (and allowed to submit sessions on behalf of other uids)
+ bool isTrustedCaller(pid_t pid, uid_t uid);
/**
* Removes an existing client from the manager.
@@ -93,39 +97,24 @@
* @param clientId id of the client to be removed..
* @return 0 if client is removed successfully, non-zero errorcode otherwise.
*/
- status_t removeClient(int32_t clientId);
-
- /**
- * Gets the number of clients.
- */
- size_t getNumOfClients() const;
-
- /**
- * Checks if a client with clientId is already registered.
- */
- bool isClientIdRegistered(int32_t clientId) const;
-
- /**
- * Dump all the client information to the fd.
- */
- void dumpAllClients(int fd, const Vector<String16>& args);
-
- private:
- friend class MediaTranscodingService;
- friend class TranscodingClientManagerTest;
-
- /** Get the singleton instance of the TranscodingClientManager. */
- static TranscodingClientManager& getInstance();
-
- TranscodingClientManager();
+ status_t removeClient(ClientIdType clientId);
static void BinderDiedCallback(void* cookie);
mutable std::mutex mLock;
- std::unordered_map<int32_t, std::unique_ptr<ClientInfo>> mClientIdToClientInfoMap
+ std::unordered_map<ClientIdType, std::shared_ptr<ClientImpl>> mClientIdToClientMap
GUARDED_BY(mLock);
+ std::unordered_set<uintptr_t> mRegisteredCallbacks GUARDED_BY(mLock);
::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
+
+ std::shared_ptr<ControllerClientInterface> mSessionController;
+ std::unordered_set<uid_t> mTrustedUids;
+
+ static std::atomic<ClientIdType> sCookieCounter;
+ static std::mutex sCookie2ClientLock;
+ static std::map<ClientIdType, std::shared_ptr<ClientImpl>> sCookie2Client
+ GUARDED_BY(sCookie2ClientLock);
};
} // namespace android
diff --git a/media/libmediatranscoding/include/media/TranscodingDefs.h b/media/libmediatranscoding/include/media/TranscodingDefs.h
new file mode 100644
index 0000000..8e02dd2
--- /dev/null
+++ b/media/libmediatranscoding/include/media/TranscodingDefs.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TRANSCODING_DEFS_H
+#define ANDROID_MEDIA_TRANSCODING_DEFS_H
+
+#include <aidl/android/media/ITranscodingClientCallback.h>
+#include <aidl/android/media/TranscodingRequestParcel.h>
+
+namespace android {
+
+using ClientIdType = uintptr_t;
+using SessionIdType = int32_t;
+
+} // namespace android
+#endif // ANDROID_MEDIA_TRANSCODING_DEFS_H
diff --git a/media/libmediatranscoding/include/media/TranscodingLogger.h b/media/libmediatranscoding/include/media/TranscodingLogger.h
new file mode 100644
index 0000000..dc24551
--- /dev/null
+++ b/media/libmediatranscoding/include/media/TranscodingLogger.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TRANSCODING_LOGGER_H
+#define ANDROID_MEDIA_TRANSCODING_LOGGER_H
+
+#include <media/NdkMediaFormat.h>
+#include <utils/Condition.h>
+
+#include <chrono>
+#include <memory>
+#include <mutex>
+#include <queue>
+
+namespace android {
+
+/** Class for logging transcoding events. */
+class TranscodingLogger {
+public:
+ /** The maximum number of atoms pushed to statsd per day. */
+ static constexpr int kMaxAtomsPerDay = 50;
+
+ /** The maximum number of successful transcoding atoms pushed to statsd per day. */
+ static constexpr int kMaxSuccessfulAtomsPerDay = 35;
+
+ /** Reason transcoding session ended. Maps to MediaTranscodingSessionEnded atom's Reason. */
+ enum SessionEndedReason {
+ UNKNOWN = 0,
+ FINISHED,
+ ERROR,
+ PAUSED,
+ CANCELLED,
+ START_FAILED,
+ RESUME_FAILED,
+ CREATE_FAILED,
+ CONFIG_SRC_FAILED,
+ CONFIG_DST_FAILED,
+ CONFIG_TRACK_FAILED,
+ OPEN_SRC_FD_FAILED,
+ OPEN_DST_FD_FAILED,
+ NO_TRACKS,
+ };
+
+ TranscodingLogger();
+ ~TranscodingLogger() = default;
+
+ /**
+ * Logs a transcoding session ended event (MediaTranscodingSessionEnded atom).
+ * @param reason Reason for the transcoding session to end.
+ * @param callingUid UID of the caller connecting to the transcoding service.
+ * @param status Status (error code) of the transcoding session.
+ * @param duration Duration of the transcoding session.
+ * @param srcFormat The source video track format.
+ * @param dstFormat The destination video track format.
+ */
+ void logSessionEnded(enum SessionEndedReason reason, uid_t callingUid, int status,
+ std::chrono::microseconds duration, AMediaFormat* srcFormat,
+ AMediaFormat* dstFormat);
+
+private:
+ friend class TranscodingLoggerTest;
+
+ // Function prototype for writing out the session ended atom.
+ using SessionEndedAtomWriter = std::function<int(
+ int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, char const*, int32_t,
+ int32_t, int32_t, int32_t, bool arg12, int32_t, int32_t, char const*, bool)>;
+
+ std::mutex mLock;
+ std::queue<std::pair<std::chrono::steady_clock::time_point, int>> mLastLoggedAtoms
+ GUARDED_BY(mLock);
+ uint32_t mSuccessfulCount = 0;
+ SessionEndedAtomWriter mSessionEndedAtomWriter;
+
+ void logSessionEnded(const std::chrono::steady_clock::time_point& now,
+ enum SessionEndedReason reason, uid_t callingUid, int status,
+ std::chrono::microseconds duration, AMediaFormat* srcFormat,
+ AMediaFormat* dstFormat);
+ bool shouldLogAtom(const std::chrono::steady_clock::time_point& now, int status);
+ // Used for testing to validate what gets sent to statsd.
+ void setSessionEndedAtomWriter(const SessionEndedAtomWriter& writer);
+};
+
+} // namespace android
+#endif // ANDROID_MEDIA_TRANSCODING_LOGGER_H
diff --git a/media/libmediatranscoding/include/media/TranscodingRequest.h b/media/libmediatranscoding/include/media/TranscodingRequest.h
new file mode 100644
index 0000000..d38fc59
--- /dev/null
+++ b/media/libmediatranscoding/include/media/TranscodingRequest.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TRANSCODING_REQUEST_H
+#define ANDROID_MEDIA_TRANSCODING_REQUEST_H
+
+#include <aidl/android/media/TranscodingRequestParcel.h>
+#include <android/binder_parcel.h>
+
+namespace android {
+
+using ::aidl::android::media::TranscodingRequestParcel;
+
+// TODO: replace __ANDROID_API_FUTURE__with 31 when it's official (b/178144708)
+#define __TRANSCODING_MIN_API__ __ANDROID_API_FUTURE__
+
+// Helper class for duplicating a TranscodingRequestParcel
+class TranscodingRequest : public TranscodingRequestParcel {
+public:
+ TranscodingRequest() = default;
+ TranscodingRequest(const TranscodingRequestParcel& parcel) { setTo(parcel); }
+ TranscodingRequest(const TranscodingRequest& request) { setTo(request); }
+ TranscodingRequest& operator=(const TranscodingRequest& request) {
+ setTo(request);
+ return *this;
+ }
+
+private:
+ void setTo(const TranscodingRequestParcel& parcel) {
+ if (__builtin_available(android __TRANSCODING_MIN_API__, *)) {
+ AParcel* p = AParcel_create();
+ parcel.writeToParcel(p);
+ AParcel_setDataPosition(p, 0);
+ readFromParcel(p);
+ AParcel_delete(p);
+ } else {
+ sourceFilePath = parcel.sourceFilePath;
+ sourceFd = ndk::ScopedFileDescriptor(dup(parcel.sourceFd.get()));
+ destinationFilePath = parcel.destinationFilePath;
+ destinationFd = ndk::ScopedFileDescriptor(dup(parcel.destinationFd.get()));
+ clientUid = parcel.clientUid;
+ clientPid = parcel.clientPid;
+ clientPackageName = parcel.clientPackageName;
+ transcodingType = parcel.transcodingType;
+ requestedVideoTrackFormat = parcel.requestedVideoTrackFormat;
+ priority = parcel.priority;
+ requestProgressUpdate = parcel.requestProgressUpdate;
+ requestSessionEventUpdate = parcel.requestSessionEventUpdate;
+ isForTesting = parcel.isForTesting;
+ testConfig = parcel.testConfig;
+ }
+ }
+};
+
+} // namespace android
+#endif // ANDROID_MEDIA_TRANSCODING_REQUEST_H
diff --git a/media/libmediatranscoding/include/media/TranscodingResourcePolicy.h b/media/libmediatranscoding/include/media/TranscodingResourcePolicy.h
new file mode 100644
index 0000000..ee232e7
--- /dev/null
+++ b/media/libmediatranscoding/include/media/TranscodingResourcePolicy.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TRANSCODING_RESOURCE_POLICY_H
+#define ANDROID_MEDIA_TRANSCODING_RESOURCE_POLICY_H
+
+#include <android/binder_auto_utils.h>
+#include <media/ResourcePolicyInterface.h>
+#include <utils/Condition.h>
+
+#include <mutex>
+namespace aidl {
+namespace android {
+namespace media {
+class IResourceObserverService;
+}
+} // namespace android
+} // namespace aidl
+
+namespace android {
+
+using ::aidl::android::media::IResourceObserverService;
+
+class TranscodingResourcePolicy : public ResourcePolicyInterface {
+public:
+ explicit TranscodingResourcePolicy();
+ ~TranscodingResourcePolicy();
+
+ void setCallback(const std::shared_ptr<ResourcePolicyCallbackInterface>& cb) override;
+ void setPidResourceLost(pid_t pid) override;
+
+private:
+ struct ResourceObserver;
+ mutable std::mutex mRegisteredLock;
+ bool mRegistered GUARDED_BY(mRegisteredLock);
+ std::shared_ptr<IResourceObserverService> mService GUARDED_BY(mRegisteredLock);
+ std::shared_ptr<ResourceObserver> mObserver;
+
+ mutable std::mutex mCallbackLock;
+ std::weak_ptr<ResourcePolicyCallbackInterface> mResourcePolicyCallback
+ GUARDED_BY(mCallbackLock);
+ pid_t mResourceLostPid GUARDED_BY(mCallbackLock);
+
+ ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
+
+ static void BinderDiedCallback(void* cookie);
+
+ void registerSelf();
+ void unregisterSelf();
+ void onResourceAvailable(pid_t pid);
+}; // class TranscodingUidPolicy
+
+} // namespace android
+#endif // ANDROID_MEDIA_TRANSCODING_RESOURCE_POLICY_H
diff --git a/media/libmediatranscoding/include/media/TranscodingSessionController.h b/media/libmediatranscoding/include/media/TranscodingSessionController.h
new file mode 100644
index 0000000..2657889
--- /dev/null
+++ b/media/libmediatranscoding/include/media/TranscodingSessionController.h
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TRANSCODING_SESSION_CONTROLLER_H
+#define ANDROID_MEDIA_TRANSCODING_SESSION_CONTROLLER_H
+
+#include <aidl/android/media/TranscodingSessionPriority.h>
+#include <media/ControllerClientInterface.h>
+#include <media/ResourcePolicyInterface.h>
+#include <media/ThermalPolicyInterface.h>
+#include <media/TranscoderInterface.h>
+#include <media/TranscodingRequest.h>
+#include <media/UidPolicyInterface.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include <chrono>
+#include <functional>
+#include <list>
+#include <map>
+#include <mutex>
+
+namespace android {
+using ::aidl::android::media::TranscodingResultParcel;
+using ::aidl::android::media::TranscodingSessionPriority;
+
+class TranscodingSessionController
+ : public UidPolicyCallbackInterface,
+ public ControllerClientInterface,
+ public TranscoderCallbackInterface,
+ public ResourcePolicyCallbackInterface,
+ public ThermalPolicyCallbackInterface,
+ public std::enable_shared_from_this<TranscodingSessionController> {
+public:
+ virtual ~TranscodingSessionController();
+
+ // ControllerClientInterface
+ bool submit(ClientIdType clientId, SessionIdType sessionId, uid_t callingUid, uid_t clientUid,
+ const TranscodingRequestParcel& request,
+ const std::weak_ptr<ITranscodingClientCallback>& clientCallback) override;
+ bool cancel(ClientIdType clientId, SessionIdType sessionId) override;
+ bool getSession(ClientIdType clientId, SessionIdType sessionId,
+ TranscodingRequestParcel* request) override;
+ bool addClientUid(ClientIdType clientId, SessionIdType sessionId, uid_t clientUid) override;
+ bool getClientUids(ClientIdType clientId, SessionIdType sessionId,
+ std::vector<int32_t>* out_clientUids) override;
+ // ~ControllerClientInterface
+
+ // TranscoderCallbackInterface
+ void onStarted(ClientIdType clientId, SessionIdType sessionId) override;
+ void onPaused(ClientIdType clientId, SessionIdType sessionId) override;
+ void onResumed(ClientIdType clientId, SessionIdType sessionId) override;
+ void onFinish(ClientIdType clientId, SessionIdType sessionId) override;
+ void onError(ClientIdType clientId, SessionIdType sessionId, TranscodingErrorCode err) override;
+ void onProgressUpdate(ClientIdType clientId, SessionIdType sessionId,
+ int32_t progress) override;
+ void onHeartBeat(ClientIdType clientId, SessionIdType sessionId) override;
+ void onResourceLost(ClientIdType clientId, SessionIdType sessionId) override;
+ // ~TranscoderCallbackInterface
+
+ // UidPolicyCallbackInterface
+ void onTopUidsChanged(const std::unordered_set<uid_t>& uids) override;
+ void onUidGone(uid_t goneUid) override;
+ // ~UidPolicyCallbackInterface
+
+ // ResourcePolicyCallbackInterface
+ void onResourceAvailable() override;
+ // ~ResourcePolicyCallbackInterface
+
+ // ThermalPolicyCallbackInterface
+ void onThrottlingStarted() override;
+ void onThrottlingStopped() override;
+ // ~ResourcePolicyCallbackInterface
+
+ /**
+ * Dump all the session information to the fd.
+ */
+ void dumpAllSessions(int fd, const Vector<String16>& args);
+
+private:
+ friend class MediaTranscodingService;
+ friend class TranscodingSessionControllerTest;
+
+ using SessionKeyType = std::pair<ClientIdType, SessionIdType>;
+ using SessionQueueType = std::list<SessionKeyType>;
+ using TranscoderFactoryType = std::function<std::shared_ptr<TranscoderInterface>(
+ const std::shared_ptr<TranscoderCallbackInterface>&)>;
+
+ struct ControllerConfig {
+ // Watchdog timeout.
+ int64_t watchdogTimeoutUs = 3000000LL;
+ // Threshold of time between finish/start below which a back-to-back start is counted.
+ int32_t pacerBurstThresholdMs = 1000;
+ // Maximum allowed back-to-back start count.
+ int32_t pacerBurstCountQuota = 10;
+ // Maximum allowed back-to-back running time.
+ int32_t pacerBurstTimeQuotaSeconds = 120; // 2-min
+ };
+
+ struct Session {
+ enum State {
+ INVALID = -1,
+ NOT_STARTED = 0,
+ RUNNING,
+ PAUSED,
+ // The following states would not appear in live sessions map, but could
+ // appear in past sessions map for logging purpose.
+ FINISHED,
+ CANCELED,
+ ERROR,
+ DROPPED_BY_PACER,
+ };
+ SessionKeyType key;
+ uid_t callingUid;
+ std::unordered_set<uid_t> allClientUids;
+ int32_t lastProgress = 0;
+ int32_t pauseCount = 0;
+ std::chrono::time_point<std::chrono::steady_clock> stateEnterTime;
+ std::chrono::microseconds waitingTime{0};
+ std::chrono::microseconds runningTime{0};
+ std::chrono::microseconds pausedTime{0};
+
+ TranscodingRequest request;
+ std::weak_ptr<ITranscodingClientCallback> callback;
+
+ // Must use setState to change state.
+ void setState(Session::State state);
+ State getState() const { return state; }
+ bool isRunning() { return state == RUNNING; }
+
+ private:
+ State state = INVALID;
+ };
+
+ struct Watchdog;
+ struct Pacer;
+
+ ControllerConfig mConfig;
+
+ // TODO(chz): call transcoder without global lock.
+ // Use mLock for all entrypoints for now.
+ mutable std::mutex mLock;
+
+ std::map<SessionKeyType, Session> mSessionMap;
+
+ // uid->SessionQueue map (uid == -1: offline queue)
+ std::map<uid_t, SessionQueueType> mSessionQueues;
+
+ // uids, with the head being the most-recently-top app, 2nd item is the
+ // previous top app, etc.
+ std::list<uid_t> mUidSortedList;
+ std::list<uid_t>::iterator mOfflineUidIterator;
+ std::map<uid_t, std::string> mUidPackageNames;
+
+ TranscoderFactoryType mTranscoderFactory;
+ std::shared_ptr<TranscoderInterface> mTranscoder;
+ std::shared_ptr<UidPolicyInterface> mUidPolicy;
+ std::shared_ptr<ResourcePolicyInterface> mResourcePolicy;
+ std::shared_ptr<ThermalPolicyInterface> mThermalPolicy;
+
+ Session* mCurrentSession;
+ bool mResourceLost;
+ bool mThermalThrottling;
+ std::list<Session> mSessionHistory;
+ std::shared_ptr<Watchdog> mWatchdog;
+ std::shared_ptr<Pacer> mPacer;
+
+ // Only allow MediaTranscodingService and unit tests to instantiate.
+ TranscodingSessionController(const TranscoderFactoryType& transcoderFactory,
+ const std::shared_ptr<UidPolicyInterface>& uidPolicy,
+ const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy,
+ const std::shared_ptr<ThermalPolicyInterface>& thermalPolicy,
+ const ControllerConfig* config = nullptr);
+
+ void dumpSession_l(const Session& session, String8& result, bool closedSession = false);
+ Session* getTopSession_l();
+ void updateCurrentSession_l();
+ void addUidToSession_l(uid_t uid, const SessionKeyType& sessionKey);
+ void removeSession_l(const SessionKeyType& sessionKey, Session::State finalState,
+ const std::shared_ptr<std::function<bool(uid_t uid)>>& keepUid = nullptr);
+ void moveUidsToTop_l(const std::unordered_set<uid_t>& uids, bool preserveTopUid);
+ void setSessionState_l(Session* session, Session::State state);
+ void notifyClient(ClientIdType clientId, SessionIdType sessionId, const char* reason,
+ std::function<void(const SessionKeyType&)> func);
+ // Internal state verifier (debug only)
+ void validateState_l();
+
+ static String8 sessionToString(const SessionKeyType& sessionKey);
+ static const char* sessionStateToString(const Session::State sessionState);
+};
+
+} // namespace android
+#endif // ANDROID_MEDIA_TRANSCODING_SESSION_CONTROLLER_H
diff --git a/media/libmediatranscoding/include/media/TranscodingThermalPolicy.h b/media/libmediatranscoding/include/media/TranscodingThermalPolicy.h
new file mode 100644
index 0000000..81c6eac
--- /dev/null
+++ b/media/libmediatranscoding/include/media/TranscodingThermalPolicy.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TRANSCODING_THERMAL_POLICY_H
+#define ANDROID_MEDIA_TRANSCODING_THERMAL_POLICY_H
+
+#include <android/thermal.h>
+#include <media/ThermalPolicyInterface.h>
+#include <utils/Condition.h>
+
+#include <mutex>
+
+namespace android {
+
+class TranscodingThermalPolicy : public ThermalPolicyInterface {
+public:
+ explicit TranscodingThermalPolicy();
+ ~TranscodingThermalPolicy();
+
+ void setCallback(const std::shared_ptr<ThermalPolicyCallbackInterface>& cb) override;
+ bool getThrottlingStatus() override;
+
+private:
+ mutable std::mutex mRegisteredLock;
+ bool mRegistered GUARDED_BY(mRegisteredLock);
+
+ mutable std::mutex mCallbackLock;
+ std::weak_ptr<ThermalPolicyCallbackInterface> mThermalPolicyCallback GUARDED_BY(mCallbackLock);
+
+ AThermalManager* mThermalManager;
+ bool mIsThrottling;
+
+ static void onStatusChange(void* data, AThermalStatus status);
+ void onStatusChange(AThermalStatus status);
+ void registerSelf();
+ void unregisterSelf();
+};
+
+} // namespace android
+#endif // ANDROID_MEDIA_TRANSCODING_THERMAL_POLICY_H
diff --git a/media/libmediatranscoding/include/media/TranscodingUidPolicy.h b/media/libmediatranscoding/include/media/TranscodingUidPolicy.h
new file mode 100644
index 0000000..dcb22df
--- /dev/null
+++ b/media/libmediatranscoding/include/media/TranscodingUidPolicy.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TRANSCODING_UID_POLICY_H
+#define ANDROID_MEDIA_TRANSCODING_UID_POLICY_H
+
+#include <aidl/android/media/ITranscodingClient.h>
+#include <aidl/android/media/ITranscodingClientCallback.h>
+#include <media/UidPolicyInterface.h>
+#include <sys/types.h>
+#include <utils/Condition.h>
+
+#include <map>
+#include <mutex>
+#include <unordered_map>
+#include <unordered_set>
+
+// TODO: replace __ANDROID_API_FUTURE__with 31 when it's official (b/178144708)
+#define __TRANSCODING_MIN_API__ __ANDROID_API_FUTURE__
+
+struct AActivityManager_UidImportanceListener;
+
+namespace android {
+
+// Observer for UID lifecycle and provide information about the uid's app
+// priority used by the session controller.
+class TranscodingUidPolicy : public UidPolicyInterface {
+public:
+ explicit TranscodingUidPolicy();
+ ~TranscodingUidPolicy();
+
+ // UidPolicyInterface
+ bool isUidOnTop(uid_t uid) override;
+ void registerMonitorUid(uid_t uid) override;
+ void unregisterMonitorUid(uid_t uid) override;
+ std::unordered_set<uid_t> getTopUids() const override;
+ void setCallback(const std::shared_ptr<UidPolicyCallbackInterface>& cb) override;
+ // ~UidPolicyInterface
+
+private:
+ void onUidStateChanged(uid_t uid, int32_t procState);
+ void registerSelf();
+ void unregisterSelf();
+ int32_t getProcState_l(uid_t uid) NO_THREAD_SAFETY_ANALYSIS;
+ void updateTopUid_l() NO_THREAD_SAFETY_ANALYSIS;
+
+ static void OnUidImportance(uid_t uid, int32_t uidImportance, void* cookie);
+
+ struct ResourceManagerClient;
+ mutable Mutex mUidLock;
+ AActivityManager_UidImportanceListener* mUidObserver;
+
+ bool mRegistered GUARDED_BY(mUidLock);
+ int32_t mTopUidState GUARDED_BY(mUidLock);
+ std::unordered_map<uid_t, int32_t> mUidStateMap GUARDED_BY(mUidLock);
+ std::map<int32_t, std::unordered_set<uid_t>> mStateUidMap GUARDED_BY(mUidLock);
+ std::weak_ptr<UidPolicyCallbackInterface> mUidPolicyCallback;
+}; // class TranscodingUidPolicy
+
+} // namespace android
+#endif // ANDROID_MEDIA_TRANSCODING_SERVICE_H
diff --git a/media/libmediatranscoding/include/media/UidPolicyInterface.h b/media/libmediatranscoding/include/media/UidPolicyInterface.h
new file mode 100644
index 0000000..445a2ff
--- /dev/null
+++ b/media/libmediatranscoding/include/media/UidPolicyInterface.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_UID_POLICY_INTERFACE_H
+#define ANDROID_MEDIA_UID_POLICY_INTERFACE_H
+
+#include <unordered_set>
+
+namespace android {
+
+class UidPolicyCallbackInterface;
+
+// Interface for the controller to query a uid's info.
+class UidPolicyInterface {
+public:
+ // Instruct the uid policy to start monitoring a uid.
+ virtual void registerMonitorUid(uid_t uid) = 0;
+ // Instruct the uid policy to stop monitoring a uid.
+ virtual void unregisterMonitorUid(uid_t uid) = 0;
+ // Whether a uid is among the set of uids that's currently top priority.
+ virtual bool isUidOnTop(uid_t uid) = 0;
+ // Retrieves the set of uids that's currently top priority.
+ virtual std::unordered_set<uid_t> getTopUids() const = 0;
+ // Set the associated callback interface to send the events when uid states change.
+ virtual void setCallback(const std::shared_ptr<UidPolicyCallbackInterface>& cb) = 0;
+
+protected:
+ virtual ~UidPolicyInterface() = default;
+};
+
+// Interface for notifying the controller of a change in uid states.
+class UidPolicyCallbackInterface {
+public:
+ // Called when the set of uids that's top priority among the uids of interest
+ // has changed. The receiver of this callback should adjust accordingly.
+ virtual void onTopUidsChanged(const std::unordered_set<uid_t>& uids) = 0;
+
+ // Called when a uid is gone.
+ virtual void onUidGone(uid_t goneUid) = 0;
+
+protected:
+ virtual ~UidPolicyCallbackInterface() = default;
+};
+
+} // namespace android
+#endif // ANDROID_MEDIA_UID_POLICY_INTERFACE_H
diff --git a/media/libmediatranscoding/tests/AdjustableMaxPriorityQueue_tests.cpp b/media/libmediatranscoding/tests/AdjustableMaxPriorityQueue_tests.cpp
index d58af4e..a35ca53 100644
--- a/media/libmediatranscoding/tests/AdjustableMaxPriorityQueue_tests.cpp
+++ b/media/libmediatranscoding/tests/AdjustableMaxPriorityQueue_tests.cpp
@@ -36,7 +36,7 @@
namespace android {
class IntUniquePtrComp {
- public:
+public:
bool operator()(const std::unique_ptr<int>& lhs, const std::unique_ptr<int>& rhs) const {
return *lhs < *rhs;
}
@@ -223,19 +223,19 @@
}
// Test the heap property and make sure it is the same as std::priority_queue.
-TEST(AdjustableMaxPriorityQueueTest, TranscodingJobTest) {
- // Test data structure that mimics the Transcoding job.
- struct TranscodingJob {
+TEST(AdjustableMaxPriorityQueueTest, TranscodingSessionTest) {
+ // Test data structure that mimics the Transcoding session.
+ struct TranscodingSession {
int32_t priority;
int64_t createTimeUs;
};
- // The job is arranging according to priority with highest priority comes first.
- // For the job with the same priority, the job with early createTime will come first.
- class TranscodingJobComp {
- public:
- bool operator()(const std::unique_ptr<TranscodingJob>& lhs,
- const std::unique_ptr<TranscodingJob>& rhs) const {
+ // The session is arranging according to priority with highest priority comes first.
+ // For the session with the same priority, the session with early createTime will come first.
+ class TranscodingSessionComp {
+ public:
+ bool operator()(const std::unique_ptr<TranscodingSession>& lhs,
+ const std::unique_ptr<TranscodingSession>& rhs) const {
if (lhs->priority != rhs->priority) {
return lhs->priority < rhs->priority;
}
@@ -244,46 +244,47 @@
};
// Map to save each value's position in the heap.
- std::unordered_map<int, TranscodingJob*> jobIdToJobMap;
+ std::unordered_map<int, TranscodingSession*> sessionIdToSessionMap;
- TranscodingJob testJobs[] = {
- {1 /*priority*/, 66 /*createTimeUs*/}, // First job,
- {2 /*priority*/, 67 /*createTimeUs*/}, // Second job,
- {2 /*priority*/, 66 /*createTimeUs*/}, // Third job,
- {3 /*priority*/, 68 /*createTimeUs*/}, // Fourth job.
+ TranscodingSession testSessions[] = {
+ {1 /*priority*/, 66 /*createTimeUs*/}, // First session,
+ {2 /*priority*/, 67 /*createTimeUs*/}, // Second session,
+ {2 /*priority*/, 66 /*createTimeUs*/}, // Third session,
+ {3 /*priority*/, 68 /*createTimeUs*/}, // Fourth session.
};
- AdjustableMaxPriorityQueue<std::unique_ptr<TranscodingJob>, TranscodingJobComp> jobQueue;
+ AdjustableMaxPriorityQueue<std::unique_ptr<TranscodingSession>, TranscodingSessionComp>
+ sessionQueue;
- // Pushes all the jobs into the heap.
- for (int jobId = 0; jobId < 4; ++jobId) {
- auto newJob = std::make_unique<TranscodingJob>(testJobs[jobId]);
- jobIdToJobMap[jobId] = newJob.get();
- EXPECT_TRUE(jobQueue.push(std::move(newJob)));
+ // Pushes all the sessions into the heap.
+ for (int sessionId = 0; sessionId < 4; ++sessionId) {
+ auto newSession = std::make_unique<TranscodingSession>(testSessions[sessionId]);
+ sessionIdToSessionMap[sessionId] = newSession.get();
+ EXPECT_TRUE(sessionQueue.push(std::move(newSession)));
}
- // Check the job queue size.
- EXPECT_EQ(4, jobQueue.size());
+ // Check the session queue size.
+ EXPECT_EQ(4, sessionQueue.size());
- // Check the top and it should be Forth job: (3, 68)
- const std::unique_ptr<TranscodingJob>& topJob = jobQueue.top();
- EXPECT_EQ(3, topJob->priority);
- EXPECT_EQ(68, topJob->createTimeUs);
+ // Check the top and it should be Forth session: (3, 68)
+ const std::unique_ptr<TranscodingSession>& topSession = sessionQueue.top();
+ EXPECT_EQ(3, topSession->priority);
+ EXPECT_EQ(68, topSession->createTimeUs);
// Consume the top.
- std::unique_ptr<TranscodingJob> consumeJob = jobQueue.consume_top();
+ std::unique_ptr<TranscodingSession> consumeSession = sessionQueue.consume_top();
- // Check the top and it should be Third Job (2, 66)
- const std::unique_ptr<TranscodingJob>& topJob2 = jobQueue.top();
- EXPECT_EQ(2, topJob2->priority);
- EXPECT_EQ(66, topJob2->createTimeUs);
+ // Check the top and it should be Third Session (2, 66)
+ const std::unique_ptr<TranscodingSession>& topSession2 = sessionQueue.top();
+ EXPECT_EQ(2, topSession2->priority);
+ EXPECT_EQ(66, topSession2->createTimeUs);
- // Change the Second job's priority to 4 from (2, 67) -> (4, 67). It should becomes top of the
- // queue.
- jobIdToJobMap[1]->priority = 4;
- jobQueue.rebuild();
- const std::unique_ptr<TranscodingJob>& topJob3 = jobQueue.top();
- EXPECT_EQ(4, topJob3->priority);
- EXPECT_EQ(67, topJob3->createTimeUs);
+ // Change the Second session's priority to 4 from (2, 67) -> (4, 67). It should becomes
+ // top of the queue.
+ sessionIdToSessionMap[1]->priority = 4;
+ sessionQueue.rebuild();
+ const std::unique_ptr<TranscodingSession>& topSession3 = sessionQueue.top();
+ EXPECT_EQ(4, topSession3->priority);
+ EXPECT_EQ(67, topSession3->createTimeUs);
}
} // namespace android
\ No newline at end of file
diff --git a/media/libmediatranscoding/tests/Android.bp b/media/libmediatranscoding/tests/Android.bp
index 17a43df..603611a 100644
--- a/media/libmediatranscoding/tests/Android.bp
+++ b/media/libmediatranscoding/tests/Android.bp
@@ -8,6 +8,12 @@
default_applicable_licenses: ["frameworks_av_license"],
}
+filegroup {
+ name: "test_assets",
+ path: "assets",
+ srcs: ["assets/**/*"],
+}
+
cc_defaults {
name: "libmediatranscoding_test_defaults",
@@ -17,15 +23,16 @@
],
shared_libs: [
+ "libandroid",
"libbinder_ndk",
"libcutils",
"liblog",
"libutils",
- "libmediatranscoding"
],
static_libs: [
"mediatranscoding_aidl_interface-ndk_platform",
+ "libmediatranscoding",
],
cflags: [
@@ -47,6 +54,16 @@
}
//
+// TranscodingSessionController unit test
+//
+cc_test {
+ name: "TranscodingSessionController_tests",
+ defaults: ["libmediatranscoding_test_defaults"],
+
+ srcs: ["TranscodingSessionController_tests.cpp"],
+}
+
+//
// AdjustableMaxPriorityQueue unit test
//
cc_test {
@@ -55,3 +72,15 @@
srcs: ["AdjustableMaxPriorityQueue_tests.cpp"],
}
+
+//
+// TranscodingLogger unit test
+//
+cc_test {
+ name: "TranscodingLogger_tests",
+ defaults: ["libmediatranscoding_test_defaults"],
+ shared_libs: ["libmediandk", "libstatssocket#30"],
+ static_libs: ["libmediatranscoder", "libstatslog_media"],
+
+ srcs: ["TranscodingLogger_tests.cpp"],
+}
diff --git a/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp b/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp
index 5d2419d..9233410 100644
--- a/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp
+++ b/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp
@@ -19,262 +19,647 @@
// #define LOG_NDEBUG 0
#define LOG_TAG "TranscodingClientManagerTest"
-#include <aidl/android/media/BnTranscodingServiceClient.h>
+#include <aidl/android/media/BnTranscodingClientCallback.h>
#include <aidl/android/media/IMediaTranscodingService.h>
-#include <aidl/android/media/ITranscodingServiceClient.h>
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <gtest/gtest.h>
+#include <media/ControllerClientInterface.h>
#include <media/TranscodingClientManager.h>
+#include <media/TranscodingRequest.h>
#include <utils/Log.h>
+#include <list>
+
namespace android {
using Status = ::ndk::ScopedAStatus;
-using aidl::android::media::BnTranscodingServiceClient;
-using aidl::android::media::IMediaTranscodingService;
-using aidl::android::media::ITranscodingServiceClient;
+using ::aidl::android::media::BnTranscodingClientCallback;
+using ::aidl::android::media::IMediaTranscodingService;
+using ::aidl::android::media::TranscodingErrorCode;
+using ::aidl::android::media::TranscodingRequestParcel;
+using ::aidl::android::media::TranscodingResultParcel;
+using ::aidl::android::media::TranscodingSessionParcel;
+using ::aidl::android::media::TranscodingSessionPriority;
-constexpr int32_t kInvalidClientId = -1;
-constexpr int32_t kInvalidClientPid = -1;
-constexpr int32_t kInvalidClientUid = -1;
-constexpr const char* kInvalidClientOpPackageName = "";
+constexpr pid_t kInvalidClientPid = -5;
+constexpr pid_t kInvalidClientUid = -10;
+constexpr const char* kInvalidClientName = "";
+constexpr const char* kInvalidClientPackage = "";
-constexpr int32_t kClientId = 1;
-constexpr int32_t kClientPid = 2;
-constexpr int32_t kClientUid = 3;
-constexpr const char* kClientOpPackageName = "TestClient";
+constexpr const char* kClientName = "TestClientName";
+constexpr const char* kClientPackage = "TestClientPackage";
+constexpr uid_t OFFLINE_UID = -1;
-struct TestClient : public BnTranscodingServiceClient {
- TestClient(const std::shared_ptr<IMediaTranscodingService>& service) : mService(service) {
- ALOGD("TestClient Created");
- }
+#define SESSION(n) (n)
- Status getName(std::string* _aidl_return) override {
- *_aidl_return = "test_client";
+struct TestClientCallback : public BnTranscodingClientCallback {
+ TestClientCallback() { ALOGI("TestClientCallback Created"); }
+
+ virtual ~TestClientCallback() { ALOGI("TestClientCallback destroyed"); };
+
+ Status openFileDescriptor(const std::string& /*in_fileUri*/, const std::string& /*in_mode*/,
+ ::ndk::ScopedFileDescriptor* /*_aidl_return*/) override {
return Status::ok();
}
- Status onTranscodingFinished(
- int32_t /* in_jobId */,
- const ::aidl::android::media::TranscodingResultParcel& /* in_result */) override {
+ Status onTranscodingStarted(int32_t /*in_sessionId*/) override { return Status::ok(); }
+
+ Status onTranscodingPaused(int32_t /*in_sessionId*/) override { return Status::ok(); }
+
+ Status onTranscodingResumed(int32_t /*in_sessionId*/) override { return Status::ok(); }
+
+ Status onTranscodingFinished(int32_t in_sessionId,
+ const TranscodingResultParcel& in_result) override {
+ EXPECT_EQ(in_sessionId, in_result.sessionId);
+ mEventQueue.push_back(Finished(in_sessionId));
return Status::ok();
}
- Status onTranscodingFailed(
- int32_t /* in_jobId */,
- ::aidl::android::media::TranscodingErrorCode /*in_errorCode */) override {
+ Status onTranscodingFailed(int32_t in_sessionId,
+ TranscodingErrorCode /*in_errorCode */) override {
+ mEventQueue.push_back(Failed(in_sessionId));
return Status::ok();
}
- Status onAwaitNumberOfJobsChanged(int32_t /* in_jobId */, int32_t /* in_oldAwaitNumber */,
- int32_t /* in_newAwaitNumber */) override {
+ Status onAwaitNumberOfSessionsChanged(int32_t /* in_sessionId */,
+ int32_t /* in_oldAwaitNumber */,
+ int32_t /* in_newAwaitNumber */) override {
return Status::ok();
}
- Status onProgressUpdate(int32_t /* in_jobId */, int32_t /* in_progress */) override {
+ Status onProgressUpdate(int32_t /* in_sessionId */, int32_t /* in_progress */) override {
return Status::ok();
}
- virtual ~TestClient() { ALOGI("TestClient destroyed"); };
+ struct Event {
+ enum {
+ NoEvent,
+ Finished,
+ Failed,
+ } type;
+ SessionIdType sessionId;
+ };
- private:
- std::shared_ptr<IMediaTranscodingService> mService;
- TestClient(const TestClient&) = delete;
- TestClient& operator=(const TestClient&) = delete;
+ static constexpr Event NoEvent = {Event::NoEvent, 0};
+#define DECLARE_EVENT(action) \
+ static Event action(SessionIdType sessionId) { return {Event::action, sessionId}; }
+
+ DECLARE_EVENT(Finished);
+ DECLARE_EVENT(Failed);
+
+ const Event& popEvent() {
+ if (mEventQueue.empty()) {
+ mPoppedEvent = NoEvent;
+ } else {
+ mPoppedEvent = *mEventQueue.begin();
+ mEventQueue.pop_front();
+ }
+ return mPoppedEvent;
+ }
+
+private:
+ Event mPoppedEvent;
+ std::list<Event> mEventQueue;
+
+ TestClientCallback(const TestClientCallback&) = delete;
+ TestClientCallback& operator=(const TestClientCallback&) = delete;
+};
+
+bool operator==(const TestClientCallback::Event& lhs, const TestClientCallback::Event& rhs) {
+ return lhs.type == rhs.type && lhs.sessionId == rhs.sessionId;
+}
+
+struct TestController : public ControllerClientInterface {
+ TestController() { ALOGI("TestController Created"); }
+
+ virtual ~TestController() { ALOGI("TestController Destroyed"); }
+
+ bool submit(ClientIdType clientId, SessionIdType sessionId, uid_t /*callingUid*/,
+ uid_t clientUid, const TranscodingRequestParcel& request,
+ const std::weak_ptr<ITranscodingClientCallback>& clientCallback) override {
+ SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+ if (mSessions.count(sessionKey) > 0) {
+ return false;
+ }
+
+ // This is the secret name we'll check, to test error propagation from
+ // the controller back to client.
+ if (request.sourceFilePath == "bad_source_file") {
+ return false;
+ }
+
+ if (request.priority == TranscodingSessionPriority::kUnspecified) {
+ clientUid = OFFLINE_UID;
+ }
+
+ mSessions[sessionKey].request = request;
+ mSessions[sessionKey].callback = clientCallback;
+ mSessions[sessionKey].allClientUids.insert(clientUid);
+
+ mLastSession = sessionKey;
+ return true;
+ }
+
+ bool addClientUid(ClientIdType clientId, SessionIdType sessionId, uid_t clientUid) override {
+ SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+
+ if (mSessions.count(sessionKey) == 0) {
+ return false;
+ }
+ if (mSessions[sessionKey].allClientUids.count(clientUid) > 0) {
+ return false;
+ }
+ mSessions[sessionKey].allClientUids.insert(clientUid);
+ return true;
+ }
+
+ bool getClientUids(ClientIdType clientId, SessionIdType sessionId,
+ std::vector<int32_t>* out_clientUids) override {
+ SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+
+ if (mSessions.count(sessionKey) == 0) {
+ return false;
+ }
+ out_clientUids->clear();
+ for (uid_t uid : mSessions[sessionKey].allClientUids) {
+ if (uid != OFFLINE_UID) {
+ out_clientUids->push_back(uid);
+ }
+ }
+ return true;
+ }
+
+ bool cancel(ClientIdType clientId, SessionIdType sessionId) override {
+ SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+
+ if (mSessions.count(sessionKey) == 0) {
+ return false;
+ }
+ mSessions.erase(sessionKey);
+ return true;
+ }
+
+ bool getSession(ClientIdType clientId, SessionIdType sessionId,
+ TranscodingRequestParcel* request) override {
+ SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+ if (mSessions.count(sessionKey) == 0) {
+ return false;
+ }
+
+ *(TranscodingRequest*)request = mSessions[sessionKey].request;
+ return true;
+ }
+
+ void finishLastSession() {
+ auto it = mSessions.find(mLastSession);
+ if (it == mSessions.end()) {
+ return;
+ }
+ {
+ auto clientCallback = it->second.callback.lock();
+ if (clientCallback != nullptr) {
+ clientCallback->onTranscodingFinished(
+ mLastSession.second,
+ TranscodingResultParcel({mLastSession.second, 0, std::nullopt}));
+ }
+ }
+ mSessions.erase(it);
+ }
+
+ void abortLastSession() {
+ auto it = mSessions.find(mLastSession);
+ if (it == mSessions.end()) {
+ return;
+ }
+ {
+ auto clientCallback = it->second.callback.lock();
+ if (clientCallback != nullptr) {
+ clientCallback->onTranscodingFailed(mLastSession.second,
+ TranscodingErrorCode::kUnknown);
+ }
+ }
+ mSessions.erase(it);
+ }
+
+ struct Session {
+ TranscodingRequest request;
+ std::weak_ptr<ITranscodingClientCallback> callback;
+ std::unordered_set<uid_t> allClientUids;
+ };
+
+ typedef std::pair<ClientIdType, SessionIdType> SessionKeyType;
+ std::map<SessionKeyType, Session> mSessions;
+ SessionKeyType mLastSession;
};
class TranscodingClientManagerTest : public ::testing::Test {
- public:
- TranscodingClientManagerTest() : mClientManager(TranscodingClientManager::getInstance()) {
+public:
+ TranscodingClientManagerTest()
+ : mController(new TestController()),
+ mClientManager(new TranscodingClientManager(mController)) {
ALOGD("TranscodingClientManagerTest created");
}
void SetUp() override {
- ::ndk::SpAIBinder binder(AServiceManager_getService("media.transcoding"));
- mService = IMediaTranscodingService::fromBinder(binder);
- if (mService == nullptr) {
- ALOGE("Failed to connect to the media.trascoding service.");
- return;
- }
-
- mTestClient = ::ndk::SharedRefBase::make<TestClient>(mService);
+ mClientCallback1 = ::ndk::SharedRefBase::make<TestClientCallback>();
+ mClientCallback2 = ::ndk::SharedRefBase::make<TestClientCallback>();
+ mClientCallback3 = ::ndk::SharedRefBase::make<TestClientCallback>();
}
- void TearDown() override {
- ALOGI("TranscodingClientManagerTest tear down");
- mService = nullptr;
- }
+ void TearDown() override { ALOGI("TranscodingClientManagerTest tear down"); }
~TranscodingClientManagerTest() { ALOGD("TranscodingClientManagerTest destroyed"); }
- TranscodingClientManager& mClientManager;
- std::shared_ptr<ITranscodingServiceClient> mTestClient = nullptr;
- std::shared_ptr<IMediaTranscodingService> mService = nullptr;
+ void addMultipleClients() {
+ EXPECT_EQ(
+ mClientManager->addClient(mClientCallback1, kClientName, kClientPackage, &mClient1),
+ OK);
+ EXPECT_NE(mClient1, nullptr);
+
+ EXPECT_EQ(
+ mClientManager->addClient(mClientCallback2, kClientName, kClientPackage, &mClient2),
+ OK);
+ EXPECT_NE(mClient2, nullptr);
+
+ EXPECT_EQ(
+ mClientManager->addClient(mClientCallback3, kClientName, kClientPackage, &mClient3),
+ OK);
+ EXPECT_NE(mClient3, nullptr);
+
+ EXPECT_EQ(mClientManager->getNumOfClients(), 3);
+ }
+
+ void unregisterMultipleClients() {
+ EXPECT_TRUE(mClient1->unregister().isOk());
+ EXPECT_TRUE(mClient2->unregister().isOk());
+ EXPECT_TRUE(mClient3->unregister().isOk());
+ EXPECT_EQ(mClientManager->getNumOfClients(), 0);
+ }
+
+ std::shared_ptr<TestController> mController;
+ std::shared_ptr<TranscodingClientManager> mClientManager;
+ std::shared_ptr<ITranscodingClient> mClient1;
+ std::shared_ptr<ITranscodingClient> mClient2;
+ std::shared_ptr<ITranscodingClient> mClient3;
+ std::shared_ptr<TestClientCallback> mClientCallback1;
+ std::shared_ptr<TestClientCallback> mClientCallback2;
+ std::shared_ptr<TestClientCallback> mClientCallback3;
};
-TEST_F(TranscodingClientManagerTest, TestAddingWithInvalidClientId) {
- std::shared_ptr<ITranscodingServiceClient> client =
- ::ndk::SharedRefBase::make<TestClient>(mService);
-
- // Create a client with invalid client id.
- std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo =
- std::make_unique<TranscodingClientManager::ClientInfo>(
- client, kInvalidClientId, kClientPid, kClientUid, kClientOpPackageName);
-
- // Add the client to the manager and expect failure.
- status_t err = mClientManager.addClient(std::move(clientInfo));
- EXPECT_TRUE(err != OK);
+TEST_F(TranscodingClientManagerTest, TestAddingWithInvalidClientCallback) {
+ // Add a client with null callback and expect failure.
+ std::shared_ptr<ITranscodingClient> client;
+ status_t err = mClientManager->addClient(nullptr, kClientName, kClientPackage, &client);
+ EXPECT_EQ(err, IMediaTranscodingService::ERROR_ILLEGAL_ARGUMENT);
}
+//
+//TEST_F(TranscodingClientManagerTest, TestAddingWithInvalidClientPid) {
+// // Add a client with invalid Pid and expect failure.
+// std::shared_ptr<ITranscodingClient> client;
+// status_t err = mClientManager->addClient(mClientCallback1,
+// kClientName, kClientPackage, &client);
+// EXPECT_EQ(err, IMediaTranscodingService::ERROR_ILLEGAL_ARGUMENT);
+//}
-TEST_F(TranscodingClientManagerTest, TestAddingWithInvalidClientPid) {
- std::shared_ptr<ITranscodingServiceClient> client =
- ::ndk::SharedRefBase::make<TestClient>(mService);
-
- // Create a client with invalid Pid.
- std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo =
- std::make_unique<TranscodingClientManager::ClientInfo>(
- client, kClientId, kInvalidClientPid, kClientUid, kClientOpPackageName);
-
- // Add the client to the manager and expect failure.
- status_t err = mClientManager.addClient(std::move(clientInfo));
- EXPECT_TRUE(err != OK);
-}
-
-TEST_F(TranscodingClientManagerTest, TestAddingWithInvalidClientUid) {
- std::shared_ptr<ITranscodingServiceClient> client =
- ::ndk::SharedRefBase::make<TestClient>(mService);
-
- // Create a client with invalid Uid.
- std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo =
- std::make_unique<TranscodingClientManager::ClientInfo>(
- client, kClientId, kClientPid, kInvalidClientUid, kClientOpPackageName);
-
- // Add the client to the manager and expect failure.
- status_t err = mClientManager.addClient(std::move(clientInfo));
- EXPECT_TRUE(err != OK);
+TEST_F(TranscodingClientManagerTest, TestAddingWithInvalidClientName) {
+ // Add a client with invalid name and expect failure.
+ std::shared_ptr<ITranscodingClient> client;
+ status_t err = mClientManager->addClient(mClientCallback1, kInvalidClientName, kClientPackage,
+ &client);
+ EXPECT_EQ(err, IMediaTranscodingService::ERROR_ILLEGAL_ARGUMENT);
}
TEST_F(TranscodingClientManagerTest, TestAddingWithInvalidClientPackageName) {
- std::shared_ptr<ITranscodingServiceClient> client =
- ::ndk::SharedRefBase::make<TestClient>(mService);
-
- // Create a client with invalid packagename.
- std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo =
- std::make_unique<TranscodingClientManager::ClientInfo>(
- client, kClientId, kClientPid, kClientUid, kInvalidClientOpPackageName);
-
- // Add the client to the manager and expect failure.
- status_t err = mClientManager.addClient(std::move(clientInfo));
- EXPECT_TRUE(err != OK);
+ // Add a client with invalid packagename and expect failure.
+ std::shared_ptr<ITranscodingClient> client;
+ status_t err = mClientManager->addClient(mClientCallback1, kClientName, kInvalidClientPackage,
+ &client);
+ EXPECT_EQ(err, IMediaTranscodingService::ERROR_ILLEGAL_ARGUMENT);
}
TEST_F(TranscodingClientManagerTest, TestAddingValidClient) {
- std::shared_ptr<ITranscodingServiceClient> client1 =
- ::ndk::SharedRefBase::make<TestClient>(mService);
+ // Add a valid client, should succeed.
+ std::shared_ptr<ITranscodingClient> client;
+ status_t err =
+ mClientManager->addClient(mClientCallback1, kClientName, kClientPackage, &client);
+ EXPECT_EQ(err, OK);
+ EXPECT_NE(client.get(), nullptr);
+ EXPECT_EQ(mClientManager->getNumOfClients(), 1);
- std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo =
- std::make_unique<TranscodingClientManager::ClientInfo>(
- client1, kClientId, kClientPid, kClientUid, kClientOpPackageName);
-
- status_t err = mClientManager.addClient(std::move(clientInfo));
- EXPECT_TRUE(err == OK);
-
- size_t numOfClients = mClientManager.getNumOfClients();
- EXPECT_EQ(numOfClients, 1);
-
- err = mClientManager.removeClient(kClientId);
- EXPECT_TRUE(err == OK);
+ // Unregister client, should succeed.
+ Status status = client->unregister();
+ EXPECT_TRUE(status.isOk());
+ EXPECT_EQ(mClientManager->getNumOfClients(), 0);
}
TEST_F(TranscodingClientManagerTest, TestAddingDupliacteClient) {
- std::shared_ptr<ITranscodingServiceClient> client1 =
- ::ndk::SharedRefBase::make<TestClient>(mService);
+ std::shared_ptr<ITranscodingClient> client;
+ status_t err =
+ mClientManager->addClient(mClientCallback1, kClientName, kClientPackage, &client);
+ EXPECT_EQ(err, OK);
+ EXPECT_NE(client.get(), nullptr);
+ EXPECT_EQ(mClientManager->getNumOfClients(), 1);
- std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo =
- std::make_unique<TranscodingClientManager::ClientInfo>(
- client1, kClientId, kClientPid, kClientUid, kClientOpPackageName);
+ std::shared_ptr<ITranscodingClient> dupClient;
+ err = mClientManager->addClient(mClientCallback1, "dupClient", "dupPackage", &dupClient);
+ EXPECT_EQ(err, IMediaTranscodingService::ERROR_ALREADY_EXISTS);
+ EXPECT_EQ(dupClient.get(), nullptr);
+ EXPECT_EQ(mClientManager->getNumOfClients(), 1);
- status_t err = mClientManager.addClient(std::move(clientInfo));
- EXPECT_TRUE(err == OK);
+ Status status = client->unregister();
+ EXPECT_TRUE(status.isOk());
+ EXPECT_EQ(mClientManager->getNumOfClients(), 0);
- err = mClientManager.addClient(std::move(clientInfo));
- EXPECT_TRUE(err != OK);
+ err = mClientManager->addClient(mClientCallback1, "dupClient", "dupPackage", &dupClient);
+ EXPECT_EQ(err, OK);
+ EXPECT_NE(dupClient.get(), nullptr);
+ EXPECT_EQ(mClientManager->getNumOfClients(), 1);
- err = mClientManager.removeClient(kClientId);
- EXPECT_TRUE(err == OK);
+ status = dupClient->unregister();
+ EXPECT_TRUE(status.isOk());
+ EXPECT_EQ(mClientManager->getNumOfClients(), 0);
}
TEST_F(TranscodingClientManagerTest, TestAddingMultipleClient) {
- std::shared_ptr<ITranscodingServiceClient> client1 =
- ::ndk::SharedRefBase::make<TestClient>(mService);
-
- std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo1 =
- std::make_unique<TranscodingClientManager::ClientInfo>(
- client1, kClientId, kClientPid, kClientUid, kClientOpPackageName);
-
- status_t err = mClientManager.addClient(std::move(clientInfo1));
- EXPECT_TRUE(err == OK);
-
- std::shared_ptr<ITranscodingServiceClient> client2 =
- ::ndk::SharedRefBase::make<TestClient>(mService);
-
- std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo2 =
- std::make_unique<TranscodingClientManager::ClientInfo>(
- client2, kClientId + 1, kClientPid, kClientUid, kClientOpPackageName);
-
- err = mClientManager.addClient(std::move(clientInfo2));
- EXPECT_TRUE(err == OK);
-
- std::shared_ptr<ITranscodingServiceClient> client3 =
- ::ndk::SharedRefBase::make<TestClient>(mService);
-
- // Create a client with invalid packagename.
- std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo3 =
- std::make_unique<TranscodingClientManager::ClientInfo>(
- client3, kClientId + 2, kClientPid, kClientUid, kClientOpPackageName);
-
- err = mClientManager.addClient(std::move(clientInfo3));
- EXPECT_TRUE(err == OK);
-
- size_t numOfClients = mClientManager.getNumOfClients();
- EXPECT_EQ(numOfClients, 3);
-
- err = mClientManager.removeClient(kClientId);
- EXPECT_TRUE(err == OK);
-
- err = mClientManager.removeClient(kClientId + 1);
- EXPECT_TRUE(err == OK);
-
- err = mClientManager.removeClient(kClientId + 2);
- EXPECT_TRUE(err == OK);
+ addMultipleClients();
+ unregisterMultipleClients();
}
-TEST_F(TranscodingClientManagerTest, TestRemovingNonExistClient) {
- status_t err = mClientManager.removeClient(kInvalidClientId);
- EXPECT_TRUE(err != OK);
+TEST_F(TranscodingClientManagerTest, TestSubmitCancelGetSessions) {
+ addMultipleClients();
- err = mClientManager.removeClient(1000 /* clientId */);
- EXPECT_TRUE(err != OK);
+ // Test sessionId assignment.
+ TranscodingRequestParcel request;
+ request.sourceFilePath = "test_source_file_0";
+ request.destinationFilePath = "test_desintaion_file_0";
+ TranscodingSessionParcel session;
+ bool result;
+ EXPECT_TRUE(mClient1->submitRequest(request, &session, &result).isOk());
+ EXPECT_TRUE(result);
+ EXPECT_EQ(session.sessionId, SESSION(0));
+
+ request.sourceFilePath = "test_source_file_1";
+ request.destinationFilePath = "test_desintaion_file_1";
+ EXPECT_TRUE(mClient1->submitRequest(request, &session, &result).isOk());
+ EXPECT_TRUE(result);
+ EXPECT_EQ(session.sessionId, SESSION(1));
+
+ request.sourceFilePath = "test_source_file_2";
+ request.destinationFilePath = "test_desintaion_file_2";
+ EXPECT_TRUE(mClient1->submitRequest(request, &session, &result).isOk());
+ EXPECT_TRUE(result);
+ EXPECT_EQ(session.sessionId, SESSION(2));
+
+ // Test submit bad request (no valid sourceFilePath) fails.
+ TranscodingRequestParcel badRequest;
+ badRequest.sourceFilePath = "bad_source_file";
+ badRequest.destinationFilePath = "bad_destination_file";
+ EXPECT_TRUE(mClient1->submitRequest(badRequest, &session, &result).isOk());
+ EXPECT_FALSE(result);
+
+ // Test submit with bad pid/uid.
+ badRequest.sourceFilePath = "test_source_file_3";
+ badRequest.destinationFilePath = "test_desintaion_file_3";
+ badRequest.clientPid = kInvalidClientPid;
+ badRequest.clientUid = kInvalidClientUid;
+ EXPECT_TRUE(mClient1->submitRequest(badRequest, &session, &result).isOk());
+ EXPECT_FALSE(result);
+
+ // Test get sessions by id.
+ EXPECT_TRUE(mClient1->getSessionWithId(SESSION(2), &session, &result).isOk());
+ EXPECT_EQ(session.sessionId, SESSION(2));
+ EXPECT_EQ(session.request.sourceFilePath, "test_source_file_2");
+ EXPECT_TRUE(result);
+
+ // Test get sessions by invalid id fails.
+ EXPECT_TRUE(mClient1->getSessionWithId(SESSION(100), &session, &result).isOk());
+ EXPECT_FALSE(result);
+
+ // Test cancel non-existent session fail.
+ EXPECT_TRUE(mClient2->cancelSession(SESSION(100), &result).isOk());
+ EXPECT_FALSE(result);
+
+ // Test cancel valid sessionId in arbitrary order.
+ EXPECT_TRUE(mClient1->cancelSession(SESSION(2), &result).isOk());
+ EXPECT_TRUE(result);
+
+ EXPECT_TRUE(mClient1->cancelSession(SESSION(0), &result).isOk());
+ EXPECT_TRUE(result);
+
+ EXPECT_TRUE(mClient1->cancelSession(SESSION(1), &result).isOk());
+ EXPECT_TRUE(result);
+
+ // Test cancel session again fails.
+ EXPECT_TRUE(mClient1->cancelSession(SESSION(1), &result).isOk());
+ EXPECT_FALSE(result);
+
+ // Test get session after cancel fails.
+ EXPECT_TRUE(mClient1->getSessionWithId(SESSION(2), &session, &result).isOk());
+ EXPECT_FALSE(result);
+
+ // Test sessionId independence for each client.
+ EXPECT_TRUE(mClient2->submitRequest(request, &session, &result).isOk());
+ EXPECT_TRUE(result);
+ EXPECT_EQ(session.sessionId, SESSION(0));
+
+ EXPECT_TRUE(mClient2->submitRequest(request, &session, &result).isOk());
+ EXPECT_TRUE(result);
+ EXPECT_EQ(session.sessionId, SESSION(1));
+
+ unregisterMultipleClients();
}
-TEST_F(TranscodingClientManagerTest, TestCheckClientWithClientId) {
- std::shared_ptr<ITranscodingServiceClient> client =
- ::ndk::SharedRefBase::make<TestClient>(mService);
+TEST_F(TranscodingClientManagerTest, TestClientCallback) {
+ addMultipleClients();
- std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo =
- std::make_unique<TranscodingClientManager::ClientInfo>(
- client, kClientId, kClientPid, kClientUid, kClientOpPackageName);
+ TranscodingRequestParcel request;
+ request.sourceFilePath = "test_source_file_name";
+ request.destinationFilePath = "test_destination_file_name";
+ TranscodingSessionParcel session;
+ bool result;
+ EXPECT_TRUE(mClient1->submitRequest(request, &session, &result).isOk());
+ EXPECT_TRUE(result);
+ EXPECT_EQ(session.sessionId, SESSION(0));
- status_t err = mClientManager.addClient(std::move(clientInfo));
- EXPECT_TRUE(err == OK);
+ mController->finishLastSession();
+ EXPECT_EQ(mClientCallback1->popEvent(), TestClientCallback::Finished(session.sessionId));
- bool res = mClientManager.isClientIdRegistered(kClientId);
- EXPECT_TRUE(res);
+ EXPECT_TRUE(mClient1->submitRequest(request, &session, &result).isOk());
+ EXPECT_TRUE(result);
+ EXPECT_EQ(session.sessionId, SESSION(1));
- res = mClientManager.isClientIdRegistered(kInvalidClientId);
- EXPECT_FALSE(res);
+ mController->abortLastSession();
+ EXPECT_EQ(mClientCallback1->popEvent(), TestClientCallback::Failed(session.sessionId));
+
+ EXPECT_TRUE(mClient1->submitRequest(request, &session, &result).isOk());
+ EXPECT_TRUE(result);
+ EXPECT_EQ(session.sessionId, SESSION(2));
+
+ EXPECT_TRUE(mClient2->submitRequest(request, &session, &result).isOk());
+ EXPECT_TRUE(result);
+ EXPECT_EQ(session.sessionId, SESSION(0));
+
+ mController->finishLastSession();
+ EXPECT_EQ(mClientCallback2->popEvent(), TestClientCallback::Finished(session.sessionId));
+
+ unregisterMultipleClients();
}
-} // namespace android
\ No newline at end of file
+TEST_F(TranscodingClientManagerTest, TestUseAfterUnregister) {
+ // Add a client.
+ std::shared_ptr<ITranscodingClient> client;
+ status_t err =
+ mClientManager->addClient(mClientCallback1, kClientName, kClientPackage, &client);
+ EXPECT_EQ(err, OK);
+ EXPECT_NE(client.get(), nullptr);
+
+ // Submit 2 requests, 1 offline and 1 realtime.
+ TranscodingRequestParcel request;
+ TranscodingSessionParcel session;
+ bool result;
+
+ request.sourceFilePath = "test_source_file_0";
+ request.destinationFilePath = "test_destination_file_0";
+ request.priority = TranscodingSessionPriority::kUnspecified;
+ EXPECT_TRUE(client->submitRequest(request, &session, &result).isOk() && result);
+ EXPECT_EQ(session.sessionId, SESSION(0));
+
+ request.sourceFilePath = "test_source_file_1";
+ request.destinationFilePath = "test_destination_file_1";
+ request.priority = TranscodingSessionPriority::kNormal;
+ EXPECT_TRUE(client->submitRequest(request, &session, &result).isOk() && result);
+ EXPECT_EQ(session.sessionId, SESSION(1));
+
+ // Unregister client, should succeed.
+ Status status = client->unregister();
+ EXPECT_TRUE(status.isOk());
+
+ // Test submit new request after unregister, should fail with ERROR_DISCONNECTED.
+ request.sourceFilePath = "test_source_file_2";
+ request.destinationFilePath = "test_destination_file_2";
+ request.priority = TranscodingSessionPriority::kNormal;
+ status = client->submitRequest(request, &session, &result);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
+
+ // Test cancel sessions after unregister, should fail with ERROR_DISCONNECTED
+ // regardless of realtime or offline session, or whether the sessionId is valid.
+ status = client->cancelSession(SESSION(0), &result);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
+
+ status = client->cancelSession(SESSION(1), &result);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
+
+ status = client->cancelSession(SESSION(2), &result);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
+
+ // Test get sessions, should fail with ERROR_DISCONNECTED regardless of realtime
+ // or offline session, or whether the sessionId is valid.
+ status = client->getSessionWithId(SESSION(0), &session, &result);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
+
+ status = client->getSessionWithId(SESSION(1), &session, &result);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
+
+ status = client->getSessionWithId(SESSION(2), &session, &result);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
+}
+
+TEST_F(TranscodingClientManagerTest, TestAddGetClientUidsInvalidArgs) {
+ addMultipleClients();
+
+ bool result;
+ std::optional<std::vector<int32_t>> clientUids;
+ TranscodingRequestParcel request;
+ TranscodingSessionParcel session;
+ uid_t ownUid = ::getuid();
+
+ // Add/Get clients with invalid session id fails.
+ EXPECT_TRUE(mClient1->addClientUid(-1, ownUid, &result).isOk());
+ EXPECT_FALSE(result);
+ EXPECT_TRUE(mClient1->addClientUid(SESSION(0), ownUid, &result).isOk());
+ EXPECT_FALSE(result);
+ EXPECT_TRUE(mClient1->getClientUids(-1, &clientUids).isOk());
+ EXPECT_EQ(clientUids, std::nullopt);
+ EXPECT_TRUE(mClient1->getClientUids(SESSION(0), &clientUids).isOk());
+ EXPECT_EQ(clientUids, std::nullopt);
+
+ unregisterMultipleClients();
+}
+
+TEST_F(TranscodingClientManagerTest, TestAddGetClientUids) {
+ addMultipleClients();
+
+ bool result;
+ std::optional<std::vector<int32_t>> clientUids;
+ TranscodingRequestParcel request;
+ TranscodingSessionParcel session;
+ uid_t ownUid = ::getuid();
+
+ // Submit one real-time session.
+ request.sourceFilePath = "test_source_file_0";
+ request.destinationFilePath = "test_desintaion_file_0";
+ request.priority = TranscodingSessionPriority::kNormal;
+ EXPECT_TRUE(mClient1->submitRequest(request, &session, &result).isOk());
+ EXPECT_TRUE(result);
+
+ // Should have own uid in client uid list.
+ EXPECT_TRUE(mClient1->getClientUids(SESSION(0), &clientUids).isOk());
+ EXPECT_NE(clientUids, std::nullopt);
+ EXPECT_EQ(clientUids->size(), 1);
+ EXPECT_EQ((*clientUids)[0], ownUid);
+
+ // Adding invalid client uid should fail.
+ EXPECT_TRUE(mClient1->addClientUid(SESSION(0), kInvalidClientUid, &result).isOk());
+ EXPECT_FALSE(result);
+
+ // Adding own uid again should fail.
+ EXPECT_TRUE(mClient1->addClientUid(SESSION(0), ownUid, &result).isOk());
+ EXPECT_FALSE(result);
+
+ // Submit one offline session.
+ request.sourceFilePath = "test_source_file_1";
+ request.destinationFilePath = "test_desintaion_file_1";
+ request.priority = TranscodingSessionPriority::kUnspecified;
+ EXPECT_TRUE(mClient1->submitRequest(request, &session, &result).isOk());
+ EXPECT_TRUE(result);
+
+ // Should not have own uid in client uid list.
+ EXPECT_TRUE(mClient1->getClientUids(SESSION(1), &clientUids).isOk());
+ EXPECT_NE(clientUids, std::nullopt);
+ EXPECT_EQ(clientUids->size(), 0);
+
+ // Add own uid (with IMediaTranscodingService::USE_CALLING_UID) again, should succeed.
+ EXPECT_TRUE(
+ mClient1->addClientUid(SESSION(1), IMediaTranscodingService::USE_CALLING_UID, &result)
+ .isOk());
+ EXPECT_TRUE(result);
+ EXPECT_TRUE(mClient1->getClientUids(SESSION(1), &clientUids).isOk());
+ EXPECT_NE(clientUids, std::nullopt);
+ EXPECT_EQ(clientUids->size(), 1);
+ EXPECT_EQ((*clientUids)[0], ownUid);
+
+ // Add more uids, should succeed.
+ int32_t kFakeUid = ::getuid() ^ 0x1;
+ EXPECT_TRUE(mClient1->addClientUid(SESSION(1), kFakeUid, &result).isOk());
+ EXPECT_TRUE(result);
+ EXPECT_TRUE(mClient1->getClientUids(SESSION(1), &clientUids).isOk());
+ EXPECT_NE(clientUids, std::nullopt);
+ std::unordered_set<uid_t> uidSet;
+ uidSet.insert(clientUids->begin(), clientUids->end());
+ EXPECT_EQ(uidSet.size(), 2);
+ EXPECT_EQ(uidSet.count(ownUid), 1);
+ EXPECT_EQ(uidSet.count(kFakeUid), 1);
+
+ unregisterMultipleClients();
+}
+
+} // namespace android
diff --git a/media/libmediatranscoding/tests/TranscodingLogger_tests.cpp b/media/libmediatranscoding/tests/TranscodingLogger_tests.cpp
new file mode 100644
index 0000000..39e5cd4
--- /dev/null
+++ b/media/libmediatranscoding/tests/TranscodingLogger_tests.cpp
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Unit Test for TranscodingLogger
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "TranscodingLoggerTest"
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+#include <media/NdkCommon.h>
+#include <media/TranscodingLogger.h>
+#include <statslog_media.h>
+#include <utils/Log.h>
+
+#include <chrono>
+
+namespace android {
+
+using Reason = TranscodingLogger::SessionEndedReason;
+
+// Data structure corresponding to MediaTranscodingEnded atom.
+struct SessionEndedAtom {
+ SessionEndedAtom(int32_t atomCode, int32_t reason, int32_t callingUid, int32_t status,
+ int32_t transcoderFps, int32_t srcWidth, int32_t srcHeight,
+ char const* srcMime, int32_t srcProfile, int32_t srcLevel, int32_t srcFps,
+ int32_t srcDurationMs, bool srcIsHdr, int32_t dstWidth, int32_t dstHeight,
+ char const* dstMime, bool dstIsHdr)
+ : atomCode(atomCode),
+ reason(reason),
+ callingUid(callingUid),
+ status(status),
+ transcoderFps(transcoderFps),
+ srcWidth(srcWidth),
+ srcHeight(srcHeight),
+ srcMime(srcMime),
+ srcProfile(srcProfile),
+ srcLevel(srcLevel),
+ srcFps(srcFps),
+ srcDurationMs(srcDurationMs),
+ srcIsHdr(srcIsHdr),
+ dstWidth(dstWidth),
+ dstHeight(dstHeight),
+ dstMime(dstMime),
+ dstIsHdr(dstIsHdr) {}
+
+ int32_t atomCode;
+ int32_t reason;
+ int32_t callingUid;
+ int32_t status;
+ int32_t transcoderFps;
+ int32_t srcWidth;
+ int32_t srcHeight;
+ std::string srcMime;
+ int32_t srcProfile;
+ int32_t srcLevel;
+ int32_t srcFps;
+ int32_t srcDurationMs;
+ bool srcIsHdr;
+ int32_t dstWidth;
+ int32_t dstHeight;
+ std::string dstMime;
+ bool dstIsHdr;
+};
+
+// Default configuration values.
+static constexpr int32_t kDefaultCallingUid = 1;
+static constexpr std::chrono::microseconds kDefaultTranscodeDuration = std::chrono::seconds{2};
+
+static constexpr int32_t kDefaultSrcWidth = 1920;
+static constexpr int32_t kDefaultSrcHeight = 1080;
+static const std::string kDefaultSrcMime{AMEDIA_MIMETYPE_VIDEO_HEVC};
+static constexpr int32_t kDefaultSrcProfile = 1; // HEVC Main
+static constexpr int32_t kDefaultSrcLevel = 65536; // HEVCMainTierLevel51
+static constexpr int32_t kDefaultSrcFps = 30;
+static constexpr int32_t kDefaultSrcFrameCount = 120;
+static constexpr int64_t kDefaultSrcDurationUs = 1000000 * kDefaultSrcFrameCount / kDefaultSrcFps;
+
+static constexpr int32_t kDefaultDstWidth = 1280;
+static constexpr int32_t kDefaultDstHeight = 720;
+static const std::string kDefaultDstMime{AMEDIA_MIMETYPE_VIDEO_AVC};
+
+// Util for creating a default source video format.
+static AMediaFormat* CreateSrcFormat() {
+ AMediaFormat* fmt = AMediaFormat_new();
+ AMediaFormat_setInt32(fmt, AMEDIAFORMAT_KEY_WIDTH, kDefaultSrcWidth);
+ AMediaFormat_setInt32(fmt, AMEDIAFORMAT_KEY_HEIGHT, kDefaultSrcHeight);
+ AMediaFormat_setString(fmt, AMEDIAFORMAT_KEY_MIME, kDefaultSrcMime.c_str());
+ AMediaFormat_setInt32(fmt, AMEDIAFORMAT_KEY_PROFILE, kDefaultSrcProfile);
+ AMediaFormat_setInt32(fmt, AMEDIAFORMAT_KEY_LEVEL, kDefaultSrcLevel);
+ AMediaFormat_setInt32(fmt, AMEDIAFORMAT_KEY_FRAME_RATE, kDefaultSrcFps);
+ AMediaFormat_setInt32(fmt, AMEDIAFORMAT_KEY_FRAME_COUNT, kDefaultSrcFrameCount);
+ AMediaFormat_setInt64(fmt, AMEDIAFORMAT_KEY_DURATION, kDefaultSrcDurationUs);
+ return fmt;
+}
+
+// Util for creating a default destination video format.
+static AMediaFormat* CreateDstFormat() {
+ AMediaFormat* fmt = AMediaFormat_new();
+ AMediaFormat_setInt32(fmt, AMEDIAFORMAT_KEY_WIDTH, kDefaultDstWidth);
+ AMediaFormat_setInt32(fmt, AMEDIAFORMAT_KEY_HEIGHT, kDefaultDstHeight);
+ AMediaFormat_setString(fmt, AMEDIAFORMAT_KEY_MIME, kDefaultDstMime.c_str());
+ return fmt;
+}
+
+class TranscodingLoggerTest : public ::testing::Test {
+public:
+ TranscodingLoggerTest() { ALOGI("TranscodingLoggerTest created"); }
+
+ void SetUp() override {
+ ALOGI("TranscodingLoggerTest set up");
+ mLogger.reset(new TranscodingLogger());
+ mLoggedAtoms.clear();
+ mSrcFormat.reset();
+ mDstFormat.reset();
+
+ // Set a custom atom writer that saves all data, so the test can validate it afterwards.
+ mLogger->setSessionEndedAtomWriter(
+ [=](int32_t atomCode, int32_t reason, int32_t callingUid, int32_t status,
+ int32_t transcoderFps, int32_t srcWidth, int32_t srcHeight, char const* srcMime,
+ int32_t srcProfile, int32_t srcLevel, int32_t srcFps, int32_t srcDurationMs,
+ bool srcIsHdr, int32_t dstWidth, int32_t dstHeight, char const* dstMime,
+ bool dstIsHdr) -> int {
+ mLoggedAtoms.emplace_back(atomCode, reason, callingUid, status, transcoderFps,
+ srcWidth, srcHeight, srcMime, srcProfile, srcLevel,
+ srcFps, srcDurationMs, srcIsHdr, dstWidth, dstHeight,
+ dstMime, dstIsHdr);
+ return 0;
+ });
+ }
+
+ void logSession(const std::chrono::steady_clock::time_point& time, Reason reason, int status,
+ AMediaFormat* srcFormat, AMediaFormat* dstFormat) {
+ mLogger->logSessionEnded(time, reason, kDefaultCallingUid, status,
+ kDefaultTranscodeDuration, srcFormat, dstFormat);
+ }
+
+ void logSession(const std::chrono::steady_clock::time_point& time, Reason reason, int status) {
+ if (!mSrcFormat) {
+ mSrcFormat = std::shared_ptr<AMediaFormat>(CreateSrcFormat(), &AMediaFormat_delete);
+ }
+ if (!mDstFormat) {
+ mDstFormat = std::shared_ptr<AMediaFormat>(CreateDstFormat(), &AMediaFormat_delete);
+ }
+ logSession(time, reason, status, mSrcFormat.get(), mDstFormat.get());
+ }
+
+ void logSessionFinished(const std::chrono::steady_clock::time_point& time) {
+ logSession(time, Reason::FINISHED, 0);
+ }
+
+ void logSessionFailed(const std::chrono::steady_clock::time_point& time) {
+ logSession(time, Reason::ERROR, AMEDIA_ERROR_UNKNOWN);
+ }
+
+ int logCount() const { return mLoggedAtoms.size(); }
+
+ void validateLatestAtom(Reason reason, int status, bool passthrough = false) {
+ const SessionEndedAtom& atom = mLoggedAtoms.back();
+
+ EXPECT_EQ(atom.atomCode, android::media::stats::MEDIA_TRANSCODING_SESSION_ENDED);
+ EXPECT_EQ(atom.reason, static_cast<int>(reason));
+ EXPECT_EQ(atom.callingUid, kDefaultCallingUid);
+ EXPECT_EQ(atom.status, status);
+ EXPECT_EQ(atom.srcWidth, kDefaultSrcWidth);
+ EXPECT_EQ(atom.srcHeight, kDefaultSrcHeight);
+ EXPECT_EQ(atom.srcMime, kDefaultSrcMime);
+ EXPECT_EQ(atom.srcProfile, kDefaultSrcProfile);
+ EXPECT_EQ(atom.srcLevel, kDefaultSrcLevel);
+ EXPECT_EQ(atom.srcFps, kDefaultSrcFps);
+ EXPECT_EQ(atom.srcDurationMs, kDefaultSrcDurationUs / 1000);
+ EXPECT_FALSE(atom.srcIsHdr);
+ EXPECT_EQ(atom.dstWidth, passthrough ? kDefaultSrcWidth : kDefaultDstWidth);
+ EXPECT_EQ(atom.dstHeight, passthrough ? kDefaultSrcHeight : kDefaultDstHeight);
+ EXPECT_EQ(atom.dstMime, passthrough ? "passthrough" : kDefaultDstMime);
+ EXPECT_FALSE(atom.dstIsHdr);
+
+ // Transcoder frame rate is only present on successful sessions.
+ if (status == AMEDIA_OK) {
+ std::chrono::duration<double> seconds{kDefaultTranscodeDuration};
+ const int32_t transcoderFps =
+ static_cast<int32_t>(kDefaultSrcFrameCount / seconds.count());
+ EXPECT_EQ(atom.transcoderFps, transcoderFps);
+ } else {
+ EXPECT_EQ(atom.transcoderFps, -1);
+ }
+ }
+
+ void TearDown() override { ALOGI("TranscodingLoggerTest tear down"); }
+ ~TranscodingLoggerTest() { ALOGD("TranscodingLoggerTest destroyed"); }
+
+ std::shared_ptr<TranscodingLogger> mLogger;
+ std::vector<SessionEndedAtom> mLoggedAtoms;
+
+ std::shared_ptr<AMediaFormat> mSrcFormat;
+ std::shared_ptr<AMediaFormat> mDstFormat;
+};
+
+TEST_F(TranscodingLoggerTest, TestDailyLogQuota) {
+ ALOGD("TestDailyLogQuota");
+ auto start = std::chrono::steady_clock::now();
+
+ EXPECT_LT(TranscodingLogger::kMaxSuccessfulAtomsPerDay, TranscodingLogger::kMaxAtomsPerDay);
+
+ // 1. Check that the first kMaxSuccessfulAtomsPerDay successful atoms are logged.
+ for (int i = 0; i < TranscodingLogger::kMaxSuccessfulAtomsPerDay; ++i) {
+ logSessionFinished(start + std::chrono::seconds{i});
+ EXPECT_EQ(logCount(), i + 1);
+ }
+
+ // 2. Check that subsequent successful atoms within the same 24h interval are not logged.
+ for (int i = 1; i < 24; ++i) {
+ logSessionFinished(start + std::chrono::hours{i});
+ EXPECT_EQ(logCount(), TranscodingLogger::kMaxSuccessfulAtomsPerDay);
+ }
+
+ // 3. Check that failed atoms are logged up to kMaxAtomsPerDay.
+ for (int i = TranscodingLogger::kMaxSuccessfulAtomsPerDay;
+ i < TranscodingLogger::kMaxAtomsPerDay; ++i) {
+ logSessionFailed(start + std::chrono::seconds{i});
+ EXPECT_EQ(logCount(), i + 1);
+ }
+
+ // 4. Check that subsequent failed atoms within the same 24h interval are not logged.
+ for (int i = 1; i < 24; ++i) {
+ logSessionFailed(start + std::chrono::hours{i});
+ EXPECT_EQ(logCount(), TranscodingLogger::kMaxAtomsPerDay);
+ }
+
+ // 5. Check that failed and successful atoms are logged again after 24h.
+ logSessionFinished(start + std::chrono::hours{24});
+ EXPECT_EQ(logCount(), TranscodingLogger::kMaxAtomsPerDay + 1);
+
+ logSessionFailed(start + std::chrono::hours{24} + std::chrono::seconds{1});
+ EXPECT_EQ(logCount(), TranscodingLogger::kMaxAtomsPerDay + 2);
+}
+
+TEST_F(TranscodingLoggerTest, TestNullFormats) {
+ ALOGD("TestNullFormats");
+ auto srcFormat = std::shared_ptr<AMediaFormat>(CreateSrcFormat(), &AMediaFormat_delete);
+ auto dstFormat = std::shared_ptr<AMediaFormat>(CreateDstFormat(), &AMediaFormat_delete);
+ auto now = std::chrono::steady_clock::now();
+
+ // Source format null, should not log.
+ logSession(now, Reason::FINISHED, AMEDIA_OK, nullptr /*srcFormat*/, dstFormat.get());
+ EXPECT_EQ(logCount(), 0);
+
+ // Both formats null, should not log.
+ logSession(now, Reason::FINISHED, AMEDIA_OK, nullptr /*srcFormat*/, nullptr /*dstFormat*/);
+ EXPECT_EQ(logCount(), 0);
+
+ // Destination format null (passthrough mode), should log.
+ logSession(now, Reason::FINISHED, AMEDIA_OK, srcFormat.get(), nullptr /*dstFormat*/);
+ EXPECT_EQ(logCount(), 1);
+ validateLatestAtom(Reason::FINISHED, AMEDIA_OK, true /*passthrough*/);
+}
+
+TEST_F(TranscodingLoggerTest, TestAtomContentCorrectness) {
+ ALOGD("TestAtomContentCorrectness");
+ auto now = std::chrono::steady_clock::now();
+
+ // Log and validate a failure.
+ logSession(now, Reason::ERROR, AMEDIA_ERROR_MALFORMED);
+ EXPECT_EQ(logCount(), 1);
+ validateLatestAtom(Reason::ERROR, AMEDIA_ERROR_MALFORMED);
+
+ // Log and validate a success.
+ logSession(now, Reason::FINISHED, AMEDIA_OK);
+ EXPECT_EQ(logCount(), 2);
+ validateLatestAtom(Reason::FINISHED, AMEDIA_OK);
+}
+
+} // namespace android
diff --git a/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp b/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp
new file mode 100644
index 0000000..ef9c4f8
--- /dev/null
+++ b/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp
@@ -0,0 +1,1347 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Unit Test for TranscodingSessionController
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "TranscodingSessionControllerTest"
+
+#include <aidl/android/media/BnTranscodingClientCallback.h>
+#include <aidl/android/media/IMediaTranscodingService.h>
+#include <aidl/android/media/ITranscodingClient.h>
+#include <aidl/android/media/ITranscodingClientCallback.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <gtest/gtest.h>
+#include <media/TranscodingClientManager.h>
+#include <media/TranscodingSessionController.h>
+#include <utils/Log.h>
+
+#include <unordered_set>
+
+namespace android {
+
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::media::BnTranscodingClientCallback;
+using aidl::android::media::IMediaTranscodingService;
+using aidl::android::media::ITranscodingClient;
+using aidl::android::media::TranscodingRequestParcel;
+
+constexpr ClientIdType kClientId = 1000;
+constexpr SessionIdType kClientSessionId = 0;
+constexpr uid_t kClientUid = 5000;
+constexpr pid_t kClientPid = 10000;
+constexpr uid_t kInvalidUid = (uid_t)-1;
+constexpr pid_t kInvalidPid = (pid_t)-1;
+
+#define CLIENT(n) (kClientId + (n))
+#define SESSION(n) (kClientSessionId + (n))
+#define UID(n) (kClientUid + (n))
+#define PID(n) (kClientPid + (n))
+
+class TestUidPolicy : public UidPolicyInterface {
+public:
+ TestUidPolicy() = default;
+ virtual ~TestUidPolicy() = default;
+
+ // UidPolicyInterface
+ void registerMonitorUid(uid_t /*uid*/) override {}
+ void unregisterMonitorUid(uid_t /*uid*/) override {}
+ bool isUidOnTop(uid_t uid) override { return mTopUids.count(uid) > 0; }
+ std::unordered_set<uid_t> getTopUids() const override { return mTopUids; }
+ void setCallback(const std::shared_ptr<UidPolicyCallbackInterface>& cb) override {
+ mUidPolicyCallback = cb;
+ }
+ void setTop(uid_t uid) {
+ std::unordered_set<uid_t> uids = {uid};
+ setTop(uids);
+ }
+ void setTop(const std::unordered_set<uid_t>& uids) {
+ mTopUids = uids;
+ auto uidPolicyCb = mUidPolicyCallback.lock();
+ if (uidPolicyCb != nullptr) {
+ uidPolicyCb->onTopUidsChanged(mTopUids);
+ }
+ }
+
+ std::unordered_set<uid_t> mTopUids;
+ std::weak_ptr<UidPolicyCallbackInterface> mUidPolicyCallback;
+};
+
+class TestResourcePolicy : public ResourcePolicyInterface {
+public:
+ TestResourcePolicy() { reset(); }
+ virtual ~TestResourcePolicy() = default;
+
+ // ResourcePolicyInterface
+ void setCallback(const std::shared_ptr<ResourcePolicyCallbackInterface>& /*cb*/) override {}
+ void setPidResourceLost(pid_t pid) override { mResourceLostPid = pid; }
+ // ~ResourcePolicyInterface
+
+ pid_t getPid() {
+ pid_t result = mResourceLostPid;
+ reset();
+ return result;
+ }
+
+private:
+ void reset() { mResourceLostPid = kInvalidPid; }
+ pid_t mResourceLostPid;
+};
+
+class TestThermalPolicy : public ThermalPolicyInterface {
+public:
+ TestThermalPolicy() = default;
+ virtual ~TestThermalPolicy() = default;
+
+ // ThermalPolicyInterface
+ void setCallback(const std::shared_ptr<ThermalPolicyCallbackInterface>& /*cb*/) override {}
+ bool getThrottlingStatus() { return false; }
+ // ~ThermalPolicyInterface
+
+private:
+};
+
+class TestTranscoder : public TranscoderInterface {
+public:
+ TestTranscoder() : mGeneration(0) {}
+ virtual ~TestTranscoder() {}
+
+ // TranscoderInterface
+ void start(ClientIdType clientId, SessionIdType sessionId,
+ const TranscodingRequestParcel& /*request*/, uid_t /*callingUid*/,
+ const std::shared_ptr<ITranscodingClientCallback>& /*clientCallback*/) override {
+ append(Start(clientId, sessionId));
+ }
+ void pause(ClientIdType clientId, SessionIdType sessionId) override {
+ append(Pause(clientId, sessionId));
+ }
+ void resume(ClientIdType clientId, SessionIdType sessionId,
+ const TranscodingRequestParcel& /*request*/, uid_t /*callingUid*/,
+ const std::shared_ptr<ITranscodingClientCallback>& /*clientCallback*/) override {
+ append(Resume(clientId, sessionId));
+ }
+ void stop(ClientIdType clientId, SessionIdType sessionId, bool abandon) override {
+ append(abandon ? Abandon(clientId, sessionId) : Stop(clientId, sessionId));
+ }
+
+ void onFinished(ClientIdType clientId, SessionIdType sessionId) {
+ append(Finished(clientId, sessionId));
+ }
+
+ void onFailed(ClientIdType clientId, SessionIdType sessionId, TranscodingErrorCode err) {
+ append(Failed(clientId, sessionId), err);
+ }
+
+ void onCreated() {
+ std::scoped_lock lock{mLock};
+ mGeneration++;
+ }
+
+ struct Event {
+ enum { NoEvent, Start, Pause, Resume, Stop, Finished, Failed, Abandon } type;
+ ClientIdType clientId;
+ SessionIdType sessionId;
+ };
+
+ static constexpr Event NoEvent = {Event::NoEvent, 0, 0};
+
+#define DECLARE_EVENT(action) \
+ static Event action(ClientIdType clientId, SessionIdType sessionId) { \
+ return {Event::action, clientId, sessionId}; \
+ }
+
+ DECLARE_EVENT(Start);
+ DECLARE_EVENT(Pause);
+ DECLARE_EVENT(Resume);
+ DECLARE_EVENT(Stop);
+ DECLARE_EVENT(Finished);
+ DECLARE_EVENT(Failed);
+ DECLARE_EVENT(Abandon);
+
+ // Push 1 event to back.
+ void append(const Event& event,
+ const TranscodingErrorCode err = TranscodingErrorCode::kNoError) {
+ std::unique_lock lock(mLock);
+
+ mEventQueue.push_back(event);
+ // Error is sticky, non-error event will not erase it, only getLastError()
+ // clears last error.
+ if (err != TranscodingErrorCode::kNoError) {
+ mLastErrorQueue.push_back(err);
+ }
+ mCondition.notify_one();
+ }
+
+ // Pop 1 event from front, wait for up to timeoutUs if empty.
+ const Event& popEvent(int64_t timeoutUs = 0) {
+ std::unique_lock lock(mLock);
+
+ if (mEventQueue.empty() && timeoutUs > 0) {
+ mCondition.wait_for(lock, std::chrono::microseconds(timeoutUs));
+ }
+
+ if (mEventQueue.empty()) {
+ mPoppedEvent = NoEvent;
+ } else {
+ mPoppedEvent = *mEventQueue.begin();
+ mEventQueue.pop_front();
+ }
+
+ return mPoppedEvent;
+ }
+
+ TranscodingErrorCode getLastError() {
+ std::scoped_lock lock{mLock};
+ if (mLastErrorQueue.empty()) {
+ return TranscodingErrorCode::kNoError;
+ }
+ TranscodingErrorCode err = mLastErrorQueue.front();
+ mLastErrorQueue.pop_front();
+ return err;
+ }
+
+ int32_t getGeneration() {
+ std::scoped_lock lock{mLock};
+ return mGeneration;
+ }
+
+private:
+ std::mutex mLock;
+ std::condition_variable mCondition;
+ Event mPoppedEvent;
+ std::list<Event> mEventQueue;
+ std::list<TranscodingErrorCode> mLastErrorQueue;
+ int32_t mGeneration;
+};
+
+bool operator==(const TestTranscoder::Event& lhs, const TestTranscoder::Event& rhs) {
+ return lhs.type == rhs.type && lhs.clientId == rhs.clientId && lhs.sessionId == rhs.sessionId;
+}
+
+struct TestClientCallback : public BnTranscodingClientCallback {
+ TestClientCallback(TestTranscoder* owner, ClientIdType clientId, uid_t clientUid)
+ : mOwner(owner), mClientId(clientId), mClientUid(clientUid) {
+ ALOGD("TestClient Created");
+ }
+
+ ClientIdType clientId() const { return mClientId; }
+ uid_t clientUid() const { return mClientUid; }
+
+ Status openFileDescriptor(const std::string& /*in_fileUri*/, const std::string& /*in_mode*/,
+ ::ndk::ScopedFileDescriptor* /*_aidl_return*/) override {
+ return Status::ok();
+ }
+
+ Status onTranscodingStarted(int32_t /*in_sessionId*/) override { return Status::ok(); }
+
+ Status onTranscodingPaused(int32_t /*in_sessionId*/) override { return Status::ok(); }
+
+ Status onTranscodingResumed(int32_t /*in_sessionId*/) override { return Status::ok(); }
+
+ Status onTranscodingFinished(int32_t in_sessionId,
+ const TranscodingResultParcel& in_result) override {
+ EXPECT_EQ(in_sessionId, in_result.sessionId);
+ ALOGD("TestClientCallback: received onTranscodingFinished");
+ mOwner->onFinished(mClientId, in_sessionId);
+ return Status::ok();
+ }
+
+ Status onTranscodingFailed(int32_t in_sessionId, TranscodingErrorCode in_errorCode) override {
+ mOwner->onFailed(mClientId, in_sessionId, in_errorCode);
+ return Status::ok();
+ }
+
+ Status onAwaitNumberOfSessionsChanged(int32_t /* in_sessionId */,
+ int32_t /* in_oldAwaitNumber */,
+ int32_t /* in_newAwaitNumber */) override {
+ return Status::ok();
+ }
+
+ Status onProgressUpdate(int32_t /* in_sessionId */, int32_t /* in_progress */) override {
+ return Status::ok();
+ }
+
+ virtual ~TestClientCallback() { ALOGI("TestClient destroyed"); };
+
+private:
+ TestTranscoder* mOwner;
+ ClientIdType mClientId;
+ uid_t mClientUid;
+ TestClientCallback(const TestClientCallback&) = delete;
+ TestClientCallback& operator=(const TestClientCallback&) = delete;
+};
+
+class TranscodingSessionControllerTest : public ::testing::Test {
+public:
+ TranscodingSessionControllerTest() { ALOGI("TranscodingSessionControllerTest created"); }
+ ~TranscodingSessionControllerTest() { ALOGD("TranscodingSessionControllerTest destroyed"); }
+
+ void SetUp() override {
+ ALOGI("TranscodingSessionControllerTest set up");
+ mTranscoder.reset(new TestTranscoder());
+ mUidPolicy.reset(new TestUidPolicy());
+ mResourcePolicy.reset(new TestResourcePolicy());
+ mThermalPolicy.reset(new TestThermalPolicy());
+ // Overrid default burst params with shorter values for testing.
+ TranscodingSessionController::ControllerConfig config = {
+ .pacerBurstThresholdMs = 500,
+ .pacerBurstCountQuota = 10,
+ .pacerBurstTimeQuotaSeconds = 3,
+ };
+ mController.reset(new TranscodingSessionController(
+ [this](const std::shared_ptr<TranscoderCallbackInterface>& /*cb*/) {
+ // Here we require that the SessionController clears out all its refcounts of
+ // the transcoder object when it calls create.
+ EXPECT_EQ(mTranscoder.use_count(), 1);
+ mTranscoder->onCreated();
+ return mTranscoder;
+ },
+ mUidPolicy, mResourcePolicy, mThermalPolicy, &config));
+ mUidPolicy->setCallback(mController);
+
+ // Set priority only, ignore other fields for now.
+ mOfflineRequest.priority = TranscodingSessionPriority::kUnspecified;
+ mRealtimeRequest.priority = TranscodingSessionPriority::kHigh;
+ mClientCallback0 = ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(),
+ CLIENT(0), UID(0));
+ mClientCallback1 = ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(),
+ CLIENT(1), UID(1));
+ mClientCallback2 = ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(),
+ CLIENT(2), UID(2));
+ mClientCallback3 = ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(),
+ CLIENT(3), UID(3));
+ }
+
+ void TearDown() override { ALOGI("TranscodingSessionControllerTest tear down"); }
+
+ void expectTimeout(int64_t clientId, int32_t sessionId, int32_t generation) {
+ EXPECT_EQ(mTranscoder->popEvent(2900000), TestTranscoder::NoEvent);
+ EXPECT_EQ(mTranscoder->popEvent(200000), TestTranscoder::Abandon(clientId, sessionId));
+ EXPECT_EQ(mTranscoder->popEvent(100000), TestTranscoder::Failed(clientId, sessionId));
+ EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kWatchdogTimeout);
+ // Should have created new transcoder.
+ EXPECT_EQ(mTranscoder->getGeneration(), generation);
+ EXPECT_EQ(mTranscoder.use_count(), 2);
+ }
+
+ void testPacerHelper(int numSubmits, int sessionDurationMs, int expectedSuccess) {
+ testPacerHelper(numSubmits, sessionDurationMs, expectedSuccess, mClientCallback0, {},
+ false /*pauseLastSuccessSession*/, true /*useRealCallingUid*/);
+ }
+
+ void testPacerHelperWithPause(int numSubmits, int sessionDurationMs, int expectedSuccess) {
+ testPacerHelper(numSubmits, sessionDurationMs, expectedSuccess, mClientCallback0, {},
+ true /*pauseLastSuccessSession*/, true /*useRealCallingUid*/);
+ }
+
+ void testPacerHelperWithMultipleUids(int numSubmits, int sessionDurationMs, int expectedSuccess,
+ const std::shared_ptr<TestClientCallback>& client,
+ const std::vector<int>& additionalClientUids) {
+ testPacerHelper(numSubmits, sessionDurationMs, expectedSuccess, client,
+ additionalClientUids, false /*pauseLastSuccessSession*/,
+ true /*useRealCallingUid*/);
+ }
+
+ void testPacerHelperWithSelfUid(int numSubmits, int sessionDurationMs, int expectedSuccess) {
+ testPacerHelper(numSubmits, sessionDurationMs, expectedSuccess, mClientCallback0, {},
+ false /*pauseLastSuccessSession*/, false /*useRealCallingUid*/);
+ }
+
+ void testPacerHelper(int numSubmits, int sessionDurationMs, int expectedSuccess,
+ const std::shared_ptr<TestClientCallback>& client,
+ const std::vector<int>& additionalClientUids, bool pauseLastSuccessSession,
+ bool useRealCallingUid) {
+ uid_t callingUid = useRealCallingUid ? ::getuid() : client->clientUid();
+ for (int i = 0; i < numSubmits; i++) {
+ mController->submit(client->clientId(), SESSION(i), callingUid, client->clientUid(),
+ mRealtimeRequest, client);
+ for (int additionalUid : additionalClientUids) {
+ mController->addClientUid(client->clientId(), SESSION(i), additionalUid);
+ }
+ }
+ for (int i = 0; i < expectedSuccess; i++) {
+ EXPECT_EQ(mTranscoder->popEvent(),
+ TestTranscoder::Start(client->clientId(), SESSION(i)));
+ if ((i == expectedSuccess - 1) && pauseLastSuccessSession) {
+ // Insert a pause of 3 sec to the last success running session
+ mController->onThrottlingStarted();
+ EXPECT_EQ(mTranscoder->popEvent(),
+ TestTranscoder::Pause(client->clientId(), SESSION(i)));
+ sleep(3);
+ mController->onThrottlingStopped();
+ EXPECT_EQ(mTranscoder->popEvent(),
+ TestTranscoder::Resume(client->clientId(), SESSION(i)));
+ }
+ usleep(sessionDurationMs * 1000);
+ // Test half of Finish and half of Error, both should be counted as burst runs.
+ if (i & 1) {
+ mController->onFinish(client->clientId(), SESSION(i));
+ EXPECT_EQ(mTranscoder->popEvent(),
+ TestTranscoder::Finished(client->clientId(), SESSION(i)));
+ } else {
+ mController->onError(client->clientId(), SESSION(i),
+ TranscodingErrorCode::kUnknown);
+ EXPECT_EQ(mTranscoder->popEvent(100000),
+ TestTranscoder::Failed(client->clientId(), SESSION(i)));
+ EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUnknown);
+ }
+ }
+ for (int i = expectedSuccess; i < numSubmits; i++) {
+ EXPECT_EQ(mTranscoder->popEvent(),
+ TestTranscoder::Failed(client->clientId(), SESSION(i)));
+ EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kDroppedByService);
+ }
+ }
+
+ std::shared_ptr<TestTranscoder> mTranscoder;
+ std::shared_ptr<TestUidPolicy> mUidPolicy;
+ std::shared_ptr<TestResourcePolicy> mResourcePolicy;
+ std::shared_ptr<TestThermalPolicy> mThermalPolicy;
+ std::shared_ptr<TranscodingSessionController> mController;
+ TranscodingRequestParcel mOfflineRequest;
+ TranscodingRequestParcel mRealtimeRequest;
+ std::shared_ptr<TestClientCallback> mClientCallback0;
+ std::shared_ptr<TestClientCallback> mClientCallback1;
+ std::shared_ptr<TestClientCallback> mClientCallback2;
+ std::shared_ptr<TestClientCallback> mClientCallback3;
+};
+
+TEST_F(TranscodingSessionControllerTest, TestSubmitSession) {
+ ALOGD("TestSubmitSession");
+
+ // Start with UID(1) on top.
+ mUidPolicy->setTop(UID(1));
+
+ // Submit offline session to CLIENT(0) in UID(0).
+ // Should start immediately (because this is the only session).
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mOfflineRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), 0));
+
+ // Submit real-time session to CLIENT(0).
+ // Should pause offline session and start new session, even if UID(0) is not on top.
+ mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+
+ // Submit real-time session to CLIENT(0), should be queued after the previous session.
+ mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Submit real-time session to CLIENT(1) in same uid, should be queued after the previous
+ // session.
+ mController->submit(CLIENT(1), SESSION(0), UID(1), UID(0), mRealtimeRequest, mClientCallback1);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Submit real-time session to CLIENT(2) in UID(1).
+ // Should pause previous session and start new session, because UID(1) is (has been) top.
+ mController->submit(CLIENT(2), SESSION(0), UID(2), UID(1), mRealtimeRequest, mClientCallback2);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(1)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0)));
+
+ // Submit offline session, shouldn't generate any event.
+ mController->submit(CLIENT(2), SESSION(1), UID(2), UID(1), mOfflineRequest, mClientCallback2);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Bring UID(0) to top.
+ mUidPolicy->setTop(UID(0));
+ // Should pause current session, and resume last session in UID(0).
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(1)));
+}
+
+TEST_F(TranscodingSessionControllerTest, TestCancelSession) {
+ ALOGD("TestCancelSession");
+
+ // Submit real-time session SESSION(0), should start immediately.
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit real-time session SESSION(1), should not start.
+ mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Submit offline session SESSION(2), should not start.
+ mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mOfflineRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Cancel queued real-time session.
+ // Cancel real-time session SESSION(1), should be cancelled.
+ EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(1)));
+
+ // Cancel queued offline session.
+ // Cancel offline session SESSION(2), should be cancelled.
+ EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(2)));
+
+ // Submit offline session SESSION(3), shouldn't cause any event.
+ mController->submit(CLIENT(0), SESSION(3), UID(0), UID(0), mOfflineRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Cancel running real-time session SESSION(0).
+ // - Should be stopped first then cancelled.
+ // - Should also start offline session SESSION(2) because real-time queue is empty.
+ EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(3)));
+
+ // Submit real-time session SESSION(4), offline SESSION(3) should pause and SESSION(4)
+ // should start.
+ mController->submit(CLIENT(0), SESSION(4), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(3)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(4)));
+
+ // Cancel paused SESSION(3). SESSION(3) should be stopped.
+ EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(3)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(3)));
+}
+
+TEST_F(TranscodingSessionControllerTest, TestCancelSessionWithMultipleUids) {
+ ALOGD("TestCancelSessionWithMultipleUids");
+ std::vector<int32_t> clientUids;
+
+ // Submit real-time session SESSION(0), should start immediately.
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit real-time session SESSION(1), should not start.
+ mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Submit offline session SESSION(2), should not start.
+ mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mOfflineRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // UID(1) moves to top.
+ mUidPolicy->setTop(UID(1));
+
+ // Add UID(1) to the offline SESSION(2), SESSION(2) should start and SESSION(0) should pause.
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(2), UID(1)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2)));
+
+ // Add UID(1) to SESSION(1) as well.
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(1), UID(1)));
+
+ // Cancel SESSION(2), should be cancelled and SESSION(1) should start.
+ EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(2)));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(2), &clientUids));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(2)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+
+ // Cancel SESSION(1), should be cancelled and SESSION(0) should resume.
+ EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(1)));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(1), &clientUids));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(1)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+}
+
+TEST_F(TranscodingSessionControllerTest, TestCancelAllSessionsForClient) {
+ // Submit real-time session SESSION(0), should start immediately.
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit real-time session SESSION(1), should not start.
+ mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Submit offline session SESSION(2), should not start.
+ mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mOfflineRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ std::vector<int32_t> clientUids;
+ // Make some more uids blocked on the sessions.
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(0), UID(1)));
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(1), UID(1)));
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(2), UID(1)));
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(0), &clientUids));
+ EXPECT_EQ(clientUids.size(), 2);
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(1), &clientUids));
+ EXPECT_EQ(clientUids.size(), 2);
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(2), &clientUids));
+ EXPECT_EQ(clientUids.size(), 1);
+
+ // Cancel all sessions for CLIENT(0) with -1.
+ // Expect SESSION(0) and SESSION(1) to be gone.
+ // Expect SESSION(2) still there with empty client uid list (only kept for offline) and start.
+ EXPECT_TRUE(mController->cancel(CLIENT(0), -1));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(0)));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(0), &clientUids));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(1), &clientUids));
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(2), &clientUids));
+ EXPECT_EQ(clientUids.size(), 0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2)));
+}
+
+TEST_F(TranscodingSessionControllerTest, TestFinishSession) {
+ ALOGD("TestFinishSession");
+
+ // Start with unspecified top UID.
+ // Finish without any sessions submitted, should be ignored.
+ mController->onFinish(CLIENT(0), SESSION(0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Submit offline session SESSION(0), should start immediately.
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mOfflineRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit real-time session SESSION(1), should pause offline session and start immediately.
+ mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+
+ // Submit real-time session SESSION(2), should not start.
+ mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Finish when the session never started, should be ignored.
+ mController->onFinish(CLIENT(0), SESSION(2));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // UID(1) moves to top.
+ mUidPolicy->setTop(UID(1));
+ // Submit real-time session to CLIENT(1) in UID(1), should pause previous session and start
+ // new session.
+ mController->submit(CLIENT(1), SESSION(0), UID(1), UID(1), mRealtimeRequest, mClientCallback1);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(1)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0)));
+
+ // Simulate Finish that arrived late, after pause issued by controller.
+ // Should still be propagated to client, but shouldn't trigger any new start.
+ mController->onFinish(CLIENT(0), SESSION(1));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(1)));
+
+ // Finish running real-time session, should start next real-time session in queue.
+ mController->onFinish(CLIENT(1), SESSION(0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(1), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2)));
+
+ // Finish running real-time session, should resume next session (offline session) in queue.
+ mController->onFinish(CLIENT(0), SESSION(2));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(2)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+
+ // Finish running offline session.
+ mController->onFinish(CLIENT(0), SESSION(0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(0)));
+
+ // Duplicate finish for last session, should be ignored.
+ mController->onFinish(CLIENT(0), SESSION(0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+}
+
+TEST_F(TranscodingSessionControllerTest, TestFinishSessionWithMultipleUids) {
+ ALOGD("TestFinishSessionWithMultipleUids");
+ std::vector<int32_t> clientUids;
+
+ // Start with unspecified top uid.
+ // Submit real-time session SESSION(0), should start immediately.
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit real-time session SESSION(1), should not start.
+ mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(1), UID(1)));
+
+ // Submit real-time session SESSION(2), should not start.
+ mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(2), UID(1)));
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(2), UID(2)));
+
+ // UID(1) moves to top.
+ mUidPolicy->setTop(UID(1));
+ // SESSION(0) should pause, SESSION(1) should start.
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+
+ // Finish SESSION(1), SESSION(2) (next in line for UID(1)) should start.
+ mController->onFinish(CLIENT(0), SESSION(1));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(1)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2)));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(1), &clientUids));
+
+ // Finish SESSION(2), SESSION(0) should resume.
+ mController->onFinish(CLIENT(0), SESSION(2));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(2)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(2), &clientUids));
+}
+
+TEST_F(TranscodingSessionControllerTest, TestFailSession) {
+ ALOGD("TestFailSession");
+
+ // Start with unspecified top UID.
+ // Fail without any sessions submitted, should be ignored.
+ mController->onError(CLIENT(0), SESSION(0), TranscodingErrorCode::kUnknown);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Submit offline session SESSION(0), should start immediately.
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mOfflineRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit real-time session SESSION(1), should pause offline session and start immediately.
+ mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+
+ // Submit real-time session SESSION(2), should not start.
+ mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Fail when the session never started, should be ignored.
+ mController->onError(CLIENT(0), SESSION(2), TranscodingErrorCode::kUnknown);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // UID(1) moves to top.
+ mUidPolicy->setTop(UID(1));
+ // Submit real-time session to CLIENT(1) in UID(1), should pause previous session and start
+ // new session.
+ mController->submit(CLIENT(1), SESSION(0), UID(1), UID(1), mRealtimeRequest, mClientCallback1);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(1)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0)));
+
+ // Simulate Fail that arrived late, after pause issued by controller.
+ // Should still be propagated to client, but shouldn't trigger any new start.
+ mController->onError(CLIENT(0), SESSION(1), TranscodingErrorCode::kUnknown);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(1)));
+ EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUnknown);
+
+ // Fail running real-time session, should start next real-time session in queue.
+ mController->onError(CLIENT(1), SESSION(0), TranscodingErrorCode::kUnknown);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(1), SESSION(0)));
+ EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUnknown);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2)));
+
+ // Fail running real-time session, should resume next session (offline session) in queue.
+ mController->onError(CLIENT(0), SESSION(2), TranscodingErrorCode::kUnknown);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(2)));
+ EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUnknown);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+
+ // Fail running offline session, and test error code propagation.
+ mController->onError(CLIENT(0), SESSION(0), TranscodingErrorCode::kInvalidOperation);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kInvalidOperation);
+
+ // Duplicate fail for last session, should be ignored.
+ mController->onError(CLIENT(0), SESSION(0), TranscodingErrorCode::kUnknown);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+}
+
+TEST_F(TranscodingSessionControllerTest, TestFailSessionWithMultipleUids) {
+ ALOGD("TestFailSessionWithMultipleUids");
+ std::vector<int32_t> clientUids;
+
+ // Start with unspecified top uid.
+ // Submit real-time session SESSION(0), should start immediately.
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit real-time session SESSION(1), should not start.
+ mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(1), UID(1)));
+
+ // Submit real-time session SESSION(2), should not start.
+ mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // UID(1) moves to top.
+ mUidPolicy->setTop(UID(1));
+ // SESSION(0) should pause, SESSION(1) should start.
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+
+ // Add UID(1) and UID(2) to SESSION(2).
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(2), UID(1)));
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(2), UID(2)));
+
+ // Fail SESSION(1), SESSION(2) (next in line for UID(1)) should start.
+ mController->onError(CLIENT(0), SESSION(1), TranscodingErrorCode::kUnknown);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(1)));
+ EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUnknown);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2)));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(1), &clientUids));
+
+ // Fail SESSION(2), SESSION(0) should resume.
+ mController->onError(CLIENT(0), SESSION(2), TranscodingErrorCode::kInvalidOperation);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(2)));
+ EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kInvalidOperation);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(2), &clientUids));
+}
+
+TEST_F(TranscodingSessionControllerTest, TestTopUidChanged) {
+ ALOGD("TestTopUidChanged");
+
+ // Start with unspecified top UID.
+ // Submit real-time session to CLIENT(0), session should start immediately.
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit offline session to CLIENT(0), should not start.
+ mController->submit(CLIENT(1), SESSION(0), UID(1), UID(0), mOfflineRequest, mClientCallback1);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Move UID(1) to top.
+ mUidPolicy->setTop(UID(1));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Submit real-time session to CLIENT(2) in different uid UID(1).
+ // Should pause previous session and start new session.
+ mController->submit(CLIENT(2), SESSION(0), UID(2), UID(1), mRealtimeRequest, mClientCallback2);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0)));
+
+ // Bring UID(0) back to top.
+ mUidPolicy->setTop(UID(0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+
+ // Bring invalid uid to top.
+ mUidPolicy->setTop(kInvalidUid);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Finish session, next real-time session should resume.
+ mController->onFinish(CLIENT(0), SESSION(0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0)));
+
+ // Finish session, offline session should start.
+ mController->onFinish(CLIENT(2), SESSION(0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(2), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0)));
+}
+
+TEST_F(TranscodingSessionControllerTest, TestTopUidChangedMultipleUids) {
+ ALOGD("TestTopUidChangedMultipleUids");
+
+ // Start with unspecified top UID.
+ // Submit real-time session to CLIENT(0), session should start immediately.
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit offline session to CLIENT(0), should not start.
+ mController->submit(CLIENT(1), SESSION(0), UID(1), UID(0), mOfflineRequest, mClientCallback1);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Bring UID(1) to top.
+ mUidPolicy->setTop(UID(1));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Add UID(1) to SESSION(0), SESSION(0) should continue to run
+ // (no pause&resume of the same session).
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(0), UID(1)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Bring UID(0) back to top, SESSION(0) should continue to run
+ // (no pause&resume of the same session).
+ mUidPolicy->setTop(UID(0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Bring UID(2) to top.
+ mUidPolicy->setTop(UID(2));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ // Add UID(2) to the offline session, it should be started.
+ EXPECT_TRUE(mController->addClientUid(CLIENT(1), SESSION(0), UID(2)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0)));
+
+ // ADD UID(3) to SESSION(0).
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(0), UID(3)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ // Bring UID(3) to top, SESSION(0) should resume.
+ mUidPolicy->setTop(UID(3));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(1), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+
+ // Now make UID(2) also blocked on CLIENT(0), SESSION(0).
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(0), UID(2)));
+
+ // Bring UID(2) back to top, CLIENT(0), SESSION(0) should continue to run (even if it's
+ // added to UID(2)'s queue later than CLIENT(1)'s SESSION(0)).
+ mUidPolicy->setTop(UID(2));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+}
+
+TEST_F(TranscodingSessionControllerTest, TestTopUidSetChanged) {
+ ALOGD("TestTopUidSetChanged");
+
+ // Start with unspecified top UID.
+ // Submit real-time session to CLIENT(0), session should start immediately.
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit offline session to CLIENT(0), should not start.
+ mController->submit(CLIENT(1), SESSION(0), UID(1), UID(0), mOfflineRequest, mClientCallback1);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Set UID(0), UID(1) to top set.
+ // UID(0) should continue to run.
+ mUidPolicy->setTop({UID(0), UID(1)});
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Submit real-time session to CLIENT(2) in different uid UID(1).
+ // UID(0) should pause and UID(1) should start.
+ mController->submit(CLIENT(2), SESSION(0), UID(2), UID(1), mRealtimeRequest, mClientCallback2);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0)));
+
+ // Remove UID(0) from top set, and only leave UID(1) in the set.
+ // UID(1) should continue to run.
+ mUidPolicy->setTop(UID(1));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Set UID(0), UID(2) to top set.
+ // UID(1) should continue to run.
+ mUidPolicy->setTop({UID(1), UID(2)});
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Bring UID(0) back to top.
+ mUidPolicy->setTop(UID(0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+
+ // Bring invalid uid to top.
+ mUidPolicy->setTop(kInvalidUid);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Finish session, next real-time session from UID(1) should resume, even if UID(1)
+ // no longer top.
+ mController->onFinish(CLIENT(0), SESSION(0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0)));
+
+ // Finish session, offline session should start.
+ mController->onFinish(CLIENT(2), SESSION(0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(2), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0)));
+}
+
+TEST_F(TranscodingSessionControllerTest, TestUidGone) {
+ ALOGD("TestUidGone");
+
+ mUidPolicy->setTop(UID(0));
+ // Start with unspecified top UID.
+ // Submit real-time sessions to CLIENT(0), session should start immediately.
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(1), UID(1)));
+
+ // Submit real-time session to CLIENT(1), should not start.
+ mController->submit(CLIENT(1), SESSION(0), UID(1), UID(1), mOfflineRequest, mClientCallback1);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ EXPECT_TRUE(mController->addClientUid(CLIENT(1), SESSION(0), UID(1)));
+
+ // Tell the controller that UID(0) is gone.
+ mUidPolicy->setTop(UID(1));
+ // CLIENT(0)'s SESSION(1) should start, SESSION(0) should be cancelled.
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+ mController->onUidGone(UID(0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUidGoneCancelled);
+
+ std::vector<int32_t> clientUids;
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(0), &clientUids));
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(1), &clientUids));
+ EXPECT_EQ(clientUids.size(), 1);
+ EXPECT_EQ(clientUids[0], UID(1));
+
+ // Tell the controller that UID(1) is gone too.
+ mController->onUidGone(UID(1));
+ // CLIENT(1)'s SESSION(0) should start, CLIENT(0)'s SESSION(1) should be cancelled.
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(1)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(1)));
+ EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUidGoneCancelled);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0)));
+ // CLIENT(1) SESSION(0) should not have any client uids as it's only kept for offline.
+ EXPECT_TRUE(mController->getClientUids(CLIENT(1), SESSION(0), &clientUids));
+ EXPECT_EQ(clientUids.size(), 0);
+}
+
+TEST_F(TranscodingSessionControllerTest, TestAddGetClientUids) {
+ ALOGD("TestAddGetClientUids");
+
+ // Add/get client uids with non-existent session, should fail.
+ std::vector<int32_t> clientUids;
+ uid_t ownUid = ::getuid();
+ EXPECT_FALSE(mController->addClientUid(CLIENT(0), SESSION(0), ownUid));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(0), &clientUids));
+
+ // Submit a real-time request.
+ EXPECT_TRUE(mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest,
+ mClientCallback0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Should have own uid in client uids.
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(0), &clientUids));
+ EXPECT_EQ(clientUids.size(), 1);
+ EXPECT_EQ(clientUids[0], UID(0));
+
+ // Add UID(0) again should fail.
+ EXPECT_FALSE(mController->addClientUid(CLIENT(0), SESSION(0), UID(0)));
+
+ // Add own uid should succeed.
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(0), ownUid));
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(0), &clientUids));
+ std::unordered_set<uid_t> uidSet;
+ uidSet.insert(clientUids.begin(), clientUids.end());
+ EXPECT_EQ(uidSet.size(), 2);
+ EXPECT_EQ(uidSet.count(UID(0)), 1);
+ EXPECT_EQ(uidSet.count(ownUid), 1);
+
+ // Submit an offline request.
+ EXPECT_TRUE(mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mOfflineRequest,
+ mClientCallback0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Should not have own uid in client uids.
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(1), &clientUids));
+ EXPECT_EQ(clientUids.size(), 0);
+
+ // Move UID(1) to top.
+ mUidPolicy->setTop(UID(1));
+ // Add UID(1) to offline session, offline session should start and SESSION(0) should pause.
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(1), UID(1)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+}
+
+/* Test resource lost without thermal throttling */
+TEST_F(TranscodingSessionControllerTest, TestResourceLost) {
+ ALOGD("TestResourceLost");
+
+ // Start with unspecified top UID.
+ // Submit real-time session to CLIENT(0), session should start immediately.
+ mRealtimeRequest.clientPid = PID(0);
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit offline session to CLIENT(0), should not start.
+ mOfflineRequest.clientPid = PID(0);
+ mController->submit(CLIENT(1), SESSION(0), UID(1), UID(0), mOfflineRequest, mClientCallback1);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Move UID(1) to top.
+ mUidPolicy->setTop(UID(1));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Submit real-time session to CLIENT(2) in different uid UID(1).
+ // Should pause previous session and start new session.
+ mRealtimeRequest.clientPid = PID(1);
+ mController->submit(CLIENT(2), SESSION(0), UID(2), UID(1), mRealtimeRequest, mClientCallback2);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0)));
+
+ // Test 0: No call into ResourcePolicy if resource lost is from a non-running
+ // or non-existent session.
+ mController->onResourceLost(CLIENT(0), SESSION(0));
+ EXPECT_EQ(mResourcePolicy->getPid(), kInvalidPid);
+ mController->onResourceLost(CLIENT(3), SESSION(0));
+ EXPECT_EQ(mResourcePolicy->getPid(), kInvalidPid);
+
+ // Test 1: No queue change during resource loss.
+ // Signal resource lost.
+ mController->onResourceLost(CLIENT(2), SESSION(0));
+ EXPECT_EQ(mResourcePolicy->getPid(), PID(1));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Signal resource available, CLIENT(2) should resume.
+ mController->onResourceAvailable();
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0)));
+
+ // Test 2: Change of queue order during resource loss.
+ // Signal resource lost.
+ mController->onResourceLost(CLIENT(2), SESSION(0));
+ EXPECT_EQ(mResourcePolicy->getPid(), PID(1));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Move UID(0) back to top, should have no resume due to no resource.
+ mUidPolicy->setTop(UID(0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Signal resource available, CLIENT(0) should resume.
+ mController->onResourceAvailable();
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+
+ // Test 3:
+ mController->onResourceLost(CLIENT(0), SESSION(0));
+ EXPECT_EQ(mResourcePolicy->getPid(), PID(0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ // Cancel the paused top session during resource lost.
+ EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(0)));
+ // Signal resource available, CLIENT(2)'s session should start.
+ mController->onResourceAvailable();
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0)));
+
+ // Test 4: Adding new queue during resource loss.
+ // Signal resource lost.
+ mController->onResourceLost(CLIENT(2), SESSION(0));
+ EXPECT_EQ(mResourcePolicy->getPid(), PID(1));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Move UID(2) to top.
+ mUidPolicy->setTop(UID(2));
+
+ // Submit real-time session to CLIENT(3) in UID(2), session shouldn't start due to no resource.
+ mRealtimeRequest.clientPid = PID(2);
+ mController->submit(CLIENT(3), SESSION(0), UID(3), UID(2), mRealtimeRequest, mClientCallback3);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Signal resource available, CLIENT(3)'s session should start.
+ mController->onResourceAvailable();
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(3), SESSION(0)));
+}
+
+/* Test thermal throttling without resource lost */
+TEST_F(TranscodingSessionControllerTest, TestThermalCallback) {
+ ALOGD("TestThermalCallback");
+
+ // Start with unspecified top UID.
+ // Submit real-time session to CLIENT(0), session should start immediately.
+ mRealtimeRequest.clientPid = PID(0);
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit offline session to CLIENT(0), should not start.
+ mOfflineRequest.clientPid = PID(0);
+ mController->submit(CLIENT(1), SESSION(0), UID(1), UID(0), mOfflineRequest, mClientCallback1);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Move UID(1) to top.
+ mUidPolicy->setTop(UID(1));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Submit real-time session to CLIENT(2) in different uid UID(1).
+ // Should pause previous session and start new session.
+ mRealtimeRequest.clientPid = PID(1);
+ mController->submit(CLIENT(2), SESSION(0), UID(2), UID(1), mRealtimeRequest, mClientCallback2);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0)));
+
+ // Test 0: Basic case, no queue change during throttling, top session should pause/resume
+ // with throttling.
+ mController->onThrottlingStarted();
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), SESSION(0)));
+ mController->onThrottlingStopped();
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0)));
+
+ // Test 1: Change of queue order during thermal throttling, when throttling stops,
+ // new top session should resume.
+ mController->onThrottlingStarted();
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), SESSION(0)));
+ mUidPolicy->setTop(UID(0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ mController->onThrottlingStopped();
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+
+ // Test 2: Cancel session during throttling, when throttling stops, new top
+ // session should resume.
+ mController->onThrottlingStarted();
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ // Cancel the paused top session during throttling.
+ EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(0)));
+ // Throttling stops, CLIENT(2)'s session should start.
+ mController->onThrottlingStopped();
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0)));
+
+ // Test 3: Add new queue during throttling, when throttling stops, new top
+ // session should resume.
+ mController->onThrottlingStarted();
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), SESSION(0)));
+ // Move UID(2) to top.
+ mUidPolicy->setTop(UID(2));
+ // Submit real-time session to CLIENT(3) in UID(2), session shouldn't start during throttling.
+ mRealtimeRequest.clientPid = PID(2);
+ mController->submit(CLIENT(3), SESSION(0), UID(3), UID(2), mRealtimeRequest, mClientCallback3);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ // Throttling stops, CLIENT(3)'s session should start.
+ mController->onThrottlingStopped();
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(3), SESSION(0)));
+}
+
+/* Test resource lost and thermal throttling happening simultaneously */
+TEST_F(TranscodingSessionControllerTest, TestResourceLostAndThermalCallback) {
+ ALOGD("TestResourceLostAndThermalCallback");
+
+ // Start with unspecified top UID.
+ // Submit real-time session to CLIENT(0), session should start immediately.
+ mRealtimeRequest.clientPid = PID(0);
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit offline session to CLIENT(0), should not start.
+ mOfflineRequest.clientPid = PID(0);
+ mController->submit(CLIENT(1), SESSION(0), UID(1), UID(0), mOfflineRequest, mClientCallback1);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Move UID(1) to top.
+ mUidPolicy->setTop(UID(1));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Submit real-time session to CLIENT(2) in different uid UID(1).
+ // Should pause previous session and start new session.
+ mRealtimeRequest.clientPid = PID(1);
+ mController->submit(CLIENT(2), SESSION(0), UID(2), UID(1), mRealtimeRequest, mClientCallback2);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0)));
+
+ // Test 0: Resource lost during throttling.
+ // Throttling starts, top session should pause.
+ mController->onThrottlingStarted();
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), SESSION(0)));
+ // Signal resource lost, this should get ignored because the session is now paused.
+ mController->onResourceLost(CLIENT(2), SESSION(0));
+ EXPECT_EQ(mResourcePolicy->getPid(), kInvalidPid);
+ // Signal resource available, CLIENT(2) shouldn't resume.
+ mController->onResourceAvailable();
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ // Throttling ends, top session should resume.
+ mController->onThrottlingStopped();
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0)));
+
+ // Test 1: Throttling during resource lost.
+ mController->onResourceLost(CLIENT(2), SESSION(0));
+ EXPECT_EQ(mResourcePolicy->getPid(), PID(1));
+ mController->onThrottlingStarted();
+ mController->onThrottlingStopped();
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ mController->onResourceAvailable();
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0)));
+
+ // Test 2: Interleaving resource lost and throttling.
+ mController->onResourceLost(CLIENT(2), SESSION(0));
+ EXPECT_EQ(mResourcePolicy->getPid(), PID(1));
+ mController->onThrottlingStarted();
+ mController->onResourceAvailable();
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ mController->onThrottlingStopped();
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0)));
+}
+
+TEST_F(TranscodingSessionControllerTest, TestTranscoderWatchdogNoHeartbeat) {
+ ALOGD("TestTranscoderWatchdogTimeout");
+
+ // Submit session to CLIENT(0) in UID(0).
+ // Should start immediately (because this is the only session).
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Test 1: If not sending keep-alive at all, timeout after 3 seconds.
+ expectTimeout(CLIENT(0), SESSION(0), 2);
+}
+
+TEST_F(TranscodingSessionControllerTest, TestTranscoderWatchdogHeartbeat) {
+ // Test 2: No timeout as long as keep-alive coming; timeout after keep-alive stops.
+ mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+
+ for (int i = 0; i < 5; i++) {
+ EXPECT_EQ(mTranscoder->popEvent(1000000), TestTranscoder::NoEvent);
+ mController->onHeartBeat(CLIENT(0), SESSION(1));
+ }
+ expectTimeout(CLIENT(0), SESSION(1), 2);
+}
+
+TEST_F(TranscodingSessionControllerTest, TestTranscoderWatchdogDuringPause) {
+ int expectedGen = 2;
+
+ // Test 3a: No timeout for paused session even if no keep-alive is sent.
+ mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mOfflineRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2)));
+ // Trigger a pause by sending a resource lost.
+ mController->onResourceLost(CLIENT(0), SESSION(2));
+ EXPECT_EQ(mTranscoder->popEvent(3100000), TestTranscoder::NoEvent);
+ mController->onResourceAvailable();
+ EXPECT_EQ(mTranscoder->popEvent(100000), TestTranscoder::Resume(CLIENT(0), SESSION(2)));
+ expectTimeout(CLIENT(0), SESSION(2), expectedGen++);
+
+ // Test 3b: No timeout for paused session even if no keep-alive is sent.
+ mController->submit(CLIENT(0), SESSION(3), UID(0), UID(0), mOfflineRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(3)));
+ // Let the session run almost to timeout, to test timeout reset after pause.
+ EXPECT_EQ(mTranscoder->popEvent(2900000), TestTranscoder::NoEvent);
+ // Trigger a pause by submitting a higher-priority request.
+ mController->submit(CLIENT(0), SESSION(4), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(3)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(4)));
+ // Finish the higher-priority session, lower-priority session should resume,
+ // and the timeout should reset to full value.
+ mController->onFinish(CLIENT(0), SESSION(4));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(4)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(3)));
+ expectTimeout(CLIENT(0), SESSION(3), expectedGen++);
+}
+
+TEST_F(TranscodingSessionControllerTest, TestTranscoderPacerOverCountOnly) {
+ ALOGD("TestTranscoderPacerOverCountOnly");
+ testPacerHelper(12 /*numSubmits*/, 100 /*sessionDurationMs*/, 12 /*expectedSuccess*/);
+}
+
+TEST_F(TranscodingSessionControllerTest, TestTranscoderPacerOverTimeOnly) {
+ ALOGD("TestTranscoderPacerOverTimeOnly");
+ testPacerHelper(5 /*numSubmits*/, 1000 /*sessionDurationMs*/, 5 /*expectedSuccess*/);
+}
+
+TEST_F(TranscodingSessionControllerTest, TestTranscoderPacerOverQuota) {
+ ALOGD("TestTranscoderPacerOverQuota");
+ testPacerHelper(12 /*numSubmits*/, 400 /*sessionDurationMs*/, 10 /*expectedSuccess*/);
+}
+
+TEST_F(TranscodingSessionControllerTest, TestTranscoderPacerWithPause) {
+ ALOGD("TestTranscoderPacerDuringPause");
+ testPacerHelperWithPause(12 /*numSubmits*/, 400 /*sessionDurationMs*/, 10 /*expectedSuccess*/);
+}
+
+/*
+ * Test the case where multiple client uids request the same session. Session should only
+ * be dropped when all clients are over quota.
+ */
+TEST_F(TranscodingSessionControllerTest, TestTranscoderPacerMultipleUids) {
+ ALOGD("TestTranscoderPacerMultipleUids");
+ // First, run mClientCallback0 to the point of no quota.
+ testPacerHelperWithMultipleUids(12 /*numSubmits*/, 400 /*sessionDurationMs*/,
+ 10 /*expectedSuccess*/, mClientCallback0, {});
+ // Make UID(0) block on Client1's sessions too, Client1's quota should not be affected.
+ testPacerHelperWithMultipleUids(12 /*numSubmits*/, 400 /*sessionDurationMs*/,
+ 10 /*expectedSuccess*/, mClientCallback1, {UID(0)});
+ // Make UID(10) block on Client2's sessions. We expect to see 11 succeeds (instead of 10),
+ // because the addClientUid() is called after the submit, and first session is already
+ // started by the time UID(10) is added. UID(10) allowed us to run the 11th session,
+ // after that both UID(10) and UID(2) are out of quota.
+ testPacerHelperWithMultipleUids(12 /*numSubmits*/, 400 /*sessionDurationMs*/,
+ 11 /*expectedSuccess*/, mClientCallback2, {UID(10)});
+}
+
+/*
+ * Use same uid for clientUid and callingUid, should not be limited by quota.
+ */
+TEST_F(TranscodingSessionControllerTest, TestTranscoderPacerSelfUid) {
+ ALOGD("TestTranscoderPacerSelfUid");
+ testPacerHelperWithSelfUid(12 /*numSubmits*/, 400 /*sessionDurationMs*/,
+ 12 /*expectedSuccess*/);
+}
+
+} // namespace android
diff --git a/media/libmediatranscoding/tests/assets/TranscodingTestAssets/Video_4K_HEVC_10Frames_Audio.mp4 b/media/libmediatranscoding/tests/assets/TranscodingTestAssets/Video_4K_HEVC_10Frames_Audio.mp4
new file mode 100644
index 0000000..b652dd9
--- /dev/null
+++ b/media/libmediatranscoding/tests/assets/TranscodingTestAssets/Video_4K_HEVC_10Frames_Audio.mp4
Binary files differ
diff --git a/media/libmediatranscoding/tests/assets/TranscodingTestAssets/backyard_hevc_1920x1080_20Mbps.mp4 b/media/libmediatranscoding/tests/assets/TranscodingTestAssets/backyard_hevc_1920x1080_20Mbps.mp4
new file mode 100644
index 0000000..80d1ec3
--- /dev/null
+++ b/media/libmediatranscoding/tests/assets/TranscodingTestAssets/backyard_hevc_1920x1080_20Mbps.mp4
Binary files differ
diff --git a/media/libmediatranscoding/tests/assets/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4 b/media/libmediatranscoding/tests/assets/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4
new file mode 100644
index 0000000..ef7e1b7
--- /dev/null
+++ b/media/libmediatranscoding/tests/assets/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4
Binary files differ
diff --git a/media/libmediatranscoding/tests/assets/TranscodingTestAssets/desk_hevc_1920x1080_aac_48KHz_rot90.mp4 b/media/libmediatranscoding/tests/assets/TranscodingTestAssets/desk_hevc_1920x1080_aac_48KHz_rot90.mp4
new file mode 100644
index 0000000..df42a15
--- /dev/null
+++ b/media/libmediatranscoding/tests/assets/TranscodingTestAssets/desk_hevc_1920x1080_aac_48KHz_rot90.mp4
Binary files differ
diff --git a/media/libmediatranscoding/tests/assets/TranscodingTestAssets/jets_hevc_1280x720_20Mbps.mp4 b/media/libmediatranscoding/tests/assets/TranscodingTestAssets/jets_hevc_1280x720_20Mbps.mp4
new file mode 100644
index 0000000..7794b99
--- /dev/null
+++ b/media/libmediatranscoding/tests/assets/TranscodingTestAssets/jets_hevc_1280x720_20Mbps.mp4
Binary files differ
diff --git a/media/libmediatranscoding/tests/assets/TranscodingTestAssets/longtest_15s.mp4 b/media/libmediatranscoding/tests/assets/TranscodingTestAssets/longtest_15s.mp4
new file mode 100644
index 0000000..b50d8e4
--- /dev/null
+++ b/media/libmediatranscoding/tests/assets/TranscodingTestAssets/longtest_15s.mp4
Binary files differ
diff --git a/media/libmediatranscoding/tests/assets/TranscodingTestAssets/plex_hevc_3840x2160_12Mbps.mp4 b/media/libmediatranscoding/tests/assets/TranscodingTestAssets/plex_hevc_3840x2160_12Mbps.mp4
new file mode 100644
index 0000000..92dda3b
--- /dev/null
+++ b/media/libmediatranscoding/tests/assets/TranscodingTestAssets/plex_hevc_3840x2160_12Mbps.mp4
Binary files differ
diff --git a/media/libmediatranscoding/tests/assets/TranscodingTestAssets/plex_hevc_3840x2160_20Mbps.mp4 b/media/libmediatranscoding/tests/assets/TranscodingTestAssets/plex_hevc_3840x2160_20Mbps.mp4
new file mode 100644
index 0000000..2fe37bd
--- /dev/null
+++ b/media/libmediatranscoding/tests/assets/TranscodingTestAssets/plex_hevc_3840x2160_20Mbps.mp4
Binary files differ
diff --git a/media/libmediatranscoding/tests/assets/TranscodingTestAssets/video_1280x720_hevc_hdr10_static_3mbps.mp4 b/media/libmediatranscoding/tests/assets/TranscodingTestAssets/video_1280x720_hevc_hdr10_static_3mbps.mp4
new file mode 100644
index 0000000..17150d4
--- /dev/null
+++ b/media/libmediatranscoding/tests/assets/TranscodingTestAssets/video_1280x720_hevc_hdr10_static_3mbps.mp4
Binary files differ
diff --git a/media/libmediatranscoding/tests/build_and_run_all_unit_tests.sh b/media/libmediatranscoding/tests/build_and_run_all_unit_tests.sh
index d8e4830..3cbf1dd 100644
--- a/media/libmediatranscoding/tests/build_and_run_all_unit_tests.sh
+++ b/media/libmediatranscoding/tests/build_and_run_all_unit_tests.sh
@@ -3,24 +3,36 @@
# Run tests in this directory.
#
-if [ -z "$ANDROID_BUILD_TOP" ]; then
- echo "Android build environment not set"
- exit -1
+if [ "$SYNC_FINISHED" != true ]; then
+ if [ -z "$ANDROID_BUILD_TOP" ]; then
+ echo "Android build environment not set"
+ exit -1
+ fi
+
+ # ensure we have mm
+ . $ANDROID_BUILD_TOP/build/envsetup.sh
+
+ mm
+
+ echo "waiting for device"
+
+ adb root && adb wait-for-device remount && adb sync
fi
-# ensure we have mm
-. $ANDROID_BUILD_TOP/build/envsetup.sh
-
-mm
-
-echo "waiting for device"
-
-adb root && adb wait-for-device remount && adb sync
-
echo "========================================"
echo "testing TranscodingClientManager"
-adb shell /data/nativetest64/TranscodingClientManager_tests/TranscodingClientManager_tests
+#adb shell /data/nativetest64/TranscodingClientManager_tests/TranscodingClientManager_tests
+adb shell /data/nativetest/TranscodingClientManager_tests/TranscodingClientManager_tests
echo "testing AdjustableMaxPriorityQueue"
-adb shell /data/nativetest64/AdjustableMaxPriorityQueue_tests/AdjustableMaxPriorityQueue_tests
+#adb shell /data/nativetest64/AdjustableMaxPriorityQueue_tests/AdjustableMaxPriorityQueue_tests
+adb shell /data/nativetest/AdjustableMaxPriorityQueue_tests/AdjustableMaxPriorityQueue_tests
+
+echo "testing TranscodingSessionController"
+#adb shell /data/nativetest64/TranscodingSessionController_tests/TranscodingSessionController_tests
+adb shell /data/nativetest/TranscodingSessionController_tests/TranscodingSessionController_tests
+
+echo "testing TranscodingLogger"
+#adb shell /data/nativetest64/TranscodingLogger_tests/TranscodingLogger_tests
+adb shell /data/nativetest/TranscodingLogger_tests/TranscodingLogger_tests
diff --git a/media/libmediatranscoding/tests/push_assets.sh b/media/libmediatranscoding/tests/push_assets.sh
new file mode 100755
index 0000000..cc71514
--- /dev/null
+++ b/media/libmediatranscoding/tests/push_assets.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+#
+# Pushes the assets to the /data/local/tmp.
+#
+
+if [ "$SYNC_FINISHED" != true ]; then
+ 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
+fi
+
+echo "Copying files to device"
+
+adb shell mkdir -p /data/local/tmp/TranscodingTestAssets
+
+FILES=$ANDROID_BUILD_TOP/frameworks/av/media/libmediatranscoding/tests/assets/TranscodingTestAssets/*
+for file in $FILES
+do
+adb push --sync $file /data/local/tmp/TranscodingTestAssets
+done
+
+echo "Copy done"
diff --git a/media/libmediatranscoding/transcoder/Android.bp b/media/libmediatranscoding/transcoder/Android.bp
new file mode 100644
index 0000000..ccb2ec2
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/Android.bp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_defaults {
+ name: "mediatranscoder_defaults",
+
+ srcs: [
+ "MediaSampleQueue.cpp",
+ "MediaSampleReaderNDK.cpp",
+ "MediaSampleWriter.cpp",
+ "MediaTrackTranscoder.cpp",
+ "MediaTranscoder.cpp",
+ "NdkCommon.cpp",
+ "PassthroughTrackTranscoder.cpp",
+ "VideoTrackTranscoder.cpp",
+ ],
+
+ min_sdk_version: "29",
+ apex_available: [
+ "com.android.media",
+ "test_com.android.media",
+ ],
+
+ //header_libs: [ "libarect_headers", "libarect_headers_for_ndk" ],
+ static_libs: [ "libarect" ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libmediandk",
+ "libnativewindow",
+ "libutils",
+ "libbinder_ndk",
+ ],
+
+ export_include_dirs: [
+ "include",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wformat",
+ "-Wno-error=deprecated-declarations",
+ "-Wthread-safety",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ cfi: true,
+ },
+}
+
+cc_library {
+ name: "libmediatranscoder",
+ defaults: ["mediatranscoder_defaults"],
+}
diff --git a/media/libmediatranscoding/transcoder/MediaSampleQueue.cpp b/media/libmediatranscoding/transcoder/MediaSampleQueue.cpp
new file mode 100644
index 0000000..b085c98
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/MediaSampleQueue.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "MediaSampleQueue"
+
+#include <android-base/logging.h>
+#include <media/MediaSampleQueue.h>
+
+namespace android {
+
+bool MediaSampleQueue::enqueue(const std::shared_ptr<MediaSample>& sample) {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ if (!mAborted) {
+ mSampleQueue.push(sample);
+ mCondition.notify_one();
+ }
+ return mAborted;
+}
+
+// Unfortunately std::unique_lock is incompatible with -Wthread-safety
+bool MediaSampleQueue::dequeue(std::shared_ptr<MediaSample>* sample) NO_THREAD_SAFETY_ANALYSIS {
+ std::unique_lock<std::mutex> lock(mMutex);
+ while (mSampleQueue.empty() && !mAborted) {
+ mCondition.wait(lock);
+ }
+
+ if (!mAborted) {
+ if (sample != nullptr) {
+ *sample = mSampleQueue.front();
+ }
+ mSampleQueue.pop();
+ }
+ return mAborted;
+}
+
+bool MediaSampleQueue::isEmpty() {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ return mSampleQueue.empty();
+}
+
+void MediaSampleQueue::abort() {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ // Clear the queue and notify consumers.
+ std::queue<std::shared_ptr<MediaSample>> empty = {};
+ std::swap(mSampleQueue, empty);
+ mAborted = true;
+ mCondition.notify_all();
+}
+} // namespace android
\ No newline at end of file
diff --git a/media/libmediatranscoding/transcoder/MediaSampleReaderNDK.cpp b/media/libmediatranscoding/transcoder/MediaSampleReaderNDK.cpp
new file mode 100644
index 0000000..8d36e31
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/MediaSampleReaderNDK.cpp
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "MediaSampleReader"
+
+#include <android-base/logging.h>
+#include <media/MediaSampleReaderNDK.h>
+
+#include <algorithm>
+#include <cmath>
+
+namespace android {
+
+// Check that the extractor sample flags have the expected NDK meaning.
+static_assert(SAMPLE_FLAG_SYNC_SAMPLE == AMEDIAEXTRACTOR_SAMPLE_FLAG_SYNC,
+ "Sample flag mismatch: SYNC_SAMPLE");
+
+// static
+std::shared_ptr<MediaSampleReader> MediaSampleReaderNDK::createFromFd(int fd, size_t offset,
+ size_t size) {
+ AMediaExtractor* extractor = AMediaExtractor_new();
+ if (extractor == nullptr) {
+ LOG(ERROR) << "Unable to allocate AMediaExtractor";
+ return nullptr;
+ }
+
+ media_status_t status = AMediaExtractor_setDataSourceFd(extractor, fd, offset, size);
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "AMediaExtractor_setDataSourceFd returned error: " << status;
+ AMediaExtractor_delete(extractor);
+ return nullptr;
+ }
+
+ auto sampleReader = std::shared_ptr<MediaSampleReaderNDK>(new MediaSampleReaderNDK(extractor));
+ return sampleReader;
+}
+
+MediaSampleReaderNDK::MediaSampleReaderNDK(AMediaExtractor* extractor)
+ : mExtractor(extractor), mTrackCount(AMediaExtractor_getTrackCount(mExtractor)) {
+ if (mTrackCount > 0) {
+ mTrackCursors.resize(mTrackCount);
+ }
+}
+
+MediaSampleReaderNDK::~MediaSampleReaderNDK() {
+ if (mExtractor != nullptr) {
+ AMediaExtractor_delete(mExtractor);
+ }
+}
+
+void MediaSampleReaderNDK::advanceTrack_l(int trackIndex) {
+ if (!mEnforceSequentialAccess) {
+ // Note: Positioning the extractor before advancing the track is needed for two reasons:
+ // 1. To enable multiple advances without explicitly letting the extractor catch up.
+ // 2. To prevent the extractor from being farther than "next".
+ (void)moveToTrack_l(trackIndex);
+ }
+
+ SampleCursor& cursor = mTrackCursors[trackIndex];
+ cursor.previous = cursor.current;
+ cursor.current = cursor.next;
+ cursor.next.reset();
+
+ if (mEnforceSequentialAccess && trackIndex == mExtractorTrackIndex) {
+ while (advanceExtractor_l()) {
+ SampleCursor& cursor = mTrackCursors[mExtractorTrackIndex];
+ if (cursor.current.isSet && cursor.current.index == mExtractorSampleIndex) {
+ if (mExtractorTrackIndex != trackIndex) {
+ mTrackSignals[mExtractorTrackIndex].notify_all();
+ }
+ break;
+ }
+ }
+ }
+ return;
+}
+
+bool MediaSampleReaderNDK::advanceExtractor_l() {
+ // Reset the "next" sample time whenever the extractor advances past a sample that is current,
+ // to ensure that "next" is appropriately updated when the extractor advances over the next
+ // sample of that track.
+ if (mTrackCursors[mExtractorTrackIndex].current.isSet &&
+ mTrackCursors[mExtractorTrackIndex].current.index == mExtractorSampleIndex) {
+ mTrackCursors[mExtractorTrackIndex].next.reset();
+ }
+
+ // Update the extractor's sample index even if this track reaches EOS, so that the other tracks
+ // are not given an incorrect extractor position.
+ mExtractorSampleIndex++;
+ if (!AMediaExtractor_advance(mExtractor)) {
+ LOG(DEBUG) << " EOS in advanceExtractor_l";
+ mEosReached = true;
+ for (auto it = mTrackSignals.begin(); it != mTrackSignals.end(); ++it) {
+ it->second.notify_all();
+ }
+ return false;
+ }
+
+ mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor);
+
+ SampleCursor& cursor = mTrackCursors[mExtractorTrackIndex];
+ if (mExtractorSampleIndex > cursor.previous.index) {
+ if (!cursor.current.isSet) {
+ cursor.current.set(mExtractorSampleIndex, AMediaExtractor_getSampleTime(mExtractor));
+ } else if (!cursor.next.isSet && mExtractorSampleIndex > cursor.current.index) {
+ cursor.next.set(mExtractorSampleIndex, AMediaExtractor_getSampleTime(mExtractor));
+ }
+ }
+
+ return true;
+}
+
+media_status_t MediaSampleReaderNDK::seekExtractorBackwards_l(int64_t targetTimeUs,
+ int targetTrackIndex,
+ uint64_t targetSampleIndex) {
+ if (targetSampleIndex > mExtractorSampleIndex) {
+ LOG(ERROR) << "Error: Forward seek is not supported";
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+
+ // AMediaExtractor supports reading negative timestamps but does not support seeking to them.
+ const int64_t seekToTimeUs = std::max(targetTimeUs, (int64_t)0);
+ media_status_t status =
+ AMediaExtractor_seekTo(mExtractor, seekToTimeUs, AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC);
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "Unable to seek to " << seekToTimeUs << ", target " << targetTimeUs;
+ return status;
+ }
+
+ mEosReached = false;
+ mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor);
+ int64_t sampleTimeUs = AMediaExtractor_getSampleTime(mExtractor);
+
+ while (sampleTimeUs != targetTimeUs || mExtractorTrackIndex != targetTrackIndex) {
+ if (!AMediaExtractor_advance(mExtractor)) {
+ return AMEDIA_ERROR_END_OF_STREAM;
+ }
+ mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor);
+ sampleTimeUs = AMediaExtractor_getSampleTime(mExtractor);
+ }
+ mExtractorSampleIndex = targetSampleIndex;
+ return AMEDIA_OK;
+}
+
+media_status_t MediaSampleReaderNDK::moveToSample_l(SamplePosition& pos, int trackIndex) {
+ // Seek backwards if the extractor is ahead of the sample.
+ if (pos.isSet && mExtractorSampleIndex > pos.index) {
+ media_status_t status = seekExtractorBackwards_l(pos.timeStampUs, trackIndex, pos.index);
+ if (status != AMEDIA_OK) return status;
+ }
+
+ // Advance until extractor points to the sample.
+ while (!(pos.isSet && pos.index == mExtractorSampleIndex)) {
+ if (!advanceExtractor_l()) {
+ return AMEDIA_ERROR_END_OF_STREAM;
+ }
+ }
+
+ return AMEDIA_OK;
+}
+
+media_status_t MediaSampleReaderNDK::moveToTrack_l(int trackIndex) {
+ return moveToSample_l(mTrackCursors[trackIndex].current, trackIndex);
+}
+
+media_status_t MediaSampleReaderNDK::waitForTrack_l(int trackIndex,
+ std::unique_lock<std::mutex>& lockHeld) {
+ while (trackIndex != mExtractorTrackIndex && !mEosReached && mEnforceSequentialAccess) {
+ mTrackSignals[trackIndex].wait(lockHeld);
+ }
+
+ if (mEosReached) {
+ return AMEDIA_ERROR_END_OF_STREAM;
+ }
+
+ if (!mEnforceSequentialAccess) {
+ return moveToTrack_l(trackIndex);
+ }
+
+ return AMEDIA_OK;
+}
+
+media_status_t MediaSampleReaderNDK::primeExtractorForTrack_l(
+ int trackIndex, std::unique_lock<std::mutex>& lockHeld) {
+ if (mExtractorTrackIndex < 0) {
+ mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor);
+ if (mExtractorTrackIndex < 0) {
+ return AMEDIA_ERROR_END_OF_STREAM;
+ }
+ mTrackCursors[mExtractorTrackIndex].current.set(mExtractorSampleIndex,
+ AMediaExtractor_getSampleTime(mExtractor));
+ }
+
+ if (mEnforceSequentialAccess) {
+ return waitForTrack_l(trackIndex, lockHeld);
+ } else {
+ return moveToTrack_l(trackIndex);
+ }
+}
+
+media_status_t MediaSampleReaderNDK::selectTrack(int trackIndex) {
+ std::scoped_lock lock(mExtractorMutex);
+
+ if (trackIndex < 0 || trackIndex >= mTrackCount) {
+ LOG(ERROR) << "Invalid trackIndex " << trackIndex << " for trackCount " << mTrackCount;
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ } else if (mTrackSignals.find(trackIndex) != mTrackSignals.end()) {
+ LOG(ERROR) << "TrackIndex " << trackIndex << " already selected";
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ } else if (mExtractorTrackIndex >= 0) {
+ LOG(ERROR) << "Tracks must be selected before sample reading begins.";
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+
+ media_status_t status = AMediaExtractor_selectTrack(mExtractor, trackIndex);
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "AMediaExtractor_selectTrack returned error: " << status;
+ return status;
+ }
+
+ mTrackSignals.emplace(std::piecewise_construct, std::forward_as_tuple(trackIndex),
+ std::forward_as_tuple());
+ return AMEDIA_OK;
+}
+
+media_status_t MediaSampleReaderNDK::unselectTrack(int trackIndex) {
+ std::scoped_lock lock(mExtractorMutex);
+
+ if (trackIndex < 0 || trackIndex >= mTrackCount) {
+ LOG(ERROR) << "Invalid trackIndex " << trackIndex << " for trackCount " << mTrackCount;
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ } else if (mExtractorTrackIndex >= 0) {
+ LOG(ERROR) << "unselectTrack must be called before sample reading begins.";
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+
+ auto it = mTrackSignals.find(trackIndex);
+ if (it == mTrackSignals.end()) {
+ LOG(ERROR) << "TrackIndex " << trackIndex << " is not selected";
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ mTrackSignals.erase(it);
+
+ media_status_t status = AMediaExtractor_unselectTrack(mExtractor, trackIndex);
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "AMediaExtractor_selectTrack returned error: " << status;
+ return status;
+ }
+
+ return AMEDIA_OK;
+}
+
+media_status_t MediaSampleReaderNDK::setEnforceSequentialAccess(bool enforce) {
+ LOG(DEBUG) << "setEnforceSequentialAccess( " << enforce << " )";
+
+ std::scoped_lock lock(mExtractorMutex);
+
+ if (mEnforceSequentialAccess && !enforce) {
+ // If switching from enforcing to not enforcing sequential access there may be threads
+ // waiting that needs to be woken up.
+ for (auto it = mTrackSignals.begin(); it != mTrackSignals.end(); ++it) {
+ it->second.notify_all();
+ }
+ } else if (!mEnforceSequentialAccess && enforce && mExtractorTrackIndex >= 0) {
+ // If switching from not enforcing to enforcing sequential access the extractor needs to be
+ // positioned for the track farthest behind so that it won't get stuck waiting.
+ struct {
+ SamplePosition* pos = nullptr;
+ int trackIndex = -1;
+ } earliestSample;
+
+ for (int trackIndex = 0; trackIndex < mTrackCount; ++trackIndex) {
+ SamplePosition& lastKnownTrackPosition = mTrackCursors[trackIndex].current.isSet
+ ? mTrackCursors[trackIndex].current
+ : mTrackCursors[trackIndex].previous;
+
+ if (lastKnownTrackPosition.isSet) {
+ if (earliestSample.pos == nullptr ||
+ earliestSample.pos->index > lastKnownTrackPosition.index) {
+ earliestSample.pos = &lastKnownTrackPosition;
+ earliestSample.trackIndex = trackIndex;
+ }
+ }
+ }
+
+ if (earliestSample.pos == nullptr) {
+ LOG(ERROR) << "No known sample position found";
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+
+ media_status_t status = moveToSample_l(*earliestSample.pos, earliestSample.trackIndex);
+ if (status != AMEDIA_OK) return status;
+
+ while (!(mTrackCursors[mExtractorTrackIndex].current.isSet &&
+ mTrackCursors[mExtractorTrackIndex].current.index == mExtractorSampleIndex)) {
+ if (!advanceExtractor_l()) {
+ return AMEDIA_ERROR_END_OF_STREAM;
+ }
+ }
+ }
+
+ mEnforceSequentialAccess = enforce;
+ return AMEDIA_OK;
+}
+
+media_status_t MediaSampleReaderNDK::getEstimatedBitrateForTrack(int trackIndex, int32_t* bitrate) {
+ std::scoped_lock lock(mExtractorMutex);
+ media_status_t status = AMEDIA_OK;
+
+ if (mTrackSignals.find(trackIndex) == mTrackSignals.end()) {
+ LOG(ERROR) << "Track is not selected.";
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ } else if (bitrate == nullptr) {
+ LOG(ERROR) << "bitrate pointer is NULL.";
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ } else if (mExtractorTrackIndex >= 0) {
+ LOG(ERROR) << "getEstimatedBitrateForTrack must be called before sample reading begins.";
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+
+ // Sample the track.
+ static constexpr int64_t kSamplingDurationUs = 10 * 1000 * 1000; // 10 seconds
+ size_t lastSampleSize = 0;
+ size_t totalSampleSize = 0;
+ int64_t firstSampleTimeUs = 0;
+ int64_t lastSampleTimeUs = 0;
+
+ do {
+ if (AMediaExtractor_getSampleTrackIndex(mExtractor) == trackIndex) {
+ lastSampleTimeUs = AMediaExtractor_getSampleTime(mExtractor);
+ if (totalSampleSize == 0) {
+ firstSampleTimeUs = lastSampleTimeUs;
+ }
+
+ lastSampleSize = AMediaExtractor_getSampleSize(mExtractor);
+ totalSampleSize += lastSampleSize;
+ }
+ } while ((lastSampleTimeUs - firstSampleTimeUs) < kSamplingDurationUs &&
+ AMediaExtractor_advance(mExtractor));
+
+ // Reset the extractor to the beginning.
+ status = AMediaExtractor_seekTo(mExtractor, 0, AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC);
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "Unable to reset extractor: " << status;
+ return status;
+ }
+
+ int64_t durationUs = 0;
+ const int64_t sampledDurationUs = lastSampleTimeUs - firstSampleTimeUs;
+
+ if (sampledDurationUs < kSamplingDurationUs) {
+ // Track is shorter than the sampling duration so use the full track duration to get better
+ // accuracy (i.e. don't skip the last sample).
+ AMediaFormat* trackFormat = getTrackFormat(trackIndex);
+ if (!AMediaFormat_getInt64(trackFormat, AMEDIAFORMAT_KEY_DURATION, &durationUs)) {
+ durationUs = 0;
+ }
+ AMediaFormat_delete(trackFormat);
+ }
+
+ if (durationUs == 0) {
+ // The sampled duration does not account for the last sample's duration so its size should
+ // not be included either.
+ totalSampleSize -= lastSampleSize;
+ durationUs = sampledDurationUs;
+ }
+
+ if (totalSampleSize == 0 || durationUs <= 0) {
+ LOG(ERROR) << "Unable to estimate track bitrate";
+ return AMEDIA_ERROR_MALFORMED;
+ }
+
+ *bitrate = roundf((float)totalSampleSize * 8 * 1000000 / durationUs);
+ return AMEDIA_OK;
+}
+
+media_status_t MediaSampleReaderNDK::getSampleInfoForTrack(int trackIndex, MediaSampleInfo* info) {
+ std::unique_lock<std::mutex> lock(mExtractorMutex);
+
+ if (mTrackSignals.find(trackIndex) == mTrackSignals.end()) {
+ LOG(ERROR) << "Track not selected.";
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ } else if (info == nullptr) {
+ LOG(ERROR) << "MediaSampleInfo pointer is NULL.";
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ media_status_t status = primeExtractorForTrack_l(trackIndex, lock);
+ if (status == AMEDIA_OK) {
+ info->presentationTimeUs = AMediaExtractor_getSampleTime(mExtractor);
+ info->flags = AMediaExtractor_getSampleFlags(mExtractor);
+ info->size = AMediaExtractor_getSampleSize(mExtractor);
+ } else if (status == AMEDIA_ERROR_END_OF_STREAM) {
+ info->presentationTimeUs = 0;
+ info->flags = SAMPLE_FLAG_END_OF_STREAM;
+ info->size = 0;
+ LOG(DEBUG) << " getSampleInfoForTrack #" << trackIndex << ": End Of Stream";
+ } else {
+ LOG(ERROR) << " getSampleInfoForTrack #" << trackIndex << ": Error " << status;
+ }
+
+ return status;
+}
+
+media_status_t MediaSampleReaderNDK::readSampleDataForTrack(int trackIndex, uint8_t* buffer,
+ size_t bufferSize) {
+ std::unique_lock<std::mutex> lock(mExtractorMutex);
+
+ if (mTrackSignals.find(trackIndex) == mTrackSignals.end()) {
+ LOG(ERROR) << "Track not selected.";
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ } else if (buffer == nullptr) {
+ LOG(ERROR) << "buffer pointer is NULL";
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ media_status_t status = primeExtractorForTrack_l(trackIndex, lock);
+ if (status != AMEDIA_OK) {
+ return status;
+ }
+
+ ssize_t sampleSize = AMediaExtractor_getSampleSize(mExtractor);
+ if (bufferSize < sampleSize) {
+ LOG(ERROR) << "Buffer is too small for sample, " << bufferSize << " vs " << sampleSize;
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ ssize_t bytesRead = AMediaExtractor_readSampleData(mExtractor, buffer, bufferSize);
+ if (bytesRead < sampleSize) {
+ LOG(ERROR) << "Unable to read full sample, " << bytesRead << " vs " << sampleSize;
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ advanceTrack_l(trackIndex);
+
+ return AMEDIA_OK;
+}
+
+void MediaSampleReaderNDK::advanceTrack(int trackIndex) {
+ std::scoped_lock lock(mExtractorMutex);
+
+ if (mTrackSignals.find(trackIndex) != mTrackSignals.end()) {
+ advanceTrack_l(trackIndex);
+ } else {
+ LOG(ERROR) << "Trying to advance a track that is not selected (#" << trackIndex << ")";
+ }
+}
+
+AMediaFormat* MediaSampleReaderNDK::getFileFormat() {
+ return AMediaExtractor_getFileFormat(mExtractor);
+}
+
+size_t MediaSampleReaderNDK::getTrackCount() const {
+ return mTrackCount;
+}
+
+AMediaFormat* MediaSampleReaderNDK::getTrackFormat(int trackIndex) {
+ if (trackIndex < 0 || trackIndex >= mTrackCount) {
+ LOG(ERROR) << "Invalid trackIndex " << trackIndex << " for trackCount " << mTrackCount;
+ return AMediaFormat_new();
+ }
+
+ return AMediaExtractor_getTrackFormat(mExtractor, trackIndex);
+}
+
+} // namespace android
diff --git a/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp b/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
new file mode 100644
index 0000000..e931cc1
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "MediaSampleWriter"
+
+#include <android-base/logging.h>
+#include <media/MediaSampleWriter.h>
+#include <media/NdkCommon.h>
+#include <media/NdkMediaMuxer.h>
+#include <sys/prctl.h>
+#include <utils/AndroidThreads.h>
+
+namespace android {
+
+class DefaultMuxer : public MediaSampleWriterMuxerInterface {
+public:
+ // MediaSampleWriterMuxerInterface
+ ssize_t addTrack(AMediaFormat* trackFormat) override {
+ // If the track format has rotation, need to call AMediaMuxer_setOrientationHint
+ // to set the rotation. Muxer doesn't take rotation specified on the track.
+ const char* mime;
+ if (AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime) &&
+ strncmp(mime, "video/", 6) == 0) {
+ int32_t rotation;
+ if (AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_ROTATION, &rotation) &&
+ (rotation != 0)) {
+ AMediaMuxer_setOrientationHint(mMuxer, rotation);
+ }
+ }
+
+ return AMediaMuxer_addTrack(mMuxer, trackFormat);
+ }
+ media_status_t start() override { return AMediaMuxer_start(mMuxer); }
+ media_status_t writeSampleData(size_t trackIndex, const uint8_t* data,
+ const AMediaCodecBufferInfo* info) override {
+ return AMediaMuxer_writeSampleData(mMuxer, trackIndex, data, info);
+ }
+ media_status_t stop() override { return AMediaMuxer_stop(mMuxer); }
+ // ~MediaSampleWriterMuxerInterface
+
+ static std::shared_ptr<DefaultMuxer> create(int fd) {
+ AMediaMuxer* ndkMuxer = AMediaMuxer_new(fd, AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4);
+ if (ndkMuxer == nullptr) {
+ LOG(ERROR) << "Unable to create AMediaMuxer";
+ return nullptr;
+ }
+
+ return std::make_shared<DefaultMuxer>(ndkMuxer);
+ }
+
+ ~DefaultMuxer() {
+ if (mMuxer != nullptr) {
+ AMediaMuxer_delete(mMuxer);
+ }
+ }
+
+ DefaultMuxer(AMediaMuxer* muxer) : mMuxer(muxer){};
+ DefaultMuxer() = delete;
+
+private:
+ AMediaMuxer* mMuxer;
+};
+
+// static
+std::shared_ptr<MediaSampleWriter> MediaSampleWriter::Create() {
+ return std::shared_ptr<MediaSampleWriter>(new MediaSampleWriter());
+}
+
+MediaSampleWriter::~MediaSampleWriter() {
+ if (mState == STARTED) {
+ stop();
+ }
+}
+
+bool MediaSampleWriter::init(int fd, const std::weak_ptr<CallbackInterface>& callbacks,
+ int64_t heartBeatIntervalUs) {
+ return init(DefaultMuxer::create(fd), callbacks, heartBeatIntervalUs);
+}
+
+bool MediaSampleWriter::init(const std::shared_ptr<MediaSampleWriterMuxerInterface>& muxer,
+ const std::weak_ptr<CallbackInterface>& callbacks,
+ int64_t heartBeatIntervalUs) {
+ if (callbacks.lock() == nullptr) {
+ LOG(ERROR) << "Callback object cannot be null";
+ return false;
+ } else if (muxer == nullptr) {
+ LOG(ERROR) << "Muxer cannot be null";
+ return false;
+ }
+
+ std::scoped_lock lock(mMutex);
+ if (mState != UNINITIALIZED) {
+ LOG(ERROR) << "Sample writer is already initialized";
+ return false;
+ }
+
+ mState = INITIALIZED;
+ mMuxer = muxer;
+ mCallbacks = callbacks;
+ mHeartBeatIntervalUs = heartBeatIntervalUs;
+ return true;
+}
+
+MediaSampleWriter::MediaSampleConsumerFunction MediaSampleWriter::addTrack(
+ const std::shared_ptr<AMediaFormat>& trackFormat) {
+ if (trackFormat == nullptr) {
+ LOG(ERROR) << "Track format must be non-null";
+ return nullptr;
+ }
+
+ std::scoped_lock lock(mMutex);
+ if (mState != INITIALIZED) {
+ LOG(ERROR) << "Muxer needs to be initialized when adding tracks.";
+ return nullptr;
+ }
+
+ AMediaFormat* trackFormatCopy = AMediaFormat_new();
+ AMediaFormat_copy(trackFormatCopy, trackFormat.get());
+ // Request muxer to use background priorities by default.
+ AMediaFormatUtils::SetDefaultFormatValueInt32(TBD_AMEDIACODEC_PARAMETER_KEY_BACKGROUND_MODE,
+ trackFormatCopy, 1 /* true */);
+
+ ssize_t trackIndexOrError = mMuxer->addTrack(trackFormatCopy);
+ AMediaFormat_delete(trackFormatCopy);
+ if (trackIndexOrError < 0) {
+ LOG(ERROR) << "Failed to add media track to muxer: " << trackIndexOrError;
+ return nullptr;
+ }
+ const size_t trackIndex = static_cast<size_t>(trackIndexOrError);
+
+ int64_t durationUs;
+ if (!AMediaFormat_getInt64(trackFormat.get(), AMEDIAFORMAT_KEY_DURATION, &durationUs)) {
+ durationUs = 0;
+ }
+
+ mTracks.emplace(trackIndex, durationUs);
+ std::shared_ptr<MediaSampleWriter> thisWriter = shared_from_this();
+
+ return [self = shared_from_this(), trackIndex](const std::shared_ptr<MediaSample>& sample) {
+ self->addSampleToTrack(trackIndex, sample);
+ };
+}
+
+void MediaSampleWriter::addSampleToTrack(size_t trackIndex,
+ const std::shared_ptr<MediaSample>& sample) {
+ if (sample == nullptr) return;
+
+ bool wasEmpty;
+ {
+ std::scoped_lock lock(mMutex);
+ wasEmpty = mSampleQueue.empty();
+ mSampleQueue.push(std::make_pair(trackIndex, sample));
+ }
+
+ if (wasEmpty) {
+ mSampleSignal.notify_one();
+ }
+}
+
+bool MediaSampleWriter::start() {
+ std::scoped_lock lock(mMutex);
+
+ if (mTracks.size() == 0) {
+ LOG(ERROR) << "No tracks to write.";
+ return false;
+ } else if (mState != INITIALIZED) {
+ LOG(ERROR) << "Sample writer is not initialized";
+ return false;
+ }
+
+ mState = STARTED;
+ std::thread([this] {
+ androidSetThreadPriority(0 /* tid (0 = current) */, ANDROID_PRIORITY_BACKGROUND);
+ prctl(PR_SET_NAME, (unsigned long)"SampleWriterTrd", 0, 0, 0);
+
+ bool wasStopped = false;
+ media_status_t status = writeSamples(&wasStopped);
+ if (auto callbacks = mCallbacks.lock()) {
+ if (wasStopped && status == AMEDIA_OK) {
+ callbacks->onStopped(this);
+ } else {
+ callbacks->onFinished(this, status);
+ }
+ }
+ }).detach();
+ return true;
+}
+
+void MediaSampleWriter::stop() {
+ {
+ std::scoped_lock lock(mMutex);
+ if (mState != STARTED) {
+ LOG(ERROR) << "Sample writer is not started.";
+ return;
+ }
+ mState = STOPPED;
+ }
+
+ mSampleSignal.notify_all();
+}
+
+media_status_t MediaSampleWriter::writeSamples(bool* wasStopped) {
+ media_status_t muxerStatus = mMuxer->start();
+ if (muxerStatus != AMEDIA_OK) {
+ LOG(ERROR) << "Error starting muxer: " << muxerStatus;
+ return muxerStatus;
+ }
+
+ media_status_t writeStatus = runWriterLoop(wasStopped);
+ if (writeStatus != AMEDIA_OK) {
+ LOG(ERROR) << "Error writing samples: " << writeStatus;
+ }
+
+ muxerStatus = mMuxer->stop();
+ if (muxerStatus != AMEDIA_OK) {
+ LOG(ERROR) << "Error stopping muxer: " << muxerStatus;
+ }
+
+ return writeStatus != AMEDIA_OK ? writeStatus : muxerStatus;
+}
+
+media_status_t MediaSampleWriter::runWriterLoop(bool* wasStopped) NO_THREAD_SAFETY_ANALYSIS {
+ AMediaCodecBufferInfo bufferInfo;
+ int32_t lastProgressUpdate = 0;
+ bool progressSinceLastReport = false;
+ int trackEosCount = 0;
+
+ // Set the "primary" track that will be used to determine progress to the track with longest
+ // duration.
+ int primaryTrackIndex = -1;
+ int64_t longestDurationUs = 0;
+ for (auto it = mTracks.begin(); it != mTracks.end(); ++it) {
+ if (it->second.mDurationUs > longestDurationUs) {
+ primaryTrackIndex = it->first;
+ longestDurationUs = it->second.mDurationUs;
+ }
+ }
+
+ std::chrono::microseconds updateInterval(mHeartBeatIntervalUs);
+ std::chrono::steady_clock::time_point nextUpdateTime =
+ std::chrono::steady_clock::now() + updateInterval;
+
+ while (true) {
+ if (trackEosCount >= mTracks.size()) {
+ break;
+ }
+
+ size_t trackIndex;
+ std::shared_ptr<MediaSample> sample;
+ {
+ std::unique_lock lock(mMutex);
+ while (mSampleQueue.empty() && mState == STARTED) {
+ if (mHeartBeatIntervalUs <= 0) {
+ mSampleSignal.wait(lock);
+ continue;
+ }
+
+ if (mSampleSignal.wait_until(lock, nextUpdateTime) == std::cv_status::timeout) {
+ // Send heart-beat if there is any progress since last update time.
+ if (progressSinceLastReport) {
+ if (auto callbacks = mCallbacks.lock()) {
+ callbacks->onHeartBeat(this);
+ }
+ progressSinceLastReport = false;
+ }
+ nextUpdateTime += updateInterval;
+ }
+ }
+
+ if (mState == STOPPED) {
+ *wasStopped = true;
+ return AMEDIA_OK;
+ }
+
+ auto& topEntry = mSampleQueue.top();
+ trackIndex = topEntry.first;
+ sample = topEntry.second;
+ mSampleQueue.pop();
+ }
+
+ TrackRecord& track = mTracks[trackIndex];
+
+ if (sample->info.flags & SAMPLE_FLAG_END_OF_STREAM) {
+ if (track.mReachedEos) {
+ continue;
+ }
+
+ // Track reached end of stream.
+ track.mReachedEos = true;
+ trackEosCount++;
+
+ // Preserve source track duration by setting the appropriate timestamp on the
+ // empty End-Of-Stream sample.
+ if (track.mDurationUs > 0 && track.mFirstSampleTimeSet) {
+ sample->info.presentationTimeUs = track.mDurationUs + track.mFirstSampleTimeUs;
+ }
+ }
+
+ track.mPrevSampleTimeUs = sample->info.presentationTimeUs;
+ if (!track.mFirstSampleTimeSet) {
+ // Record the first sample's timestamp in order to translate duration to EOS
+ // time for tracks that does not start at 0.
+ track.mFirstSampleTimeUs = sample->info.presentationTimeUs;
+ track.mFirstSampleTimeSet = true;
+ }
+
+ bufferInfo.offset = sample->dataOffset;
+ bufferInfo.size = sample->info.size;
+ bufferInfo.flags = sample->info.flags;
+ bufferInfo.presentationTimeUs = sample->info.presentationTimeUs;
+
+ media_status_t status = mMuxer->writeSampleData(trackIndex, sample->buffer, &bufferInfo);
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "writeSampleData returned " << status;
+ return status;
+ }
+ sample.reset();
+
+ // TODO(lnilsson): Add option to toggle progress reporting on/off.
+ if (trackIndex == primaryTrackIndex) {
+ const int64_t elapsed = track.mPrevSampleTimeUs - track.mFirstSampleTimeUs;
+ int32_t progress = (elapsed * 100) / track.mDurationUs;
+ progress = std::clamp(progress, 0, 100);
+
+ if (progress > lastProgressUpdate) {
+ if (auto callbacks = mCallbacks.lock()) {
+ callbacks->onProgressUpdate(this, progress);
+ }
+ lastProgressUpdate = progress;
+ }
+ }
+ progressSinceLastReport = true;
+ }
+
+ return AMEDIA_OK;
+}
+} // namespace android
diff --git a/media/libmediatranscoding/transcoder/MediaTrackTranscoder.cpp b/media/libmediatranscoding/transcoder/MediaTrackTranscoder.cpp
new file mode 100644
index 0000000..f01a948
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/MediaTrackTranscoder.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "MediaTrackTranscoder"
+
+#include <android-base/logging.h>
+#include <media/MediaTrackTranscoder.h>
+#include <media/MediaTrackTranscoderCallback.h>
+#include <utils/AndroidThreads.h>
+
+namespace android {
+
+media_status_t MediaTrackTranscoder::configure(
+ const std::shared_ptr<MediaSampleReader>& mediaSampleReader, int trackIndex,
+ const std::shared_ptr<AMediaFormat>& destinationFormat) {
+ std::scoped_lock lock{mStateMutex};
+
+ if (mState != UNINITIALIZED) {
+ LOG(ERROR) << "Configure can only be called once";
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+
+ if (mediaSampleReader == nullptr) {
+ LOG(ERROR) << "MediaSampleReader is null";
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ if (trackIndex < 0 || trackIndex >= mediaSampleReader->getTrackCount()) {
+ LOG(ERROR) << "TrackIndex is invalid " << trackIndex;
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ mMediaSampleReader = mediaSampleReader;
+ mTrackIndex = trackIndex;
+
+ mSourceFormat = std::shared_ptr<AMediaFormat>(mMediaSampleReader->getTrackFormat(mTrackIndex),
+ &AMediaFormat_delete);
+ if (mSourceFormat == nullptr) {
+ LOG(ERROR) << "Unable to get format for track #" << mTrackIndex;
+ return AMEDIA_ERROR_MALFORMED;
+ }
+
+ media_status_t status = configureDestinationFormat(destinationFormat);
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "configure failed with error " << status;
+ return status;
+ }
+
+ mState = CONFIGURED;
+ return AMEDIA_OK;
+}
+
+bool MediaTrackTranscoder::start() {
+ std::scoped_lock lock{mStateMutex};
+
+ if (mState != CONFIGURED) {
+ LOG(ERROR) << "TrackTranscoder must be configured before started";
+ return false;
+ }
+ mState = STARTED;
+
+ std::thread([this] {
+ androidSetThreadPriority(0 /* tid (0 = current) */, ANDROID_PRIORITY_BACKGROUND);
+ bool stopped = false;
+ media_status_t status = runTranscodeLoop(&stopped);
+
+ // Output an EOS sample if the transcoder was stopped.
+ if (stopped) {
+ auto sample = std::make_shared<MediaSample>();
+ sample->info.flags = SAMPLE_FLAG_END_OF_STREAM;
+ onOutputSampleAvailable(sample);
+ }
+
+ // Notify the client.
+ if (auto callbacks = mTranscoderCallback.lock()) {
+ if (stopped) {
+ callbacks->onTrackStopped(this);
+ } else if (status == AMEDIA_OK) {
+ callbacks->onTrackFinished(this);
+ } else {
+ callbacks->onTrackError(this, status);
+ }
+ }
+ }).detach();
+
+ return true;
+}
+
+void MediaTrackTranscoder::stop(bool stopOnSyncSample) {
+ std::scoped_lock lock{mStateMutex};
+
+ if (mState == STARTED || (mStopRequest == STOP_ON_SYNC && !stopOnSyncSample)) {
+ mStopRequest = stopOnSyncSample ? STOP_ON_SYNC : STOP_NOW;
+ abortTranscodeLoop();
+ mState = STOPPED;
+ } else {
+ LOG(WARNING) << "TrackTranscoder must be started before stopped";
+ }
+}
+
+void MediaTrackTranscoder::notifyTrackFormatAvailable() {
+ if (auto callbacks = mTranscoderCallback.lock()) {
+ callbacks->onTrackFormatAvailable(this);
+ }
+}
+
+void MediaTrackTranscoder::onOutputSampleAvailable(const std::shared_ptr<MediaSample>& sample) {
+ std::scoped_lock lock{mSampleMutex};
+ if (mSampleConsumer == nullptr) {
+ mSampleQueue.enqueue(sample);
+ } else {
+ mSampleConsumer(sample);
+ }
+}
+
+void MediaTrackTranscoder::setSampleConsumer(
+ const MediaSampleWriter::MediaSampleConsumerFunction& sampleConsumer) {
+ std::scoped_lock lock{mSampleMutex};
+ mSampleConsumer = sampleConsumer;
+
+ std::shared_ptr<MediaSample> sample;
+ while (!mSampleQueue.isEmpty() && !mSampleQueue.dequeue(&sample)) {
+ mSampleConsumer(sample);
+ }
+}
+
+} // namespace android
diff --git a/media/libmediatranscoding/transcoder/MediaTranscoder.cpp b/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
new file mode 100644
index 0000000..e20f7ab
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "MediaTranscoder"
+
+#include <android-base/logging.h>
+#include <fcntl.h>
+#include <media/MediaSampleReaderNDK.h>
+#include <media/MediaSampleWriter.h>
+#include <media/MediaTranscoder.h>
+#include <media/NdkCommon.h>
+#include <media/PassthroughTrackTranscoder.h>
+#include <media/VideoTrackTranscoder.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+namespace android {
+
+static std::shared_ptr<AMediaFormat> createVideoTrackFormat(AMediaFormat* srcFormat,
+ AMediaFormat* options) {
+ if (srcFormat == nullptr || options == nullptr) {
+ LOG(ERROR) << "Cannot merge null formats";
+ return nullptr;
+ }
+
+ // ------- Define parameters to copy from the source track format -------
+ std::vector<AMediaFormatUtils::EntryCopier> srcParamsToCopy{
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_MIME, String),
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_DURATION, Int64),
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_WIDTH, Int32),
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_HEIGHT, Int32),
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_FRAME_RATE, Int32),
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_RANGE, Int32),
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_STANDARD, Int32),
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_TRANSFER, Int32),
+ };
+
+ // If the destination codec is the same as the source codec, we can preserve profile and level
+ // from the source track as default values. Otherwise leave them unspecified.
+ const char *srcMime, *dstMime;
+ AMediaFormat_getString(srcFormat, AMEDIAFORMAT_KEY_MIME, &srcMime);
+ if (!AMediaFormat_getString(options, AMEDIAFORMAT_KEY_MIME, &dstMime) ||
+ strcmp(srcMime, dstMime) == 0) {
+ srcParamsToCopy.push_back(ENTRY_COPIER(AMEDIAFORMAT_KEY_PROFILE, String));
+ srcParamsToCopy.push_back(ENTRY_COPIER(AMEDIAFORMAT_KEY_LEVEL, String));
+ }
+
+ // ------- Define parameters to copy from the caller's options -------
+ static const std::vector<AMediaFormatUtils::EntryCopier> kSupportedOptions{
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_MIME, String),
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_DURATION, Int64),
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_WIDTH, Int32),
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_HEIGHT, Int32),
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_BIT_RATE, Int32),
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_PROFILE, Int32),
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_LEVEL, Int32),
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_RANGE, Int32),
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_STANDARD, Int32),
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_TRANSFER, Int32),
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_FRAME_RATE, Int32),
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, Int32),
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_PRIORITY, Int32),
+ ENTRY_COPIER2(AMEDIAFORMAT_KEY_OPERATING_RATE, Float, Int32),
+ };
+
+ // ------- Copy parameters from source and options to the destination -------
+ auto trackFormat = std::shared_ptr<AMediaFormat>(AMediaFormat_new(), &AMediaFormat_delete);
+ AMediaFormatUtils::CopyFormatEntries(srcFormat, trackFormat.get(), srcParamsToCopy);
+ AMediaFormatUtils::CopyFormatEntries(options, trackFormat.get(), kSupportedOptions);
+ return trackFormat;
+}
+
+void MediaTranscoder::onThreadFinished(const void* thread, media_status_t threadStatus,
+ bool threadStopped) {
+ LOG(DEBUG) << "Thread " << thread << " finished with status " << threadStatus << " stopped "
+ << threadStopped;
+
+ // Stop all threads if one reports an error.
+ if (threadStatus != AMEDIA_OK) {
+ requestStop(false /* stopOnSync */);
+ }
+
+ std::scoped_lock lock{mThreadStateMutex};
+
+ // Record the change.
+ mThreadStates[thread] = DONE;
+ if (threadStatus != AMEDIA_OK && mTranscoderStatus == AMEDIA_OK) {
+ mTranscoderStatus = threadStatus;
+ }
+
+ mTranscoderStopped |= threadStopped;
+
+ // Check if all threads are done. Note that if all transcoders have stopped but the sample
+ // writer has not yet started, it never will.
+ bool transcodersDone = true;
+ ThreadState sampleWriterState = PENDING;
+ for (const auto& it : mThreadStates) {
+ LOG(DEBUG) << " Thread " << it.first << " state" << it.second;
+ if (it.first == static_cast<const void*>(mSampleWriter.get())) {
+ sampleWriterState = it.second;
+ } else {
+ transcodersDone &= (it.second == DONE);
+ }
+ }
+ if (!transcodersDone || sampleWriterState == RUNNING) {
+ return;
+ }
+
+ // All done. Send callback asynchronously and wake up threads waiting in cancel/pause.
+ mThreadsDone = true;
+ if (!mCallbackSent) {
+ std::thread asyncNotificationThread{[this, self = shared_from_this(),
+ status = mTranscoderStatus,
+ stopped = mTranscoderStopped] {
+ prctl(PR_SET_NAME, (unsigned long)"TranscodCallbk", 0, 0, 0);
+
+ // If the transcoder was stopped that means a caller is waiting in stop or pause
+ // in which case we don't send a callback.
+ if (status != AMEDIA_OK) {
+ mCallbacks->onError(this, status);
+ } else if (!stopped) {
+ mCallbacks->onFinished(this);
+ }
+ mThreadsDoneSignal.notify_all();
+ }};
+ asyncNotificationThread.detach();
+ mCallbackSent = true;
+ }
+}
+
+void MediaTranscoder::onTrackFormatAvailable(const MediaTrackTranscoder* transcoder) {
+ LOG(DEBUG) << "TrackTranscoder " << transcoder << " format available.";
+
+ std::scoped_lock lock{mTracksAddedMutex};
+ const void* sampleWriterPtr = static_cast<const void*>(mSampleWriter.get());
+
+ // Ignore duplicate format change.
+ if (mTracksAdded.count(transcoder) > 0) {
+ return;
+ }
+
+ // Add track to the writer.
+ auto consumer = mSampleWriter->addTrack(transcoder->getOutputFormat());
+ if (consumer == nullptr) {
+ LOG(ERROR) << "Unable to add track to sample writer.";
+ onThreadFinished(sampleWriterPtr, AMEDIA_ERROR_UNKNOWN, false /* stopped */);
+ return;
+ }
+
+ // The sample writer is not yet started so notify the caller that progress is still made.
+ if (mHeartBeatIntervalUs > 0) {
+ mCallbacks->onHeartBeat(this);
+ }
+
+ MediaTrackTranscoder* mutableTranscoder = const_cast<MediaTrackTranscoder*>(transcoder);
+ mutableTranscoder->setSampleConsumer(consumer);
+
+ mTracksAdded.insert(transcoder);
+ bool errorStarting = false;
+ if (mTracksAdded.size() == mTrackTranscoders.size()) {
+ // Enable sequential access mode on the sample reader to achieve optimal read performance.
+ // This has to wait until all tracks have delivered their output formats and the sample
+ // writer is started. Otherwise the tracks will not get their output sample queues drained
+ // and the transcoder could hang due to one track running out of buffers and blocking the
+ // other tracks from reading source samples before they could output their formats.
+
+ std::scoped_lock lock{mThreadStateMutex};
+ // Don't start the sample writer if a stop already has been requested.
+ if (!mSampleWriterStopped) {
+ if (!mCancelled) {
+ mSampleReader->setEnforceSequentialAccess(true);
+ }
+ LOG(DEBUG) << "Starting sample writer.";
+ errorStarting = !mSampleWriter->start();
+ if (!errorStarting) {
+ mThreadStates[sampleWriterPtr] = RUNNING;
+ }
+ }
+ }
+
+ if (errorStarting) {
+ LOG(ERROR) << "Unable to start sample writer.";
+ onThreadFinished(sampleWriterPtr, AMEDIA_ERROR_UNKNOWN, false /* stopped */);
+ }
+}
+
+void MediaTranscoder::onTrackFinished(const MediaTrackTranscoder* transcoder) {
+ LOG(DEBUG) << "TrackTranscoder " << transcoder << " finished";
+ onThreadFinished(static_cast<const void*>(transcoder), AMEDIA_OK, false /* stopped */);
+}
+
+void MediaTranscoder::onTrackStopped(const MediaTrackTranscoder* transcoder) {
+ LOG(DEBUG) << "TrackTranscoder " << transcoder << " stopped";
+ onThreadFinished(static_cast<const void*>(transcoder), AMEDIA_OK, true /* stopped */);
+}
+
+void MediaTranscoder::onTrackError(const MediaTrackTranscoder* transcoder, media_status_t status) {
+ LOG(ERROR) << "TrackTranscoder " << transcoder << " returned error " << status;
+ onThreadFinished(static_cast<const void*>(transcoder), status, false /* stopped */);
+}
+
+void MediaTranscoder::onFinished(const MediaSampleWriter* writer, media_status_t status) {
+ LOG(status == AMEDIA_OK ? DEBUG : ERROR) << "Sample writer finished with status " << status;
+ onThreadFinished(static_cast<const void*>(writer), status, false /* stopped */);
+}
+
+void MediaTranscoder::onStopped(const MediaSampleWriter* writer) {
+ LOG(DEBUG) << "Sample writer " << writer << " stopped";
+ onThreadFinished(static_cast<const void*>(writer), AMEDIA_OK, true /* stopped */);
+}
+
+void MediaTranscoder::onProgressUpdate(const MediaSampleWriter* writer __unused, int32_t progress) {
+ // Dispatch progress updated to the client.
+ mCallbacks->onProgressUpdate(this, progress);
+}
+
+void MediaTranscoder::onHeartBeat(const MediaSampleWriter* writer __unused) {
+ // Signal heart-beat to the client.
+ mCallbacks->onHeartBeat(this);
+}
+
+MediaTranscoder::MediaTranscoder(const std::shared_ptr<CallbackInterface>& callbacks,
+ int64_t heartBeatIntervalUs, pid_t pid, uid_t uid)
+ : mCallbacks(callbacks), mHeartBeatIntervalUs(heartBeatIntervalUs), mPid(pid), mUid(uid) {}
+
+std::shared_ptr<MediaTranscoder> MediaTranscoder::create(
+ const std::shared_ptr<CallbackInterface>& callbacks, int64_t heartBeatIntervalUs, pid_t pid,
+ uid_t uid, const std::shared_ptr<ndk::ScopedAParcel>& pausedState) {
+ if (pausedState != nullptr) {
+ LOG(INFO) << "Initializing from paused state.";
+ }
+ if (callbacks == nullptr) {
+ LOG(ERROR) << "Callbacks cannot be null";
+ return nullptr;
+ }
+
+ return std::shared_ptr<MediaTranscoder>(
+ new MediaTranscoder(callbacks, heartBeatIntervalUs, pid, uid));
+}
+
+media_status_t MediaTranscoder::configureSource(int fd) {
+ if (fd < 0) {
+ LOG(ERROR) << "Invalid source fd: " << fd;
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ const size_t fileSize = lseek(fd, 0, SEEK_END);
+ lseek(fd, 0, SEEK_SET);
+
+ mSampleReader = MediaSampleReaderNDK::createFromFd(fd, 0 /* offset */, fileSize);
+ if (mSampleReader == nullptr) {
+ LOG(ERROR) << "Unable to parse source fd: " << fd;
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+
+ const size_t trackCount = mSampleReader->getTrackCount();
+ for (size_t trackIndex = 0; trackIndex < trackCount; ++trackIndex) {
+ AMediaFormat* trackFormat = mSampleReader->getTrackFormat(static_cast<int>(trackIndex));
+ if (trackFormat == nullptr) {
+ LOG(ERROR) << "Track #" << trackIndex << " has no format";
+ return AMEDIA_ERROR_MALFORMED;
+ }
+
+ mSourceTrackFormats.emplace_back(trackFormat, &AMediaFormat_delete);
+ }
+
+ return AMEDIA_OK;
+}
+
+std::vector<std::shared_ptr<AMediaFormat>> MediaTranscoder::getTrackFormats() const {
+ // Return a deep copy of the formats to avoid the caller modifying our internal formats.
+ std::vector<std::shared_ptr<AMediaFormat>> trackFormats;
+ for (const std::shared_ptr<AMediaFormat>& sourceFormat : mSourceTrackFormats) {
+ AMediaFormat* copy = AMediaFormat_new();
+ AMediaFormat_copy(copy, sourceFormat.get());
+ trackFormats.emplace_back(copy, &AMediaFormat_delete);
+ }
+ return trackFormats;
+}
+
+media_status_t MediaTranscoder::configureTrackFormat(size_t trackIndex,
+ AMediaFormat* destinationOptions) {
+ if (mSampleReader == nullptr) {
+ LOG(ERROR) << "Source must be configured before tracks";
+ return AMEDIA_ERROR_INVALID_OPERATION;
+ } else if (trackIndex >= mSourceTrackFormats.size()) {
+ LOG(ERROR) << "Track index " << trackIndex
+ << " is out of bounds. Track count: " << mSourceTrackFormats.size();
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ std::shared_ptr<MediaTrackTranscoder> transcoder;
+ std::shared_ptr<AMediaFormat> trackFormat;
+
+ if (destinationOptions == nullptr) {
+ transcoder = std::make_shared<PassthroughTrackTranscoder>(shared_from_this());
+ } else {
+ AMediaFormat* srcTrackFormat = mSourceTrackFormats[trackIndex].get();
+
+ const char* srcMime = nullptr;
+ if (!AMediaFormat_getString(srcTrackFormat, AMEDIAFORMAT_KEY_MIME, &srcMime)) {
+ LOG(ERROR) << "Source track #" << trackIndex << " has no mime type";
+ return AMEDIA_ERROR_MALFORMED;
+ }
+
+ if (strncmp(srcMime, "video/", 6) != 0) {
+ LOG(ERROR) << "Only video tracks are supported for transcoding. Unable to configure "
+ "track #"
+ << trackIndex << " with mime " << srcMime;
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+
+ const char* dstMime = nullptr;
+ if (AMediaFormat_getString(destinationOptions, AMEDIAFORMAT_KEY_MIME, &dstMime)) {
+ if (strncmp(dstMime, "video/", 6) != 0) {
+ LOG(ERROR) << "Unable to convert media types for track #" << trackIndex << ", from "
+ << srcMime << " to " << dstMime;
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+ }
+
+ transcoder = VideoTrackTranscoder::create(shared_from_this(), mPid, mUid);
+
+ trackFormat = createVideoTrackFormat(srcTrackFormat, destinationOptions);
+ if (trackFormat == nullptr) {
+ LOG(ERROR) << "Unable to create video track format";
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ }
+
+ media_status_t status = mSampleReader->selectTrack(trackIndex);
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "Unable to select track " << trackIndex;
+ return status;
+ }
+
+ status = transcoder->configure(mSampleReader, trackIndex, trackFormat);
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "Configure track transcoder for track #" << trackIndex << " returned error "
+ << status;
+ mSampleReader->unselectTrack(trackIndex);
+ return status;
+ }
+
+ std::scoped_lock lock{mThreadStateMutex};
+ mThreadStates[static_cast<const void*>(transcoder.get())] = PENDING;
+
+ mTrackTranscoders.emplace_back(std::move(transcoder));
+ return AMEDIA_OK;
+}
+
+media_status_t MediaTranscoder::configureDestination(int fd) {
+ if (fd < 0) {
+ LOG(ERROR) << "Invalid destination fd: " << fd;
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (mSampleWriter != nullptr) {
+ LOG(ERROR) << "Destination is already configured.";
+ return AMEDIA_ERROR_INVALID_OPERATION;
+ }
+
+ mSampleWriter = MediaSampleWriter::Create();
+ const bool initOk = mSampleWriter->init(fd, shared_from_this(), mHeartBeatIntervalUs);
+
+ if (!initOk) {
+ LOG(ERROR) << "Unable to initialize sample writer with destination fd: " << fd;
+ mSampleWriter.reset();
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+
+ std::scoped_lock lock{mThreadStateMutex};
+ mThreadStates[static_cast<const void*>(mSampleWriter.get())] = PENDING;
+ return AMEDIA_OK;
+}
+
+media_status_t MediaTranscoder::start() {
+ if (mTrackTranscoders.size() < 1) {
+ LOG(ERROR) << "Unable to start, no tracks are configured.";
+ return AMEDIA_ERROR_INVALID_OPERATION;
+ } else if (mSampleWriter == nullptr) {
+ LOG(ERROR) << "Unable to start, destination is not configured";
+ return AMEDIA_ERROR_INVALID_OPERATION;
+ }
+
+ // Start transcoders
+ bool started = true;
+ {
+ std::scoped_lock lock{mThreadStateMutex};
+ for (auto& transcoder : mTrackTranscoders) {
+ if (!(started = transcoder->start())) {
+ break;
+ }
+ mThreadStates[static_cast<const void*>(transcoder.get())] = RUNNING;
+ }
+ }
+ if (!started) {
+ LOG(ERROR) << "Unable to start track transcoder.";
+ cancel();
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ return AMEDIA_OK;
+}
+
+media_status_t MediaTranscoder::requestStop(bool stopOnSync) {
+ std::scoped_lock lock{mThreadStateMutex};
+ if (mCancelled) {
+ LOG(DEBUG) << "MediaTranscoder already cancelled";
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+
+ if (!stopOnSync) {
+ mSampleWriterStopped = true;
+ mSampleWriter->stop();
+ }
+
+ mSampleReader->setEnforceSequentialAccess(false);
+ for (auto& transcoder : mTrackTranscoders) {
+ transcoder->stop(stopOnSync);
+ }
+
+ mCancelled = true;
+ return AMEDIA_OK;
+}
+
+void MediaTranscoder::waitForThreads() NO_THREAD_SAFETY_ANALYSIS {
+ std::unique_lock lock{mThreadStateMutex};
+ while (!mThreadsDone) {
+ mThreadsDoneSignal.wait(lock);
+ }
+}
+
+media_status_t MediaTranscoder::pause(std::shared_ptr<ndk::ScopedAParcel>* pausedState) {
+ media_status_t status = requestStop(true /* stopOnSync */);
+ if (status != AMEDIA_OK) {
+ return status;
+ }
+
+ waitForThreads();
+
+ // TODO: write internal states to parcel.
+ *pausedState = std::shared_ptr<::ndk::ScopedAParcel>(new ::ndk::ScopedAParcel());
+ return AMEDIA_OK;
+}
+
+media_status_t MediaTranscoder::cancel() {
+ media_status_t status = requestStop(false /* stopOnSync */);
+ if (status != AMEDIA_OK) {
+ return status;
+ }
+
+ waitForThreads();
+
+ // TODO: Release transcoders?
+ return AMEDIA_OK;
+}
+
+media_status_t MediaTranscoder::resume() {
+ // TODO: restore internal states from parcel.
+ return start();
+}
+
+} // namespace android
diff --git a/media/libmediatranscoding/transcoder/NdkCommon.cpp b/media/libmediatranscoding/transcoder/NdkCommon.cpp
new file mode 100644
index 0000000..e133cd6
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/NdkCommon.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NdkCommon"
+
+#include <android-base/logging.h>
+#include <media/NdkCommon.h>
+
+#include <cstdio>
+#include <cstring>
+#include <utility>
+
+/* TODO(b/153592281)
+ * Note: constants used by the native media tests but not available in media ndk api
+ */
+const char* AMEDIA_MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
+const char* AMEDIA_MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
+const char* AMEDIA_MIMETYPE_VIDEO_AV1 = "video/av01";
+const char* AMEDIA_MIMETYPE_VIDEO_AVC = "video/avc";
+const char* AMEDIA_MIMETYPE_VIDEO_HEVC = "video/hevc";
+const char* AMEDIA_MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es";
+const char* AMEDIA_MIMETYPE_VIDEO_H263 = "video/3gpp";
+
+/* TODO(b/153592281) */
+const char* TBD_AMEDIACODEC_PARAMETER_KEY_ALLOW_FRAME_DROP = "allow-frame-drop";
+const char* TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync";
+const char* TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE = "video-bitrate";
+const char* TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES = "max-bframes";
+
+/* TODO(lnilsson): Finalize value or adopt AMediaFormat key once available. */
+const char* TBD_AMEDIACODEC_PARAMETER_KEY_COLOR_TRANSFER_REQUEST = "color-transfer-request";
+const char* TBD_AMEDIACODEC_PARAMETER_KEY_BACKGROUND_MODE = "android._background-mode";
+
+namespace AMediaFormatUtils {
+
+#define DEFINE_FORMAT_VALUE_COPY_FUNC(_type, _typeName) \
+ bool CopyFormatEntry##_typeName(const char* key, AMediaFormat* from, AMediaFormat* to) { \
+ _type value; \
+ if (AMediaFormat_get##_typeName(from, key, &value)) { \
+ AMediaFormat_set##_typeName(to, key, value); \
+ return true; \
+ } \
+ return false; \
+ }
+
+DEFINE_FORMAT_VALUE_COPY_FUNC(const char*, String);
+DEFINE_FORMAT_VALUE_COPY_FUNC(int64_t, Int64);
+DEFINE_FORMAT_VALUE_COPY_FUNC(int32_t, Int32);
+DEFINE_FORMAT_VALUE_COPY_FUNC(float, Float);
+
+void CopyFormatEntries(AMediaFormat* from, AMediaFormat* to,
+ const std::vector<EntryCopier>& entries) {
+ if (from == nullptr || to == nullptr) {
+ LOG(ERROR) << "Cannot copy null formats";
+ return;
+ } else if (entries.empty()) {
+ LOG(WARNING) << "No entries to copy";
+ return;
+ }
+
+ for (auto& entry : entries) {
+ if (!entry.copy(entry.key, from, to) && entry.copy2 != nullptr) {
+ entry.copy2(entry.key, from, to);
+ }
+ }
+}
+
+#define DEFINE_SET_DEFAULT_FORMAT_VALUE_FUNC(_type, _typeName) \
+ bool SetDefaultFormatValue##_typeName(const char* key, AMediaFormat* format, _type value) { \
+ _type tmp; \
+ if (!AMediaFormat_get##_typeName(format, key, &tmp)) { \
+ AMediaFormat_set##_typeName(format, key, value); \
+ return true; \
+ } \
+ return false; \
+ }
+
+DEFINE_SET_DEFAULT_FORMAT_VALUE_FUNC(float, Float);
+DEFINE_SET_DEFAULT_FORMAT_VALUE_FUNC(int32_t, Int32);
+
+// Determines whether a track format describes HDR video content or not. The
+// logic is based on isHdr() in libstagefright/Utils.cpp.
+bool VideoIsHdr(AMediaFormat* format) {
+ // If VUI signals HDR content, this internal flag is set by the extractor.
+ int32_t isHdr;
+ if (AMediaFormat_getInt32(format, "android._is-hdr", &isHdr)) {
+ return isHdr;
+ }
+
+ // If container supplied HDR static info without transfer set, assume HDR.
+ const char* hdrInfo;
+ int32_t transfer;
+ if ((AMediaFormat_getString(format, AMEDIAFORMAT_KEY_HDR_STATIC_INFO, &hdrInfo) ||
+ AMediaFormat_getString(format, "hdr10-plus-info", &hdrInfo)) &&
+ !AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_COLOR_TRANSFER, &transfer)) {
+ return true;
+ }
+
+ // Otherwise, check if an HDR transfer function is set.
+ if (AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_COLOR_TRANSFER, &transfer)) {
+ return transfer == COLOR_TRANSFER_ST2084 || transfer == COLOR_TRANSFER_HLG;
+ }
+
+ return false;
+}
+} // namespace AMediaFormatUtils
diff --git a/media/libmediatranscoding/transcoder/PassthroughTrackTranscoder.cpp b/media/libmediatranscoding/transcoder/PassthroughTrackTranscoder.cpp
new file mode 100644
index 0000000..3335d6c
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/PassthroughTrackTranscoder.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "PassthroughTrackTranscoder"
+
+#include <android-base/logging.h>
+#include <media/PassthroughTrackTranscoder.h>
+#include <sys/prctl.h>
+
+namespace android {
+
+PassthroughTrackTranscoder::BufferPool::~BufferPool() {
+ for (auto it = mAddressSizeMap.begin(); it != mAddressSizeMap.end(); ++it) {
+ delete[] it->first;
+ }
+}
+
+uint8_t* PassthroughTrackTranscoder::BufferPool::getBufferWithSize(size_t minimumBufferSize)
+ NO_THREAD_SAFETY_ANALYSIS {
+ std::unique_lock lock(mMutex);
+
+ // Wait if maximum number of buffers are allocated but none are free.
+ while (mAddressSizeMap.size() >= mMaxBufferCount && mFreeBufferMap.empty() && !mAborted) {
+ mCondition.wait(lock);
+ }
+
+ if (mAborted) {
+ return nullptr;
+ }
+
+ // Check if the free list contains a large enough buffer.
+ auto it = mFreeBufferMap.lower_bound(minimumBufferSize);
+ if (it != mFreeBufferMap.end()) {
+ uint8_t* buffer = it->second;
+ mFreeBufferMap.erase(it);
+ return buffer;
+ }
+
+ // If the maximum buffer count is reached, remove an existing free buffer.
+ if (mAddressSizeMap.size() >= mMaxBufferCount) {
+ auto it = mFreeBufferMap.begin();
+ mAddressSizeMap.erase(it->second);
+ delete[] it->second;
+ mFreeBufferMap.erase(it);
+ }
+
+ // Allocate a new buffer.
+ uint8_t* buffer = new (std::nothrow) uint8_t[minimumBufferSize];
+ if (buffer == nullptr) {
+ LOG(ERROR) << "Unable to allocate new buffer of size: " << minimumBufferSize;
+ return nullptr;
+ }
+
+ // Add the buffer to the tracking set.
+ mAddressSizeMap.emplace(buffer, minimumBufferSize);
+ return buffer;
+}
+
+void PassthroughTrackTranscoder::BufferPool::returnBuffer(uint8_t* buffer) {
+ std::scoped_lock lock(mMutex);
+
+ if (buffer == nullptr || mAddressSizeMap.find(buffer) == mAddressSizeMap.end()) {
+ LOG(WARNING) << "Ignoring untracked buffer " << buffer;
+ return;
+ }
+
+ mFreeBufferMap.emplace(mAddressSizeMap[buffer], buffer);
+ mCondition.notify_one();
+}
+
+void PassthroughTrackTranscoder::BufferPool::abort() {
+ std::scoped_lock lock(mMutex);
+ mAborted = true;
+ mCondition.notify_all();
+}
+
+media_status_t PassthroughTrackTranscoder::configureDestinationFormat(
+ const std::shared_ptr<AMediaFormat>& destinationFormat __unused) {
+ // Called by MediaTrackTranscoder. Passthrough doesn't care about destination so just return ok.
+ return AMEDIA_OK;
+}
+
+media_status_t PassthroughTrackTranscoder::runTranscodeLoop(bool* stopped) {
+ prctl(PR_SET_NAME, (unsigned long)"PassthruThread", 0, 0, 0);
+
+ MediaSampleInfo info;
+ std::shared_ptr<MediaSample> sample;
+ bool eosReached = false;
+
+ // Notify the track format as soon as we start. It's same as the source format.
+ notifyTrackFormatAvailable();
+
+ MediaSample::OnSampleReleasedCallback bufferReleaseCallback =
+ [bufferPool = mBufferPool](MediaSample* sample) {
+ bufferPool->returnBuffer(const_cast<uint8_t*>(sample->buffer));
+ };
+
+ // Move samples until EOS is reached or transcoding is stopped.
+ while (mStopRequest != STOP_NOW && !eosReached) {
+ media_status_t status = mMediaSampleReader->getSampleInfoForTrack(mTrackIndex, &info);
+
+ if (status == AMEDIA_OK) {
+ uint8_t* buffer = mBufferPool->getBufferWithSize(info.size);
+ if (buffer == nullptr) {
+ if (mStopRequest == STOP_NOW) {
+ break;
+ }
+
+ LOG(ERROR) << "Unable to get buffer from pool";
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+
+ sample = MediaSample::createWithReleaseCallback(
+ buffer, 0 /* offset */, 0 /* bufferId */, bufferReleaseCallback);
+
+ status = mMediaSampleReader->readSampleDataForTrack(mTrackIndex, buffer, info.size);
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "Unable to read next sample data. Aborting transcode.";
+ return status;
+ }
+
+ } else if (status == AMEDIA_ERROR_END_OF_STREAM) {
+ sample = std::make_shared<MediaSample>();
+ eosReached = true;
+ } else {
+ LOG(ERROR) << "Unable to get next sample info. Aborting transcode.";
+ return status;
+ }
+
+ sample->info = info;
+ onOutputSampleAvailable(sample);
+
+ if (mStopRequest == STOP_ON_SYNC && info.flags & SAMPLE_FLAG_SYNC_SAMPLE) {
+ break;
+ }
+ }
+
+ if (mStopRequest != NONE && !eosReached) {
+ *stopped = true;
+ }
+ return AMEDIA_OK;
+}
+
+void PassthroughTrackTranscoder::abortTranscodeLoop() {
+ if (mStopRequest == STOP_NOW) {
+ mBufferPool->abort();
+ }
+}
+
+std::shared_ptr<AMediaFormat> PassthroughTrackTranscoder::getOutputFormat() const {
+ return mSourceFormat;
+}
+} // namespace android
diff --git a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
new file mode 100644
index 0000000..b43efcb
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
@@ -0,0 +1,650 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "VideoTrackTranscoder"
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <media/NdkCommon.h>
+#include <media/VideoTrackTranscoder.h>
+#include <sys/prctl.h>
+
+using namespace AMediaFormatUtils;
+
+namespace android {
+
+// Check that the codec sample flags have the expected NDK meaning.
+static_assert(SAMPLE_FLAG_CODEC_CONFIG == AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG,
+ "Sample flag mismatch: CODEC_CONFIG");
+static_assert(SAMPLE_FLAG_END_OF_STREAM == AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM,
+ "Sample flag mismatch: END_OF_STREAM");
+static_assert(SAMPLE_FLAG_PARTIAL_FRAME == AMEDIACODEC_BUFFER_FLAG_PARTIAL_FRAME,
+ "Sample flag mismatch: PARTIAL_FRAME");
+
+// Color format defined by surface. (See MediaCodecInfo.CodecCapabilities#COLOR_FormatSurface.)
+static constexpr int32_t kColorFormatSurface = 0x7f000789;
+// Default key frame interval in seconds.
+static constexpr float kDefaultKeyFrameIntervalSeconds = 1.0f;
+// Default codec operating rate.
+static int32_t kDefaultCodecOperatingRate720P = base::GetIntProperty(
+ "debug.media.transcoding.codec_max_operating_rate_720P", /*default*/ 480);
+static int32_t kDefaultCodecOperatingRate1080P = base::GetIntProperty(
+ "debug.media.transcoding.codec_max_operating_rate_1080P", /*default*/ 240);
+// Default codec priority.
+static constexpr int32_t kDefaultCodecPriority = 1;
+// Default bitrate, in case source estimation fails.
+static constexpr int32_t kDefaultBitrateMbps = 10 * 1000 * 1000;
+// Default frame rate.
+static constexpr int32_t kDefaultFrameRate = 30;
+
+template <typename T>
+void VideoTrackTranscoder::BlockingQueue<T>::push(T const& value, bool front) {
+ {
+ std::scoped_lock lock(mMutex);
+ if (mAborted) {
+ return;
+ }
+
+ if (front) {
+ mQueue.push_front(value);
+ } else {
+ mQueue.push_back(value);
+ }
+ }
+ mCondition.notify_one();
+}
+
+template <typename T>
+T VideoTrackTranscoder::BlockingQueue<T>::pop() {
+ std::unique_lock lock(mMutex);
+ while (mQueue.empty()) {
+ mCondition.wait(lock);
+ }
+ T value = mQueue.front();
+ mQueue.pop_front();
+ return value;
+}
+
+// Note: Do not call if another thread might waiting in pop.
+template <typename T>
+void VideoTrackTranscoder::BlockingQueue<T>::abort() {
+ std::scoped_lock lock(mMutex);
+ mAborted = true;
+ mQueue.clear();
+}
+
+// The CodecWrapper class is used to let AMediaCodec instances outlive the transcoder object itself
+// by giving the codec a weak pointer to the transcoder. Codecs wrapped in this object are kept
+// alive by the transcoder and the codec's outstanding buffers. Once the transcoder stops and all
+// output buffers have been released by downstream components the codec will also be released.
+class VideoTrackTranscoder::CodecWrapper {
+public:
+ CodecWrapper(AMediaCodec* codec, const std::weak_ptr<VideoTrackTranscoder>& transcoder)
+ : mCodec(codec), mTranscoder(transcoder), mCodecStarted(false) {}
+ ~CodecWrapper() {
+ if (mCodecStarted) {
+ AMediaCodec_stop(mCodec);
+ }
+ AMediaCodec_delete(mCodec);
+ }
+
+ AMediaCodec* getCodec() { return mCodec; }
+ std::shared_ptr<VideoTrackTranscoder> getTranscoder() const { return mTranscoder.lock(); };
+ void setStarted() { mCodecStarted = true; }
+
+private:
+ AMediaCodec* mCodec;
+ std::weak_ptr<VideoTrackTranscoder> mTranscoder;
+ bool mCodecStarted;
+};
+
+// Dispatch responses to codec callbacks onto the message queue.
+struct AsyncCodecCallbackDispatch {
+ static void onAsyncInputAvailable(AMediaCodec* codec, void* userdata, int32_t index) {
+ VideoTrackTranscoder::CodecWrapper* wrapper =
+ static_cast<VideoTrackTranscoder::CodecWrapper*>(userdata);
+ if (auto transcoder = wrapper->getTranscoder()) {
+ if (codec == transcoder->mDecoder) {
+ transcoder->mCodecMessageQueue.push(
+ [transcoder, index] { transcoder->enqueueInputSample(index); });
+ }
+ }
+ }
+
+ static void onAsyncOutputAvailable(AMediaCodec* codec, void* userdata, int32_t index,
+ AMediaCodecBufferInfo* bufferInfoPtr) {
+ VideoTrackTranscoder::CodecWrapper* wrapper =
+ static_cast<VideoTrackTranscoder::CodecWrapper*>(userdata);
+ AMediaCodecBufferInfo bufferInfo = *bufferInfoPtr;
+ if (auto transcoder = wrapper->getTranscoder()) {
+ transcoder->mCodecMessageQueue.push([transcoder, index, codec, bufferInfo] {
+ if (codec == transcoder->mDecoder) {
+ transcoder->transferBuffer(index, bufferInfo);
+ } else if (codec == transcoder->mEncoder->getCodec()) {
+ transcoder->dequeueOutputSample(index, bufferInfo);
+ }
+ });
+ }
+ }
+
+ static void onAsyncFormatChanged(AMediaCodec* codec, void* userdata, AMediaFormat* format) {
+ VideoTrackTranscoder::CodecWrapper* wrapper =
+ static_cast<VideoTrackTranscoder::CodecWrapper*>(userdata);
+ if (auto transcoder = wrapper->getTranscoder()) {
+ const bool isDecoder = codec == transcoder->mDecoder;
+ const char* kCodecName = (isDecoder ? "Decoder" : "Encoder");
+ LOG(INFO) << kCodecName << " format changed: " << AMediaFormat_toString(format);
+ transcoder->mCodecMessageQueue.push([transcoder, format, isDecoder] {
+ transcoder->updateTrackFormat(format, isDecoder);
+ });
+ }
+ }
+
+ static void onAsyncError(AMediaCodec* codec, void* userdata, media_status_t error,
+ int32_t actionCode, const char* detail) {
+ LOG(ERROR) << "Error from codec " << codec << ", userdata " << userdata << ", error "
+ << error << ", action " << actionCode << ", detail " << detail;
+ VideoTrackTranscoder::CodecWrapper* wrapper =
+ static_cast<VideoTrackTranscoder::CodecWrapper*>(userdata);
+ if (auto transcoder = wrapper->getTranscoder()) {
+ transcoder->mCodecMessageQueue.push(
+ [transcoder, error] { transcoder->mStatus = error; }, true);
+ }
+ }
+};
+
+// static
+std::shared_ptr<VideoTrackTranscoder> VideoTrackTranscoder::create(
+ const std::weak_ptr<MediaTrackTranscoderCallback>& transcoderCallback, pid_t pid,
+ uid_t uid) {
+ return std::shared_ptr<VideoTrackTranscoder>(
+ new VideoTrackTranscoder(transcoderCallback, pid, uid));
+}
+
+VideoTrackTranscoder::~VideoTrackTranscoder() {
+ if (mDecoder != nullptr) {
+ AMediaCodec_delete(mDecoder);
+ }
+
+ if (mSurface != nullptr) {
+ ANativeWindow_release(mSurface);
+ }
+}
+
+// Search the default operating rate based on resolution.
+static int32_t getDefaultOperatingRate(AMediaFormat* encoderFormat) {
+ int32_t width, height;
+ if (AMediaFormat_getInt32(encoderFormat, AMEDIAFORMAT_KEY_WIDTH, &width) && (width > 0) &&
+ AMediaFormat_getInt32(encoderFormat, AMEDIAFORMAT_KEY_HEIGHT, &height) && (height > 0)) {
+ if ((width == 1280 && height == 720) || (width == 720 && height == 1280)) {
+ return kDefaultCodecOperatingRate720P;
+ } else if ((width == 1920 && height == 1080) || (width == 1080 && height == 1920)) {
+ return kDefaultCodecOperatingRate1080P;
+ } else {
+ LOG(WARNING) << "Could not find default operating rate: " << width << " " << height;
+ // Don't set operating rate if the correct dimensions are not found.
+ }
+ } else {
+ LOG(ERROR) << "Failed to get default operating rate due to missing resolution";
+ }
+ return -1;
+}
+
+// Creates and configures the codecs.
+media_status_t VideoTrackTranscoder::configureDestinationFormat(
+ const std::shared_ptr<AMediaFormat>& destinationFormat) {
+ media_status_t status = AMEDIA_OK;
+
+ if (destinationFormat == nullptr) {
+ LOG(ERROR) << "Destination format is null, use passthrough transcoder";
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ AMediaFormat* encoderFormat = AMediaFormat_new();
+ if (!encoderFormat || AMediaFormat_copy(encoderFormat, destinationFormat.get()) != AMEDIA_OK) {
+ LOG(ERROR) << "Unable to copy destination format";
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!AMediaFormat_getInt32(encoderFormat, AMEDIAFORMAT_KEY_BIT_RATE, &mConfiguredBitrate)) {
+ status = mMediaSampleReader->getEstimatedBitrateForTrack(mTrackIndex, &mConfiguredBitrate);
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "Unable to estimate bitrate. Using default " << kDefaultBitrateMbps;
+ mConfiguredBitrate = kDefaultBitrateMbps;
+ }
+
+ LOG(INFO) << "Configuring bitrate " << mConfiguredBitrate;
+ AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_BIT_RATE, mConfiguredBitrate);
+ }
+
+ SetDefaultFormatValueFloat(AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, encoderFormat,
+ kDefaultKeyFrameIntervalSeconds);
+
+ int32_t operatingRate = getDefaultOperatingRate(encoderFormat);
+
+ if (operatingRate != -1) {
+ float tmpf;
+ int32_t tmpi;
+ if (!AMediaFormat_getFloat(encoderFormat, AMEDIAFORMAT_KEY_OPERATING_RATE, &tmpf) &&
+ !AMediaFormat_getInt32(encoderFormat, AMEDIAFORMAT_KEY_OPERATING_RATE, &tmpi)) {
+ AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_OPERATING_RATE, operatingRate);
+ }
+ }
+
+ SetDefaultFormatValueInt32(AMEDIAFORMAT_KEY_PRIORITY, encoderFormat, kDefaultCodecPriority);
+ SetDefaultFormatValueInt32(AMEDIAFORMAT_KEY_FRAME_RATE, encoderFormat, kDefaultFrameRate);
+ AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, kColorFormatSurface);
+
+ // Always encode without rotation. The rotation degree will be transferred directly to
+ // MediaSampleWriter track format, and MediaSampleWriter will call AMediaMuxer_setOrientationHint.
+ AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_ROTATION, 0);
+
+ // Request encoder to use background priorities by default.
+ SetDefaultFormatValueInt32(TBD_AMEDIACODEC_PARAMETER_KEY_BACKGROUND_MODE, encoderFormat,
+ 1 /* true */);
+
+ mDestinationFormat = std::shared_ptr<AMediaFormat>(encoderFormat, &AMediaFormat_delete);
+
+ // Create and configure the encoder.
+ const char* destinationMime = nullptr;
+ bool ok = AMediaFormat_getString(mDestinationFormat.get(), AMEDIAFORMAT_KEY_MIME,
+ &destinationMime);
+ if (!ok) {
+ LOG(ERROR) << "Destination MIME type is required for transcoding.";
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+// TODO: replace __ANDROID_API_FUTURE__with 31 when it's official (b/178144708)
+#define __TRANSCODING_MIN_API__ __ANDROID_API_FUTURE__
+
+ AMediaCodec* encoder;
+ if (__builtin_available(android __TRANSCODING_MIN_API__, *)) {
+ encoder = AMediaCodec_createEncoderByTypeForClient(destinationMime, mPid, mUid);
+ } else {
+ encoder = AMediaCodec_createEncoderByType(destinationMime);
+ }
+ if (encoder == nullptr) {
+ LOG(ERROR) << "Unable to create encoder for type " << destinationMime;
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+ mEncoder = std::make_shared<CodecWrapper>(encoder, shared_from_this());
+
+ LOG(INFO) << "Configuring encoder with: " << AMediaFormat_toString(mDestinationFormat.get());
+ status = AMediaCodec_configure(mEncoder->getCodec(), mDestinationFormat.get(),
+ NULL /* surface */, NULL /* crypto */,
+ AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "Unable to configure video encoder: " << status;
+ return status;
+ }
+
+ status = AMediaCodec_createInputSurface(mEncoder->getCodec(), &mSurface);
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "Unable to create an encoder input surface: %d" << status;
+ return status;
+ }
+
+ // Create and configure the decoder.
+ const char* sourceMime = nullptr;
+ ok = AMediaFormat_getString(mSourceFormat.get(), AMEDIAFORMAT_KEY_MIME, &sourceMime);
+ if (!ok) {
+ LOG(ERROR) << "Source MIME type is required for transcoding.";
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (__builtin_available(android __TRANSCODING_MIN_API__, *)) {
+ mDecoder = AMediaCodec_createDecoderByTypeForClient(sourceMime, mPid, mUid);
+ } else {
+ mDecoder = AMediaCodec_createDecoderByType(sourceMime);
+ }
+ if (mDecoder == nullptr) {
+ LOG(ERROR) << "Unable to create decoder for type " << sourceMime;
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+
+ auto decoderFormat = std::shared_ptr<AMediaFormat>(AMediaFormat_new(), &AMediaFormat_delete);
+ if (!decoderFormat ||
+ AMediaFormat_copy(decoderFormat.get(), mSourceFormat.get()) != AMEDIA_OK) {
+ LOG(ERROR) << "Unable to copy source format";
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ // Request decoder to convert HDR content to SDR.
+ const bool sourceIsHdr = VideoIsHdr(mSourceFormat.get());
+ if (sourceIsHdr) {
+ AMediaFormat_setInt32(decoderFormat.get(),
+ TBD_AMEDIACODEC_PARAMETER_KEY_COLOR_TRANSFER_REQUEST,
+ COLOR_TRANSFER_SDR_VIDEO);
+ }
+
+ // Prevent decoder from overwriting frames that the encoder has not yet consumed.
+ AMediaFormat_setInt32(decoderFormat.get(), TBD_AMEDIACODEC_PARAMETER_KEY_ALLOW_FRAME_DROP, 0);
+
+ // Copy over configurations that apply to both encoder and decoder.
+ static const std::vector<EntryCopier> kEncoderEntriesToCopy{
+ ENTRY_COPIER2(AMEDIAFORMAT_KEY_OPERATING_RATE, Float, Int32),
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_PRIORITY, Int32),
+ ENTRY_COPIER(TBD_AMEDIACODEC_PARAMETER_KEY_BACKGROUND_MODE, Int32),
+ };
+ CopyFormatEntries(mDestinationFormat.get(), decoderFormat.get(), kEncoderEntriesToCopy);
+
+ LOG(INFO) << "Configuring decoder with: " << AMediaFormat_toString(decoderFormat.get());
+ status = AMediaCodec_configure(mDecoder, decoderFormat.get(), mSurface, NULL /* crypto */,
+ 0 /* flags */);
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "Unable to configure video decoder: " << status;
+ return status;
+ }
+
+ if (sourceIsHdr) {
+ bool supported = false;
+ AMediaFormat* inputFormat = AMediaCodec_getInputFormat(mDecoder);
+
+ if (inputFormat != nullptr) {
+ int32_t transferFunc;
+ supported = AMediaFormat_getInt32(inputFormat,
+ TBD_AMEDIACODEC_PARAMETER_KEY_COLOR_TRANSFER_REQUEST,
+ &transferFunc) &&
+ transferFunc == COLOR_TRANSFER_SDR_VIDEO;
+ AMediaFormat_delete(inputFormat);
+ }
+
+ if (!supported) {
+ LOG(ERROR) << "HDR to SDR conversion unsupported by the codec";
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+ }
+
+ // Configure codecs to run in async mode.
+ AMediaCodecOnAsyncNotifyCallback asyncCodecCallbacks = {
+ .onAsyncInputAvailable = AsyncCodecCallbackDispatch::onAsyncInputAvailable,
+ .onAsyncOutputAvailable = AsyncCodecCallbackDispatch::onAsyncOutputAvailable,
+ .onAsyncFormatChanged = AsyncCodecCallbackDispatch::onAsyncFormatChanged,
+ .onAsyncError = AsyncCodecCallbackDispatch::onAsyncError};
+
+ // Note: The decoder does not need its own wrapper because its lifetime is tied to the
+ // transcoder. But the same callbacks are reused for decoder and encoder so we pass the encoder
+ // wrapper as userdata here but never read the codec from it in the callback.
+ status = AMediaCodec_setAsyncNotifyCallback(mDecoder, asyncCodecCallbacks, mEncoder.get());
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "Unable to set decoder to async mode: " << status;
+ return status;
+ }
+
+ status = AMediaCodec_setAsyncNotifyCallback(mEncoder->getCodec(), asyncCodecCallbacks,
+ mEncoder.get());
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "Unable to set encoder to async mode: " << status;
+ return status;
+ }
+
+ return AMEDIA_OK;
+}
+
+void VideoTrackTranscoder::enqueueInputSample(int32_t bufferIndex) {
+ media_status_t status = AMEDIA_OK;
+
+ if (mEosFromSource) {
+ return;
+ }
+
+ status = mMediaSampleReader->getSampleInfoForTrack(mTrackIndex, &mSampleInfo);
+ if (status != AMEDIA_OK && status != AMEDIA_ERROR_END_OF_STREAM) {
+ LOG(ERROR) << "Error getting next sample info: " << status;
+ mStatus = status;
+ return;
+ }
+ const bool endOfStream = (status == AMEDIA_ERROR_END_OF_STREAM);
+
+ if (!endOfStream) {
+ size_t bufferSize = 0;
+ uint8_t* sourceBuffer = AMediaCodec_getInputBuffer(mDecoder, bufferIndex, &bufferSize);
+ if (sourceBuffer == nullptr) {
+ LOG(ERROR) << "Decoder returned a NULL input buffer.";
+ mStatus = AMEDIA_ERROR_UNKNOWN;
+ return;
+ } else if (bufferSize < mSampleInfo.size) {
+ LOG(ERROR) << "Decoder returned an input buffer that is smaller than the sample.";
+ mStatus = AMEDIA_ERROR_UNKNOWN;
+ return;
+ }
+
+ status = mMediaSampleReader->readSampleDataForTrack(mTrackIndex, sourceBuffer,
+ mSampleInfo.size);
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "Unable to read next sample data. Aborting transcode.";
+ mStatus = status;
+ return;
+ }
+
+ if (mSampleInfo.size) {
+ ++mInputFrameCount;
+ }
+ } else {
+ LOG(DEBUG) << "EOS from source.";
+ mEosFromSource = true;
+ }
+
+ status = AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, mSampleInfo.size,
+ mSampleInfo.presentationTimeUs, mSampleInfo.flags);
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "Unable to queue input buffer for decode: " << status;
+ mStatus = status;
+ return;
+ }
+}
+
+void VideoTrackTranscoder::transferBuffer(int32_t bufferIndex, AMediaCodecBufferInfo bufferInfo) {
+ if (bufferIndex >= 0) {
+ bool needsRender = bufferInfo.size > 0;
+ AMediaCodec_releaseOutputBuffer(mDecoder, bufferIndex, needsRender);
+ }
+
+ if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
+ LOG(DEBUG) << "EOS from decoder.";
+ media_status_t status = AMediaCodec_signalEndOfInputStream(mEncoder->getCodec());
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "SignalEOS on encoder returned error: " << status;
+ mStatus = status;
+ }
+ }
+}
+
+void VideoTrackTranscoder::dequeueOutputSample(int32_t bufferIndex,
+ AMediaCodecBufferInfo bufferInfo) {
+ if (bufferIndex >= 0) {
+ size_t sampleSize = 0;
+ uint8_t* buffer =
+ AMediaCodec_getOutputBuffer(mEncoder->getCodec(), bufferIndex, &sampleSize);
+
+ MediaSample::OnSampleReleasedCallback bufferReleaseCallback =
+ [encoder = mEncoder](MediaSample* sample) {
+ AMediaCodec_releaseOutputBuffer(encoder->getCodec(), sample->bufferId,
+ false /* render */);
+ };
+
+ std::shared_ptr<MediaSample> sample = MediaSample::createWithReleaseCallback(
+ buffer, bufferInfo.offset, bufferIndex, bufferReleaseCallback);
+ sample->info.size = bufferInfo.size;
+ sample->info.flags = bufferInfo.flags;
+ sample->info.presentationTimeUs = bufferInfo.presentationTimeUs;
+
+ if (bufferInfo.size > 0 && (bufferInfo.flags & SAMPLE_FLAG_CODEC_CONFIG) == 0) {
+ ++mOutputFrameCount;
+ }
+ onOutputSampleAvailable(sample);
+
+ mLastSampleWasSync = sample->info.flags & SAMPLE_FLAG_SYNC_SAMPLE;
+ }
+
+ if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
+ LOG(DEBUG) << "EOS from encoder.";
+ mEosFromEncoder = true;
+
+ if (mInputFrameCount != mOutputFrameCount) {
+ LOG(WARNING) << "Input / Output frame count mismatch: " << mInputFrameCount << " vs "
+ << mOutputFrameCount;
+ if (mInputFrameCount > 0 && mOutputFrameCount == 0) {
+ LOG(ERROR) << "Encoder did not produce any output frames.";
+ mStatus = AMEDIA_ERROR_UNKNOWN;
+ }
+ }
+ }
+}
+
+void VideoTrackTranscoder::updateTrackFormat(AMediaFormat* outputFormat, bool fromDecoder) {
+ if (fromDecoder) {
+ static const std::vector<AMediaFormatUtils::EntryCopier> kValuesToCopy{
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_RANGE, Int32),
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_STANDARD, Int32),
+ ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_TRANSFER, Int32),
+ };
+ AMediaFormat* params = AMediaFormat_new();
+ if (params != nullptr) {
+ AMediaFormatUtils::CopyFormatEntries(outputFormat, params, kValuesToCopy);
+ if (AMediaCodec_setParameters(mEncoder->getCodec(), params) != AMEDIA_OK) {
+ LOG(WARNING) << "Unable to update encoder with color information";
+ }
+ AMediaFormat_delete(params);
+ }
+ return;
+ }
+
+ if (mActualOutputFormat != nullptr) {
+ LOG(WARNING) << "Ignoring duplicate format change.";
+ return;
+ }
+
+ AMediaFormat* formatCopy = AMediaFormat_new();
+ if (!formatCopy || AMediaFormat_copy(formatCopy, outputFormat) != AMEDIA_OK) {
+ LOG(ERROR) << "Unable to copy outputFormat";
+ AMediaFormat_delete(formatCopy);
+ mStatus = AMEDIA_ERROR_INVALID_PARAMETER;
+ return;
+ }
+
+ // Generate the actual track format for muxer based on the encoder output format,
+ // since many vital information comes in the encoder format (eg. CSD).
+ // Transfer necessary fields from the user-configured track format (derived from
+ // source track format and user transcoding request) where needed.
+
+ // Transfer SAR settings:
+ // If mDestinationFormat has SAR set, it means the original source has SAR specified
+ // at container level. This is supposed to override any SAR settings in the bitstream,
+ // thus should always be transferred to the container of the transcoded file.
+ int32_t sarWidth, sarHeight;
+ if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_SAR_WIDTH, &sarWidth) &&
+ (sarWidth > 0) &&
+ AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_SAR_HEIGHT, &sarHeight) &&
+ (sarHeight > 0)) {
+ AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_SAR_WIDTH, sarWidth);
+ AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_SAR_HEIGHT, sarHeight);
+ }
+ // Transfer DAR settings.
+ int32_t displayWidth, displayHeight;
+ if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_DISPLAY_WIDTH, &displayWidth) &&
+ (displayWidth > 0) &&
+ AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_DISPLAY_HEIGHT,
+ &displayHeight) &&
+ (displayHeight > 0)) {
+ AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_DISPLAY_WIDTH, displayWidth);
+ AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_DISPLAY_HEIGHT, displayHeight);
+ }
+
+ // Transfer rotation settings.
+ // Note that muxer itself doesn't take rotation from the track format. It requires
+ // AMediaMuxer_setOrientationHint to set the rotation. Here we pass the rotation to
+ // MediaSampleWriter using the track format. MediaSampleWriter will then call
+ // AMediaMuxer_setOrientationHint as needed.
+ int32_t rotation;
+ if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_ROTATION, &rotation) &&
+ (rotation != 0)) {
+ AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_ROTATION, rotation);
+ }
+
+ // Transfer track duration.
+ // Preserve the source track duration by sending it to MediaSampleWriter.
+ int64_t durationUs;
+ if (AMediaFormat_getInt64(mSourceFormat.get(), AMEDIAFORMAT_KEY_DURATION, &durationUs) &&
+ durationUs > 0) {
+ AMediaFormat_setInt64(formatCopy, AMEDIAFORMAT_KEY_DURATION, durationUs);
+ }
+
+ // TODO: transfer other fields as required.
+
+ mActualOutputFormat = std::shared_ptr<AMediaFormat>(formatCopy, &AMediaFormat_delete);
+ LOG(INFO) << "Actual output format: " << AMediaFormat_toString(formatCopy);
+
+ notifyTrackFormatAvailable();
+}
+
+media_status_t VideoTrackTranscoder::runTranscodeLoop(bool* stopped) {
+ prctl(PR_SET_NAME, (unsigned long)"VideTranscodTrd", 0, 0, 0);
+
+ // Push start decoder and encoder as two messages, so that these are subject to the
+ // stop request as well. If the session is cancelled (or paused) immediately after start,
+ // we don't need to waste time start then stop the codecs.
+ mCodecMessageQueue.push([this] {
+ media_status_t status = AMediaCodec_start(mDecoder);
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "Unable to start video decoder: " << status;
+ mStatus = status;
+ }
+ });
+
+ mCodecMessageQueue.push([this] {
+ media_status_t status = AMediaCodec_start(mEncoder->getCodec());
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "Unable to start video encoder: " << status;
+ mStatus = status;
+ }
+ mEncoder->setStarted();
+ });
+
+ // Process codec events until EOS is reached, transcoding is stopped or an error occurs.
+ while (mStopRequest != STOP_NOW && !mEosFromEncoder && mStatus == AMEDIA_OK) {
+ std::function<void()> message = mCodecMessageQueue.pop();
+ message();
+
+ if (mStopRequest == STOP_ON_SYNC && mLastSampleWasSync) {
+ break;
+ }
+ }
+
+ mCodecMessageQueue.abort();
+ AMediaCodec_stop(mDecoder);
+
+ // Signal if transcoding was stopped before it finished.
+ if (mStopRequest != NONE && !mEosFromEncoder && mStatus == AMEDIA_OK) {
+ *stopped = true;
+ }
+
+ return mStatus;
+}
+
+void VideoTrackTranscoder::abortTranscodeLoop() {
+ if (mStopRequest == STOP_NOW) {
+ // Wake up transcoder thread.
+ mCodecMessageQueue.push([] {}, true /* front */);
+ }
+}
+
+std::shared_ptr<AMediaFormat> VideoTrackTranscoder::getOutputFormat() const {
+ return mActualOutputFormat;
+}
+
+} // namespace android
diff --git a/media/libmediatranscoding/transcoder/benchmark/Android.bp b/media/libmediatranscoding/transcoder/benchmark/Android.bp
new file mode 100644
index 0000000..459f0ae
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/benchmark/Android.bp
@@ -0,0 +1,41 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_defaults {
+ name: "benchmarkdefaults",
+ shared_libs: [
+ "libmediandk",
+ "libbase",
+ "libbinder_ndk",
+ "libbinder",
+ "libutils",
+ "libnativewindow",
+ ],
+ static_libs: ["libmediatranscoder", "libgoogle-benchmark"],
+ test_config_template: "AndroidTestTemplate.xml",
+ test_suites: ["device-tests", "TranscoderBenchmarks"],
+}
+
+cc_test {
+ name: "MediaTranscoderBenchmark",
+ srcs: ["MediaTranscoderBenchmark.cpp"],
+ defaults: ["benchmarkdefaults"],
+}
+
+cc_test {
+ name: "MediaSampleReaderBenchmark",
+ srcs: ["MediaSampleReaderBenchmark.cpp"],
+ defaults: ["benchmarkdefaults"],
+}
+
+cc_test {
+ name: "MediaTrackTranscoderBenchmark",
+ srcs: ["MediaTrackTranscoderBenchmark.cpp"],
+ defaults: ["benchmarkdefaults"],
+}
diff --git a/media/libmediatranscoding/transcoder/benchmark/AndroidTestTemplate.xml b/media/libmediatranscoding/transcoder/benchmark/AndroidTestTemplate.xml
new file mode 100644
index 0000000..683f07b
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/benchmark/AndroidTestTemplate.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Unit test configuration for {MODULE}">
+ <option name="test-suite-tag" value="TranscoderBenchmarks" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="false" />
+ <option name="push-file" key="{MODULE}" value="/data/local/tmp/{MODULE}" />
+ <option name="push-file"
+ key="https://storage.googleapis.com/android_media/frameworks/av/media/libmediatranscoding/transcoder/benchmark/TranscodingBenchmark-1.2.zip?unzip=true"
+ value="/data/local/tmp/TranscodingBenchmark/" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GoogleBenchmarkTest" >
+ <option name="native-benchmark-device-path" value="/data/local/tmp" />
+ <option name="benchmark-module-name" value="{MODULE}" />
+ </test>
+</configuration>
+
diff --git a/media/libmediatranscoding/transcoder/benchmark/MediaSampleReaderBenchmark.cpp b/media/libmediatranscoding/transcoder/benchmark/MediaSampleReaderBenchmark.cpp
new file mode 100644
index 0000000..f0b9304
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/benchmark/MediaSampleReaderBenchmark.cpp
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+/**
+ * MediaSampleReader benchmark tests.
+ *
+ * How to run the benchmark:
+ *
+ * 1. Download the media assets from http://go/transcodingbenchmark and push the directory
+ * ("TranscodingBenchmark") to /data/local/tmp.
+ *
+ * 2. Compile the benchmark and sync to device:
+ * $ mm -j72 && adb sync
+ *
+ * 3. Run:
+ * $ adb shell /data/nativetest64/MediaSampleReaderBenchmark/MediaSampleReaderBenchmark
+ */
+
+#define LOG_TAG "MediaSampleReaderBenchmark"
+
+#include <android-base/logging.h>
+#include <benchmark/benchmark.h>
+#include <fcntl.h>
+#include <media/MediaSampleReaderNDK.h>
+#include <unistd.h>
+
+#include <thread>
+
+using namespace android;
+
+static void ReadMediaSamples(benchmark::State& state, const std::string& srcFileName,
+ bool readAudio, bool sequentialAccess = false) {
+ // Asset directory.
+ static const std::string kAssetDirectory = "/data/local/tmp/TranscodingBenchmark/";
+
+ int srcFd = 0;
+ std::string srcPath = kAssetDirectory + srcFileName;
+
+ if ((srcFd = open(srcPath.c_str(), O_RDONLY)) < 0) {
+ state.SkipWithError("Unable to open source file");
+ return;
+ }
+
+ const size_t fileSize = lseek(srcFd, 0, SEEK_END);
+ lseek(srcFd, 0, SEEK_SET);
+
+ for (auto _ : state) {
+ auto sampleReader = MediaSampleReaderNDK::createFromFd(srcFd, 0, fileSize);
+ if (sampleReader->setEnforceSequentialAccess(sequentialAccess) != AMEDIA_OK) {
+ state.SkipWithError("setEnforceSequentialAccess failed");
+ return;
+ }
+
+ // Select tracks.
+ std::vector<int> trackIndices;
+ for (int trackIndex = 0; trackIndex < sampleReader->getTrackCount(); ++trackIndex) {
+ const char* mime = nullptr;
+
+ AMediaFormat* trackFormat = sampleReader->getTrackFormat(trackIndex);
+ AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+
+ if (strncmp(mime, "video/", 6) == 0) {
+ int32_t frameCount;
+ if (AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_FRAME_COUNT, &frameCount)) {
+ state.counters["VideoFrameRate"] =
+ benchmark::Counter(frameCount, benchmark::Counter::kIsRate);
+ }
+ } else if (!readAudio && strncmp(mime, "audio/", 6) == 0) {
+ continue;
+ }
+
+ trackIndices.push_back(trackIndex);
+ sampleReader->selectTrack(trackIndex);
+ }
+
+ // Start threads.
+ std::vector<std::thread> trackThreads;
+ for (auto trackIndex : trackIndices) {
+ trackThreads.emplace_back([trackIndex, sampleReader, &state] {
+ LOG(INFO) << "Track " << trackIndex << " started";
+ MediaSampleInfo info;
+
+ size_t bufferSize = 0;
+ std::unique_ptr<uint8_t[]> buffer;
+
+ while (true) {
+ media_status_t status = sampleReader->getSampleInfoForTrack(trackIndex, &info);
+ if (status == AMEDIA_ERROR_END_OF_STREAM) {
+ break;
+ }
+
+ if (info.size > bufferSize) {
+ bufferSize = info.size;
+ buffer.reset(new uint8_t[bufferSize]);
+ }
+
+ status = sampleReader->readSampleDataForTrack(trackIndex, buffer.get(),
+ bufferSize);
+ if (status != AMEDIA_OK) {
+ state.SkipWithError("Error reading sample data");
+ break;
+ }
+ }
+
+ LOG(INFO) << "Track " << trackIndex << " finished";
+ });
+ }
+
+ // Join threads.
+ for (auto& thread : trackThreads) {
+ thread.join();
+ }
+ }
+
+ close(srcFd);
+}
+
+// Benchmark registration wrapper for transcoding.
+#define TRANSCODER_BENCHMARK(func) \
+ BENCHMARK(func)->UseRealTime()->MeasureProcessCPUTime()->Unit(benchmark::kMillisecond)
+
+static void BM_MediaSampleReader_AudioVideo_Parallel(benchmark::State& state) {
+ ReadMediaSamples(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
+ true /* readAudio */);
+}
+
+static void BM_MediaSampleReader_AudioVideo_Sequential(benchmark::State& state) {
+ ReadMediaSamples(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
+ true /* readAudio */, true /* sequentialAccess */);
+}
+
+static void BM_MediaSampleReader_Video(benchmark::State& state) {
+ ReadMediaSamples(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
+ false /* readAudio */);
+}
+
+TRANSCODER_BENCHMARK(BM_MediaSampleReader_AudioVideo_Parallel);
+TRANSCODER_BENCHMARK(BM_MediaSampleReader_AudioVideo_Sequential);
+TRANSCODER_BENCHMARK(BM_MediaSampleReader_Video);
+
+BENCHMARK_MAIN();
diff --git a/media/libmediatranscoding/transcoder/benchmark/MediaTrackTranscoderBenchmark.cpp b/media/libmediatranscoding/transcoder/benchmark/MediaTrackTranscoderBenchmark.cpp
new file mode 100644
index 0000000..d6ed2c6
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/benchmark/MediaTrackTranscoderBenchmark.cpp
@@ -0,0 +1,456 @@
+/*
+ * 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.
+ */
+
+/**
+ * Native media track transcoder benchmark tests.
+ *
+ * How to run the benchmark:
+ *
+ * 1. Download the media assets from http://go/transcodingbenchmark and push the directory
+ * ("TranscodingBenchmark") to /data/local/tmp.
+ *
+ * 2. Compile the benchmark and sync to device:
+ * $ mm -j72 && adb sync
+ *
+ * 3. Run:
+ * $ adb shell /data/nativetest64/MediaTrackTranscoderBenchmark/MediaTrackTranscoderBenchmark
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "MediaTrackTranscoderBenchmark"
+
+#include <android-base/logging.h>
+#include <android/binder_process.h>
+#include <benchmark/benchmark.h>
+#include <fcntl.h>
+#include <media/MediaSampleReader.h>
+#include <media/MediaSampleReaderNDK.h>
+#include <media/MediaTrackTranscoder.h>
+#include <media/MediaTrackTranscoderCallback.h>
+#include <media/NdkCommon.h>
+#include <media/PassthroughTrackTranscoder.h>
+#include <media/VideoTrackTranscoder.h>
+
+using namespace android;
+
+typedef enum {
+ kVideo,
+ kAudio,
+} MediaType;
+
+class TrackTranscoderCallbacks : public MediaTrackTranscoderCallback {
+public:
+ virtual void onTrackFormatAvailable(const MediaTrackTranscoder* transcoder __unused) override {}
+
+ virtual void onTrackFinished(const MediaTrackTranscoder* transcoder __unused) override {
+ std::unique_lock lock(mMutex);
+ mFinished = true;
+ mCondition.notify_all();
+ }
+
+ virtual void onTrackStopped(const MediaTrackTranscoder* transcoder __unused) override {
+ std::unique_lock lock(mMutex);
+ mFinished = true;
+ mCondition.notify_all();
+ }
+
+ virtual void onTrackError(const MediaTrackTranscoder* transcoder __unused,
+ media_status_t status) override {
+ std::unique_lock lock(mMutex);
+ mFinished = true;
+ mStatus = status;
+ mCondition.notify_all();
+ }
+
+ void waitForTranscodingFinished() {
+ std::unique_lock lock(mMutex);
+ while (!mFinished) {
+ mCondition.wait(lock);
+ }
+ }
+
+ media_status_t mStatus = AMEDIA_OK;
+
+private:
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ bool mFinished = false;
+};
+
+/**
+ * MockSampleReader holds a ringbuffer of the first samples in the provided source track. Samples
+ * are returned to the caller from the ringbuffer in a round-robin fashion with increasing
+ * timestamps. The number of samples returned before EOS matches the number of frames in the source
+ * track.
+ */
+class MockSampleReader : public MediaSampleReader {
+public:
+ static std::shared_ptr<MediaSampleReader> createFromFd(int fd, size_t offset, size_t size) {
+ AMediaExtractor* extractor = AMediaExtractor_new();
+ media_status_t status = AMediaExtractor_setDataSourceFd(extractor, fd, offset, size);
+ if (status != AMEDIA_OK) return nullptr;
+
+ auto sampleReader = std::shared_ptr<MockSampleReader>(new MockSampleReader(extractor));
+ return sampleReader;
+ }
+
+ AMediaFormat* getFileFormat() override { return AMediaExtractor_getFileFormat(mExtractor); }
+
+ size_t getTrackCount() const override { return AMediaExtractor_getTrackCount(mExtractor); }
+
+ AMediaFormat* getTrackFormat(int trackIndex) override {
+ return AMediaExtractor_getTrackFormat(mExtractor, trackIndex);
+ }
+
+ media_status_t selectTrack(int trackIndex) override {
+ if (mSelectedTrack >= 0) return AMEDIA_ERROR_UNSUPPORTED;
+ mSelectedTrack = trackIndex;
+
+ media_status_t status = AMediaExtractor_selectTrack(mExtractor, trackIndex);
+ if (status != AMEDIA_OK) return status;
+
+ // Get the sample count.
+ AMediaFormat* format = getTrackFormat(trackIndex);
+ const bool haveSampleCount =
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_FRAME_COUNT, &mSampleCount);
+ AMediaFormat_delete(format);
+
+ if (!haveSampleCount) {
+ LOG(ERROR) << "No sample count in track format.";
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+
+ // Buffer samples.
+ const int32_t targetBufferCount = 60;
+ std::unique_ptr<uint8_t[]> buffer;
+ MediaSampleInfo info;
+ while (true) {
+ info.presentationTimeUs = AMediaExtractor_getSampleTime(mExtractor);
+ info.flags = AMediaExtractor_getSampleFlags(mExtractor);
+ info.size = AMediaExtractor_getSampleSize(mExtractor);
+
+ // Finish buffering after either reading all the samples in the track or after
+ // completing the GOP satisfying the target count.
+ if (mSamples.size() == mSampleCount ||
+ (mSamples.size() >= targetBufferCount && info.flags & SAMPLE_FLAG_SYNC_SAMPLE)) {
+ break;
+ }
+
+ buffer.reset(new uint8_t[info.size]);
+
+ ssize_t bytesRead = AMediaExtractor_readSampleData(mExtractor, buffer.get(), info.size);
+ if (bytesRead != info.size) {
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+
+ mSamples.emplace_back(std::move(buffer), info);
+
+ AMediaExtractor_advance(mExtractor);
+ }
+
+ mFirstPtsUs = mSamples[0].second.presentationTimeUs;
+ mPtsDiff = mSamples[1].second.presentationTimeUs - mSamples[0].second.presentationTimeUs;
+
+ return AMEDIA_OK;
+ }
+
+ media_status_t unselectTrack(int trackIndex __unused) override {
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+
+ media_status_t setEnforceSequentialAccess(bool enforce __unused) override { return AMEDIA_OK; }
+
+ media_status_t getEstimatedBitrateForTrack(int trackIndex __unused,
+ int32_t* bitrate __unused) override {
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+
+ media_status_t getSampleInfoForTrack(int trackIndex, MediaSampleInfo* info) override {
+ if (trackIndex != mSelectedTrack) return AMEDIA_ERROR_INVALID_PARAMETER;
+
+ if (mCurrentSampleIndex >= mSampleCount) {
+ info->presentationTimeUs = 0;
+ info->size = 0;
+ info->flags = SAMPLE_FLAG_END_OF_STREAM;
+ return AMEDIA_ERROR_END_OF_STREAM;
+ }
+
+ *info = mSamples[mCurrentSampleIndex % mSamples.size()].second;
+ info->presentationTimeUs = mFirstPtsUs + mCurrentSampleIndex * mPtsDiff;
+ return AMEDIA_OK;
+ }
+
+ media_status_t readSampleDataForTrack(int trackIndex, uint8_t* buffer,
+ size_t bufferSize) override {
+ if (trackIndex != mSelectedTrack) return AMEDIA_ERROR_INVALID_PARAMETER;
+
+ if (mCurrentSampleIndex >= mSampleCount) return AMEDIA_ERROR_END_OF_STREAM;
+
+ auto& p = mSamples[mCurrentSampleIndex % mSamples.size()];
+
+ if (bufferSize < p.second.size) return AMEDIA_ERROR_INVALID_PARAMETER;
+ memcpy(buffer, p.first.get(), p.second.size);
+
+ advanceTrack(trackIndex);
+ return AMEDIA_OK;
+ }
+
+ void advanceTrack(int trackIndex) {
+ if (trackIndex != mSelectedTrack) return;
+ ++mCurrentSampleIndex;
+ }
+
+ virtual ~MockSampleReader() override { AMediaExtractor_delete(mExtractor); }
+
+private:
+ MockSampleReader(AMediaExtractor* extractor) : mExtractor(extractor) {}
+ AMediaExtractor* mExtractor = nullptr;
+ int32_t mSampleCount = 0;
+ std::vector<std::pair<std::unique_ptr<uint8_t[]>, MediaSampleInfo>> mSamples;
+ int mSelectedTrack = -1;
+ int32_t mCurrentSampleIndex = 0;
+ int64_t mFirstPtsUs = 0;
+ int64_t mPtsDiff = 0;
+};
+
+static std::shared_ptr<AMediaFormat> GetDefaultTrackFormat(MediaType mediaType,
+ AMediaFormat* sourceFormat) {
+ // Default video config.
+ static constexpr int32_t kVideoBitRate = 20 * 1000 * 1000; // 20 mbps
+ static constexpr float kVideoFrameRate = 30.0f; // 30 fps
+
+ AMediaFormat* format = nullptr;
+
+ if (mediaType == kVideo) {
+ format = AMediaFormat_new();
+ AMediaFormat_copy(format, sourceFormat);
+ AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_AVC);
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, kVideoBitRate);
+ AMediaFormat_setFloat(format, AMEDIAFORMAT_KEY_FRAME_RATE, kVideoFrameRate);
+ }
+ // nothing for audio.
+
+ return std::shared_ptr<AMediaFormat>(format, &AMediaFormat_delete);
+}
+
+/** Gets a MediaSampleReader for the source file */
+static std::shared_ptr<MediaSampleReader> GetSampleReader(const std::string& srcFileName,
+ bool mock) {
+ // Asset directory
+ static const std::string kAssetDirectory = "/data/local/tmp/TranscodingBenchmark/";
+
+ int srcFd = 0;
+ std::string srcPath = kAssetDirectory + srcFileName;
+
+ if ((srcFd = open(srcPath.c_str(), O_RDONLY)) < 0) {
+ return nullptr;
+ }
+
+ const size_t fileSize = lseek(srcFd, 0, SEEK_END);
+ lseek(srcFd, 0, SEEK_SET);
+
+ std::shared_ptr<MediaSampleReader> sampleReader;
+
+ if (mock) {
+ sampleReader = MockSampleReader::createFromFd(srcFd, 0 /* offset */, fileSize);
+ } else {
+ sampleReader = MediaSampleReaderNDK::createFromFd(srcFd, 0 /* offset */, fileSize);
+ }
+
+ if (srcFd > 0) close(srcFd);
+ return sampleReader;
+}
+
+/**
+ * Configures a MediaTrackTranscoder with an empty sample consumer so that the samples are returned
+ * to the transcoder immediately.
+ */
+static void ConfigureEmptySampleConsumer(const std::shared_ptr<MediaTrackTranscoder>& transcoder,
+ uint32_t& sampleCount) {
+ transcoder->setSampleConsumer([&sampleCount](const std::shared_ptr<MediaSample>& sample) {
+ if (!(sample->info.flags & SAMPLE_FLAG_CODEC_CONFIG) && sample->info.size > 0) {
+ ++sampleCount;
+ }
+ });
+}
+
+/**
+ * Callback to edit track format for transcoding.
+ * @param dstFormat The default track format for the track type.
+ */
+using TrackFormatEditCallback = std::function<void(AMediaFormat* dstFormat)>;
+
+/**
+ * Configures a MediaTrackTranscoder with the provided MediaSampleReader, reading from the first
+ * track that matches the specified media type.
+ */
+static bool ConfigureSampleReader(const std::shared_ptr<MediaTrackTranscoder>& transcoder,
+ const std::shared_ptr<MediaSampleReader>& sampleReader,
+ MediaType mediaType,
+ const TrackFormatEditCallback& formatEditor) {
+ int srcTrackIndex = -1;
+ std::shared_ptr<AMediaFormat> srcTrackFormat = nullptr;
+
+ for (int trackIndex = 0; trackIndex < sampleReader->getTrackCount(); ++trackIndex) {
+ AMediaFormat* trackFormat = sampleReader->getTrackFormat(trackIndex);
+
+ const char* mime = nullptr;
+ AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+
+ if ((mediaType == kVideo && strncmp(mime, "video/", 6) == 0) ||
+ (mediaType == kAudio && strncmp(mime, "audio/", 6) == 0)) {
+ srcTrackIndex = trackIndex;
+ srcTrackFormat = std::shared_ptr<AMediaFormat>(trackFormat, &AMediaFormat_delete);
+ break;
+ }
+ AMediaFormat_delete(trackFormat);
+ }
+
+ if (srcTrackIndex == -1) {
+ LOG(ERROR) << "No matching source track found";
+ return false;
+ }
+
+ media_status_t status = sampleReader->selectTrack(srcTrackIndex);
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "Unable to select track";
+ return false;
+ }
+
+ auto destinationFormat = GetDefaultTrackFormat(mediaType, srcTrackFormat.get());
+ if (formatEditor != nullptr) {
+ formatEditor(destinationFormat.get());
+ }
+ status = transcoder->configure(sampleReader, srcTrackIndex, destinationFormat);
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "transcoder configure returned " << status;
+ return false;
+ }
+
+ return true;
+}
+
+static void BenchmarkTranscoder(benchmark::State& state, const std::string& srcFileName,
+ bool mockReader, MediaType mediaType,
+ const TrackFormatEditCallback& formatEditor = nullptr) {
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+ pthread_once(&once, ABinderProcess_startThreadPool);
+
+ for (auto _ : state) {
+ std::shared_ptr<TrackTranscoderCallbacks> callbacks =
+ std::make_shared<TrackTranscoderCallbacks>();
+ std::shared_ptr<MediaTrackTranscoder> transcoder;
+
+ if (mediaType == kVideo) {
+ transcoder = VideoTrackTranscoder::create(callbacks);
+ } else {
+ transcoder = std::make_shared<PassthroughTrackTranscoder>(callbacks);
+ }
+
+ std::shared_ptr<MediaSampleReader> sampleReader = GetSampleReader(srcFileName, mockReader);
+ if (sampleReader == nullptr) {
+ state.SkipWithError("Unable to create sample reader");
+ return;
+ }
+
+ if (!ConfigureSampleReader(transcoder, sampleReader, mediaType, formatEditor)) {
+ state.SkipWithError("Unable to configure the transcoder");
+ return;
+ }
+
+ uint32_t sampleCount = 0;
+ ConfigureEmptySampleConsumer(transcoder, sampleCount);
+
+ if (!transcoder->start()) {
+ state.SkipWithError("Unable to start the transcoder");
+ return;
+ }
+
+ callbacks->waitForTranscodingFinished();
+ transcoder->stop();
+
+ if (callbacks->mStatus != AMEDIA_OK) {
+ state.SkipWithError("Transcoder failed with error");
+ return;
+ }
+
+ LOG(DEBUG) << "Number of samples received: " << sampleCount;
+ state.counters["FrameRate"] = benchmark::Counter(sampleCount, benchmark::Counter::kIsRate);
+ }
+}
+
+static void BenchmarkTranscoderWithOperatingRate(benchmark::State& state,
+ const std::string& srcFile, bool mockReader,
+ MediaType mediaType) {
+ TrackFormatEditCallback editor;
+ const int32_t operatingRate = state.range(0);
+ const int32_t priority = state.range(1);
+
+ if (operatingRate >= 0 && priority >= 0) {
+ editor = [operatingRate, priority](AMediaFormat* format) {
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_OPERATING_RATE, operatingRate);
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_PRIORITY, priority);
+ };
+ }
+ BenchmarkTranscoder(state, srcFile, mockReader, mediaType, editor);
+}
+
+//-------------------------------- AVC to AVC Benchmarks -------------------------------------------
+
+static void BM_VideoTranscode_AVC2AVC(benchmark::State& state) {
+ const char* srcFile = "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4";
+ BenchmarkTranscoderWithOperatingRate(state, srcFile, false /* mockReader */, kVideo);
+}
+
+static void BM_VideoTranscode_AVC2AVC_NoExtractor(benchmark::State& state) {
+ const char* srcFile = "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4";
+ BenchmarkTranscoderWithOperatingRate(state, srcFile, true /* mockReader */, kVideo);
+}
+
+//-------------------------------- HEVC to AVC Benchmarks ------------------------------------------
+
+static void BM_VideoTranscode_HEVC2AVC(benchmark::State& state) {
+ const char* srcFile = "video_1920x1080_3863frame_hevc_4Mbps_30fps_aac.mp4";
+ BenchmarkTranscoderWithOperatingRate(state, srcFile, false /* mockReader */, kVideo);
+}
+
+static void BM_VideoTranscode_HEVC2AVC_NoExtractor(benchmark::State& state) {
+ const char* srcFile = "video_1920x1080_3863frame_hevc_4Mbps_30fps_aac.mp4";
+ BenchmarkTranscoderWithOperatingRate(state, srcFile, true /* mockReader */, kVideo);
+}
+
+//-------------------------------- Benchmark Registration ------------------------------------------
+
+// Benchmark registration wrapper for transcoding.
+#define TRANSCODER_BENCHMARK(func) \
+ BENCHMARK(func)->UseRealTime()->MeasureProcessCPUTime()->Unit(benchmark::kMillisecond)
+
+// Benchmark registration for testing different operating rate and priority combinations.
+#define TRANSCODER_OPERATING_RATE_BENCHMARK(func) \
+ TRANSCODER_BENCHMARK(func) \
+ ->Args({-1, -1}) /* <-- Use default */ \
+ ->Args({240, 0}) \
+ ->Args({INT32_MAX, 0}) \
+ ->Args({240, 1}) \
+ ->Args({INT32_MAX, 1})
+
+TRANSCODER_OPERATING_RATE_BENCHMARK(BM_VideoTranscode_AVC2AVC);
+TRANSCODER_OPERATING_RATE_BENCHMARK(BM_VideoTranscode_AVC2AVC_NoExtractor);
+
+TRANSCODER_OPERATING_RATE_BENCHMARK(BM_VideoTranscode_HEVC2AVC);
+TRANSCODER_OPERATING_RATE_BENCHMARK(BM_VideoTranscode_HEVC2AVC_NoExtractor);
+
+BENCHMARK_MAIN();
diff --git a/media/libmediatranscoding/transcoder/benchmark/MediaTranscoderBenchmark.cpp b/media/libmediatranscoding/transcoder/benchmark/MediaTranscoderBenchmark.cpp
new file mode 100644
index 0000000..8f8ad4e
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/benchmark/MediaTranscoderBenchmark.cpp
@@ -0,0 +1,648 @@
+/*
+ * 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.
+ */
+
+/**
+ * Native media transcoder library benchmark tests.
+ *
+ * How to run the benchmark:
+ *
+ * 1. Download the media assets from http://go/transcodingbenchmark and push the directory
+ * ("TranscodingBenchmark") to /data/local/tmp.
+ *
+ * 2. Compile the benchmark and sync to device:
+ * $ mm -j72 && adb sync
+ *
+ * 3. Run:
+ * $ adb shell /data/nativetest64/MediaTranscoderBenchmark/MediaTranscoderBenchmark
+ */
+
+#include <benchmark/benchmark.h>
+#include <binder/ProcessState.h>
+#include <fcntl.h>
+#include <media/MediaTranscoder.h>
+#include <media/NdkCommon.h>
+
+#include <iostream>
+
+using namespace android;
+
+const std::string PARAM_VIDEO_FRAME_RATE = "VideoFrameRate";
+
+class TranscoderCallbacks : public MediaTranscoder::CallbackInterface {
+public:
+ virtual void onFinished(const MediaTranscoder* transcoder __unused) override {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mFinished = true;
+ mCondition.notify_all();
+ }
+
+ virtual void onError(const MediaTranscoder* transcoder __unused,
+ media_status_t error) override {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mFinished = true;
+ mStatus = error;
+ mCondition.notify_all();
+ }
+
+ virtual void onProgressUpdate(const MediaTranscoder* transcoder __unused,
+ int32_t progress __unused) override {}
+
+ virtual void onHeartBeat(const MediaTranscoder* transcoder __unused) override {}
+
+ virtual void onCodecResourceLost(const MediaTranscoder* transcoder __unused,
+ const std::shared_ptr<ndk::ScopedAParcel>& pausedState
+ __unused) override {}
+
+ bool waitForTranscodingFinished() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ while (!mFinished) {
+ if (mCondition.wait_for(lock, std::chrono::minutes(5)) == std::cv_status::timeout) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ media_status_t mStatus = AMEDIA_OK;
+
+private:
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ bool mFinished = false;
+};
+
+static AMediaFormat* CreateDefaultVideoFormat() {
+ // Default bitrate
+ static constexpr int32_t kVideoBitRate = 20 * 1000 * 1000; // 20Mbs
+
+ AMediaFormat* videoFormat = AMediaFormat_new();
+ AMediaFormat_setInt32(videoFormat, AMEDIAFORMAT_KEY_BIT_RATE, kVideoBitRate);
+ AMediaFormat_setString(videoFormat, AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_AVC);
+ return videoFormat;
+}
+
+/**
+ * Callback to configure tracks for transcoding.
+ * @param mime The source track mime type.
+ * @param dstFormat The destination format if the track should be transcoded or nullptr if the track
+ * should be passed through.
+ * @return True if the track should be included in the output file.
+ */
+using TrackSelectionCallback = std::function<bool(const char* mime, AMediaFormat** dstFormat)>;
+
+static void TranscodeMediaFile(benchmark::State& state, const std::string& srcFileName,
+ const std::string& dstFileName,
+ TrackSelectionCallback trackSelectionCallback) {
+ // Write-only, create file if non-existent.
+ static constexpr int kDstOpenFlags = O_WRONLY | O_CREAT;
+ // User R+W permission.
+ static constexpr int kDstFileMode = S_IRUSR | S_IWUSR;
+ // Asset directory
+ static const std::string kAssetDirectory = "/data/local/tmp/TranscodingBenchmark/";
+
+ // Transcoding configuration params to be logged
+ int64_t trackDurationUs = 0;
+ int32_t width = 0;
+ int32_t height = 0;
+ std::string sourceMime = "NA";
+ std::string targetMime = "NA";
+ bool includeAudio = false;
+ bool transcodeVideo = false;
+ int32_t targetBitrate = 0;
+
+ int srcFd = 0;
+ int dstFd = 0;
+
+ std::string srcPath = kAssetDirectory + srcFileName;
+ std::string dstPath = kAssetDirectory + dstFileName;
+
+ media_status_t status = AMEDIA_OK;
+
+ if ((srcFd = open(srcPath.c_str(), O_RDONLY)) < 0) {
+ state.SkipWithError("Unable to open source file");
+ goto exit;
+ }
+ if ((dstFd = open(dstPath.c_str(), kDstOpenFlags, kDstFileMode)) < 0) {
+ state.SkipWithError("Unable to open destination file");
+ goto exit;
+ }
+
+ for (auto _ : state) {
+ auto callbacks = std::make_shared<TranscoderCallbacks>();
+ auto transcoder = MediaTranscoder::create(callbacks);
+
+ status = transcoder->configureSource(srcFd);
+ if (status != AMEDIA_OK) {
+ state.SkipWithError("Unable to configure transcoder source");
+ goto exit;
+ }
+
+ status = transcoder->configureDestination(dstFd);
+ if (status != AMEDIA_OK) {
+ state.SkipWithError("Unable to configure transcoder destination");
+ goto exit;
+ }
+
+ std::vector<std::shared_ptr<AMediaFormat>> trackFormats = transcoder->getTrackFormats();
+ for (int i = 0; i < trackFormats.size(); ++i) {
+ AMediaFormat* srcFormat = trackFormats[i].get();
+ AMediaFormat* dstFormat = nullptr;
+
+ const char* mime = nullptr;
+ if (!AMediaFormat_getString(srcFormat, AMEDIAFORMAT_KEY_MIME, &mime)) {
+ state.SkipWithError("Source track format does not have MIME type");
+ goto exit;
+ }
+
+ if (strncmp(mime, "video/", 6) == 0) {
+ int32_t frameCount;
+ if (AMediaFormat_getInt32(srcFormat, AMEDIAFORMAT_KEY_FRAME_COUNT, &frameCount)) {
+ state.counters[PARAM_VIDEO_FRAME_RATE] = benchmark::Counter(
+ frameCount, benchmark::Counter::kIsIterationInvariantRate);
+ }
+ if (!AMediaFormat_getInt32(srcFormat, AMEDIAFORMAT_KEY_WIDTH, &width)) {
+ state.SkipWithError("Video source track format does not have width");
+ goto exit;
+ }
+ if (!AMediaFormat_getInt32(srcFormat, AMEDIAFORMAT_KEY_HEIGHT, &height)) {
+ state.SkipWithError("Video source track format does not have height");
+ goto exit;
+ }
+ AMediaFormat_getInt64(srcFormat, AMEDIAFORMAT_KEY_DURATION, &trackDurationUs);
+ sourceMime = mime;
+ }
+
+ if (trackSelectionCallback(mime, &dstFormat)) {
+ status = transcoder->configureTrackFormat(i, dstFormat);
+ if (strncmp(mime, "video/", 6) == 0 && dstFormat != nullptr) {
+ const char* mime = nullptr;
+ if (AMediaFormat_getString(dstFormat, AMEDIAFORMAT_KEY_MIME, &mime)) {
+ targetMime = mime;
+ }
+ AMediaFormat_getInt32(dstFormat, AMEDIAFORMAT_KEY_BIT_RATE, &targetBitrate);
+ transcodeVideo = true;
+ } else if (strncmp(mime, "audio/", 6) == 0) {
+ includeAudio = true;
+ }
+ }
+
+ if (dstFormat != nullptr) {
+ AMediaFormat_delete(dstFormat);
+ }
+ if (status != AMEDIA_OK) {
+ state.SkipWithError("Unable to configure track");
+ goto exit;
+ }
+ }
+
+ status = transcoder->start();
+ if (status != AMEDIA_OK) {
+ state.SkipWithError("Unable to start transcoder");
+ goto exit;
+ }
+
+ if (!callbacks->waitForTranscodingFinished()) {
+ transcoder->cancel();
+ state.SkipWithError("Transcoder timed out");
+ goto exit;
+ }
+ if (callbacks->mStatus != AMEDIA_OK) {
+ state.SkipWithError("Transcoder error when running");
+ goto exit;
+ }
+ }
+
+ // Set transcoding configuration params in benchmark label
+ state.SetLabel(srcFileName + "," +
+ std::to_string(width) + "x" + std::to_string(height) + "," +
+ sourceMime + "," +
+ std::to_string(trackDurationUs/1000) + "," +
+ (includeAudio ? "Yes" : "No") + "," +
+ (transcodeVideo ? "Yes" : "No") + "," +
+ targetMime + "," +
+ std::to_string(targetBitrate)
+ );
+
+exit:
+ if (srcFd > 0) close(srcFd);
+ if (dstFd > 0) close(dstFd);
+}
+
+/**
+ * Callback to edit track format for transcoding.
+ * @param dstFormat The default track format for the track type.
+ */
+using TrackFormatEditCallback = std::function<void(AMediaFormat* dstFormat)>;
+
+static void TranscodeMediaFile(benchmark::State& state, const std::string& srcFileName,
+ const std::string& dstFileName, bool includeAudio,
+ bool transcodeVideo,
+ const TrackFormatEditCallback& videoFormatEditor = nullptr) {
+ TranscodeMediaFile(state, srcFileName, dstFileName,
+ [=](const char* mime, AMediaFormat** dstFormatOut) -> bool {
+ *dstFormatOut = nullptr;
+ if (strncmp(mime, "video/", 6) == 0 && transcodeVideo) {
+ *dstFormatOut = CreateDefaultVideoFormat();
+ if (videoFormatEditor != nullptr) {
+ videoFormatEditor(*dstFormatOut);
+ }
+ } else if (strncmp(mime, "audio/", 6) == 0 && !includeAudio) {
+ return false;
+ }
+ return true;
+ });
+}
+
+static void SetMaxOperatingRate(AMediaFormat* format) {
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_OPERATING_RATE, INT32_MAX);
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_PRIORITY, 1);
+}
+
+//-------------------------------- AVC to AVC Benchmarks -------------------------------------------
+
+static void BM_TranscodeAvc2AvcAudioVideo2AudioVideo(benchmark::State& state) {
+ TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
+ "video_1920x1080_3648frame_h264_22Mbps_30fps_aac_transcoded_AV.mp4",
+ true /* includeAudio */, true /* transcodeVideo */);
+}
+
+static void BM_TranscodeAvc2AvcVideo2Video(benchmark::State& state) {
+ TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps.mp4",
+ "video_1920x1080_3648frame_h264_22Mbps_30fps_transcoded_V.mp4",
+ false /* includeAudio */, true /* transcodeVideo */);
+}
+
+static void BM_TranscodeAvc2AvcAV2AVMaxOperatingRate(benchmark::State& state) {
+ TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
+ "video_1920x1080_3648frame_h264_22Mbps_30fps_aac_transcoded_AV.mp4",
+ true /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
+}
+
+static void BM_TranscodeAvc2AvcV2VMaxOperatingRate(benchmark::State& state) {
+ TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps.mp4",
+ "video_1920x1080_3648frame_h264_22Mbps_30fps_transcoded_V.mp4",
+ false /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
+}
+
+static void BM_TranscodeAvc2AvcAV2AV720P(benchmark::State& state) {
+ TranscodeMediaFile(state, "video_1280x720_3648frame_h264_16Mbps_30fps_aac.mp4",
+ "video_1280x720_3648frame_h264_16Mbps_30fps_aac_transcoded_AV.mp4",
+ true /* includeAudio */, true /* transcodeVideo */);
+}
+
+static void BM_TranscodeAvc2AvcAV2AV720PMaxOperatingRate(benchmark::State& state) {
+ TranscodeMediaFile(state, "video_1280x720_3648frame_h264_16Mbps_30fps_aac.mp4",
+ "video_1280x720_3648frame_h264_16Mbps_30fps_aac_transcoded_AV.mp4",
+ true /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
+}
+//-------------------------------- HEVC to AVC Benchmarks ------------------------------------------
+
+static void BM_TranscodeHevc2AvcAudioVideo2AudioVideo(benchmark::State& state) {
+ TranscodeMediaFile(state, "video_1920x1080_3863frame_hevc_4Mbps_30fps_aac.mp4",
+ "video_1920x1080_3863frame_hevc_4Mbps_30fps_aac_transcoded_AV.mp4",
+ true /* includeAudio */, true /* transcodeVideo */);
+}
+
+static void BM_TranscodeHevc2AvcVideo2Video(benchmark::State& state) {
+ TranscodeMediaFile(state, "video_1920x1080_3863frame_hevc_4Mbps_30fps.mp4",
+ "video_1920x1080_3863frame_hevc_4Mbps_30fps_transcoded_V.mp4",
+ false /* includeAudio */, true /* transcodeVideo */);
+}
+
+static void BM_TranscodeHevc2AvcAV2AVMaxOperatingRate(benchmark::State& state) {
+ TranscodeMediaFile(state, "video_1920x1080_3863frame_hevc_4Mbps_30fps_aac.mp4",
+ "video_1920x1080_3863frame_hevc_4Mbps_30fps_aac_transcoded_AV.mp4",
+ true /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
+}
+
+static void BM_TranscodeHevc2AvcV2VMaxOperatingRate(benchmark::State& state) {
+ TranscodeMediaFile(state, "video_1920x1080_3863frame_hevc_4Mbps_30fps.mp4",
+ "video_1920x1080_3863frame_hevc_4Mbps_30fps_transcoded_V.mp4",
+ false /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
+}
+
+static void BM_TranscodeHevc2AvcAV2AV720P(benchmark::State& state) {
+ TranscodeMediaFile(state, "video_1280x720_3863frame_hevc_16Mbps_30fps_aac.mp4",
+ "video_1280x720_3863frame_hevc_16Mbps_30fps_aac_transcoded_AV.mp4",
+ true /* includeAudio */, true /* transcodeVideo */);
+}
+
+static void BM_TranscodeHevc2AvcAV2AV720PMaxOperatingRate(benchmark::State& state) {
+ TranscodeMediaFile(state, "video_1280x720_3863frame_hevc_16Mbps_30fps_aac.mp4",
+ "video_1280x720_3863frame_hevc_16Mbps_30fps_aac_transcoded_AV.mp4",
+ true /* includeAudio */, true /* transcodeVideo */, SetMaxOperatingRate);
+}
+
+//-------------------------------- Passthrough Benchmarks ------------------------------------------
+
+static void BM_TranscodeAudioVideoPassthrough(benchmark::State& state) {
+ TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
+ "video_1920x1080_3648frame_h264_22Mbps_30fps_aac_passthrough_AV.mp4",
+ true /* includeAudio */, false /* transcodeVideo */);
+}
+static void BM_TranscodeVideoPassthrough(benchmark::State& state) {
+ TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps.mp4",
+ "video_1920x1080_3648frame_h264_22Mbps_30fps_passthrough_AV.mp4",
+ false /* includeAudio */, false /* transcodeVideo */);
+}
+
+//---------------------------- Codecs, Resolutions, Bitrate ---------------------------------------
+static void SetMimeBitrate(AMediaFormat* format, std::string mime, int32_t bitrate) {
+ AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, mime.c_str());
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, bitrate);
+}
+
+static void BM_1920x1080_Avc22Mbps2Avc12Mbps(benchmark::State& state) {
+ TranscodeMediaFile(state, "tx_bm_1920_1080_30fps_h264_22Mbps.mp4",
+ "tx_bm_1920_1080_30fps_h264_22Mbps_transcoded_h264_12Mbps.mp4",
+ false /* includeAudio */, true /* transcodeVideo */,
+ [mime = "video/avc", bitrate = 12000000](AMediaFormat* dstFormat) {
+ SetMimeBitrate(dstFormat, mime, bitrate);
+ });
+}
+
+static void BM_1920x1080_Avc15Mbps2Avc8Mbps(benchmark::State& state) {
+ TranscodeMediaFile(state, "tx_bm_1920_1080_30fps_h264_15Mbps.mp4",
+ "tx_bm_1920_1080_30fps_h264_15Mbps_transcoded_h264_8Mbps.mp4",
+ false /* includeAudio */, true /* transcodeVideo */,
+ [mime = "video/avc", bitrate = 8000000](AMediaFormat* dstFormat) {
+ SetMimeBitrate(dstFormat, mime, bitrate);
+ });
+}
+
+static void BM_1920x1080_Avc15Mbps2AvcPassthrough(benchmark::State& state) {
+ TranscodeMediaFile(state, "tx_bm_1920_1080_30fps_h264_15Mbps.mp4",
+ "tx_bm_1920_1080_30fps_h264_15Mbps_passthrough_V.mp4",
+ false /* includeAudio */, false /* transcodeVideo */);
+}
+
+static void BM_1920x1080_Avc15MbpsAac2Avc8Mbps(benchmark::State& state) {
+ TranscodeMediaFile(state, "tx_bm_1920_1080_30fps_h264_15Mbps_aac.mp4",
+ "tx_bm_1920_1080_30fps_h264_15Mbps_aac_transcoded_h264_8Mbps.mp4",
+ false /* includeAudio */, true /* transcodeVideo */,
+ [mime = "video/avc", bitrate = 8000000](AMediaFormat* dstFormat) {
+ SetMimeBitrate(dstFormat, mime, bitrate);
+ });
+}
+
+static void BM_1920x1080_Avc15MbpsAac2Avc8MbpsAac(benchmark::State& state) {
+ TranscodeMediaFile(state, "tx_bm_1920_1080_30fps_h264_15Mbps_aac.mp4",
+ "tx_bm_1920_1080_30fps_h264_15Mbps_aac_transcoded_h264_8Mbps_aac.mp4",
+ true /* includeAudio */, true /* transcodeVideo */,
+ [mime = "video/avc", bitrate = 8000000](AMediaFormat* dstFormat) {
+ SetMimeBitrate(dstFormat, mime, bitrate);
+ });
+}
+
+static void BM_1920x1080_Avc15MbpsAac2AvcPassthrough(benchmark::State& state) {
+ TranscodeMediaFile(state, "tx_bm_1920_1080_30fps_h264_15Mbps_aac.mp4",
+ "tx_bm_1920_1080_30fps_h264_15Mbps_aac_passthrough_V.mp4",
+ false /* includeAudio */, false /* transcodeVideo */);
+}
+
+static void BM_1920x1080_Avc15MbpsAac2AvcAacPassthrough(benchmark::State& state) {
+ TranscodeMediaFile(state, "tx_bm_1920_1080_30fps_h264_15Mbps_aac.mp4",
+ "tx_bm_1920_1080_30fps_h264_15Mbps_aac_passthrough_AV.mp4",
+ true /* includeAudio */, false /* transcodeVideo */);
+}
+
+static void BM_1920x1080_Hevc17Mbps2Hevc8Mbps(benchmark::State& state) {
+ TranscodeMediaFile(state, "tx_bm_1920_1080_30fps_hevc_17Mbps.mp4",
+ "tx_bm_1920_1080_30fps_hevc_17Mbps_transcoded_hevc_8Mbps.mp4",
+ false /* includeAudio */, true /* transcodeVideo */,
+ [mime = "video/hevc", bitrate = 8000000](AMediaFormat* dstFormat) {
+ SetMimeBitrate(dstFormat, mime, bitrate);
+ });
+}
+
+static void BM_1920x1080_Hevc17Mbps2Avc12Mbps(benchmark::State& state) {
+ TranscodeMediaFile(state, "tx_bm_1920_1080_30fps_hevc_17Mbps.mp4",
+ "tx_bm_1920_1080_30fps_hevc_17Mbps_transcoded_h264_12Mbps.mp4",
+ false /* includeAudio */, true /* transcodeVideo */,
+ [mime = "video/avc", bitrate = 12000000](AMediaFormat* dstFormat) {
+ SetMimeBitrate(dstFormat, mime, bitrate);
+ });
+}
+
+static void BM_1920x1080_60fps_Hevc28Mbps2Avc15Mbps(benchmark::State& state) {
+ TranscodeMediaFile(state, "tx_bm_1920_1080_60fps_hevc_28Mbps.mp4",
+ "tx_bm_1920_1080_60fps_hevc_28Mbps_transcoded_h264_15Mbps.mp4",
+ false /* includeAudio */, true /* transcodeVideo */,
+ [mime = "video/avc", bitrate = 15000000](AMediaFormat* dstFormat) {
+ SetMimeBitrate(dstFormat, mime, bitrate);
+ });
+}
+
+static void BM_1280x720_Avc10Mbps2Avc4Mbps(benchmark::State& state) {
+ TranscodeMediaFile(state, "tx_bm_1280_720_30fps_h264_10Mbps.mp4",
+ "tx_bm_1280_720_30fps_h264_10Mbps_transcoded_h264_4Mbps.mp4",
+ false /* includeAudio */, true /* transcodeVideo */,
+ [mime = "video/avc", bitrate = 4000000](AMediaFormat* dstFormat) {
+ SetMimeBitrate(dstFormat, mime, bitrate);
+ });
+}
+
+static void BM_1280x720_Avc10Mbps2AvcPassthrough(benchmark::State& state) {
+ TranscodeMediaFile(state, "tx_bm_1280_720_30fps_h264_10Mbps.mp4",
+ "tx_bm_1280_720_30fps_h264_10Mbps_passthrough_V.mp4",
+ false /* includeAudio */, false /* transcodeVideo */);
+}
+
+static void BM_1280x720_Avc10MbpsAac2Avc4Mbps(benchmark::State& state) {
+ TranscodeMediaFile(state, "tx_bm_1280_720_30fps_h264_10Mbps_aac.mp4",
+ "tx_bm_1280_720_30fps_h264_10Mbps_aac_transcoded_h264_4Mbps.mp4",
+ false /* includeAudio */, true /* transcodeVideo */,
+ [mime = "video/avc", bitrate = 4000000](AMediaFormat* dstFormat) {
+ SetMimeBitrate(dstFormat, mime, bitrate);
+ });
+}
+
+static void BM_1280x720_Avc10MbpsAac2Avc4MbpsAac(benchmark::State& state) {
+ TranscodeMediaFile(state, "tx_bm_1280_720_30fps_h264_10Mbps_aac.mp4",
+ "tx_bm_1280_720_30fps_h264_10Mbps_aac_transcoded_h264_4Mbps_aac.mp4",
+ true /* includeAudio */, true /* transcodeVideo */,
+ [mime = "video/avc", bitrate = 4000000](AMediaFormat* dstFormat) {
+ SetMimeBitrate(dstFormat, mime, bitrate);
+ });
+}
+
+static void BM_1280x720_Avc10MbpsAac2AvcPassthrough(benchmark::State& state) {
+ TranscodeMediaFile(state, "tx_bm_1280_720_30fps_h264_10Mbps_aac.mp4",
+ "tx_bm_1280_720_30fps_h264_10Mbps_aac_passthrough_V.mp4",
+ false /* includeAudio */, false /* transcodeVideo */);
+}
+
+static void BM_1280x720_Avc10MbpsAac2AvcAacPassthrough(benchmark::State& state) {
+ TranscodeMediaFile(state, "tx_bm_1280_720_30fps_h264_10Mbps_aac.mp4",
+ "tx_bm_1280_720_30fps_h264_10Mbps_aac_passthrough_AV.mp4",
+ true /* includeAudio */, false /* transcodeVideo */);
+}
+
+static void BM_1280x720_Hevc8Mbps2Avc4Mbps(benchmark::State& state) {
+ TranscodeMediaFile(state, "tx_bm_1280_720_30fps_hevc_8Mbps.mp4",
+ "tx_bm_1280_720_30fps_hevc_8Mbps_transcoded_h264_4Mbps.mp4",
+ false /* includeAudio */, true /* transcodeVideo */,
+ [mime = "video/avc", bitrate = 4000000](AMediaFormat* dstFormat) {
+ SetMimeBitrate(dstFormat, mime, bitrate);
+ });
+}
+
+static void BM_1080x1920_Avc15Mbps2Avc8Mbps(benchmark::State& state) {
+ TranscodeMediaFile(state, "tx_bm_1080_1920_30fps_h264_15Mbps.mp4",
+ "tx_bm_1080_1920_30fps_h264_15Mbps_transcoded_h264_8Mbps.mp4",
+ false /* includeAudio */, true /* transcodeVideo */,
+ [mime = "video/avc", bitrate = 8000000](AMediaFormat* dstFormat) {
+ SetMimeBitrate(dstFormat, mime, bitrate);
+ });
+}
+
+static void BM_720x1280_Avc10Mbps2Avc4Mbps(benchmark::State& state) {
+ TranscodeMediaFile(state, "tx_bm_720_1280_30fps_h264_10Mbps.mp4",
+ "tx_bm_720_1280_30fps_h264_10Mbps_transcoded_h264_4Mbps.mp4",
+ false /* includeAudio */, true /* transcodeVideo */,
+ [mime = "video/avc", bitrate = 4000000](AMediaFormat* dstFormat) {
+ SetMimeBitrate(dstFormat, mime, bitrate);
+ });
+}
+
+static void BM_3840x2160_Hevc42Mbps2Avc20Mbps(benchmark::State& state) {
+ TranscodeMediaFile(state, "tx_bm_3840_2160_30fps_hevc_42Mbps.mp4",
+ "tx_bm_3840_2160_30fps_hevc_42Mbps_transcoded_h264_4Mbps.mp4",
+ false /* includeAudio */, true /* transcodeVideo */,
+ [mime = "video/avc", bitrate = 20000000](AMediaFormat* dstFormat) {
+ SetMimeBitrate(dstFormat, mime, bitrate);
+ });
+}
+
+//-------------------------------- Benchmark Registration ------------------------------------------
+
+// Benchmark registration wrapper for transcoding.
+#define TRANSCODER_BENCHMARK(func) \
+ BENCHMARK(func)->UseRealTime()->MeasureProcessCPUTime()->Unit(benchmark::kMillisecond)
+
+TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAudioVideo2AudioVideo);
+TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcVideo2Video);
+TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAV2AVMaxOperatingRate);
+TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcV2VMaxOperatingRate);
+TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAV2AV720P);
+TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAV2AV720PMaxOperatingRate);
+
+TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcAudioVideo2AudioVideo);
+TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcVideo2Video);
+TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcAV2AVMaxOperatingRate);
+TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcV2VMaxOperatingRate);
+TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcAV2AV720P);
+TRANSCODER_BENCHMARK(BM_TranscodeHevc2AvcAV2AV720PMaxOperatingRate);
+
+TRANSCODER_BENCHMARK(BM_TranscodeAudioVideoPassthrough);
+TRANSCODER_BENCHMARK(BM_TranscodeVideoPassthrough);
+
+TRANSCODER_BENCHMARK(BM_1920x1080_Avc22Mbps2Avc12Mbps);
+TRANSCODER_BENCHMARK(BM_1920x1080_Avc15Mbps2Avc8Mbps);
+TRANSCODER_BENCHMARK(BM_1920x1080_Avc15Mbps2AvcPassthrough);
+TRANSCODER_BENCHMARK(BM_1920x1080_Avc15MbpsAac2Avc8Mbps);
+TRANSCODER_BENCHMARK(BM_1920x1080_Avc15MbpsAac2Avc8MbpsAac);
+TRANSCODER_BENCHMARK(BM_1920x1080_Avc15MbpsAac2AvcPassthrough);
+TRANSCODER_BENCHMARK(BM_1920x1080_Avc15MbpsAac2AvcAacPassthrough);
+TRANSCODER_BENCHMARK(BM_1920x1080_Hevc17Mbps2Hevc8Mbps);
+TRANSCODER_BENCHMARK(BM_1920x1080_Hevc17Mbps2Avc12Mbps);
+TRANSCODER_BENCHMARK(BM_1920x1080_60fps_Hevc28Mbps2Avc15Mbps);
+
+TRANSCODER_BENCHMARK(BM_1280x720_Avc10Mbps2Avc4Mbps);
+TRANSCODER_BENCHMARK(BM_1280x720_Avc10Mbps2AvcPassthrough);
+TRANSCODER_BENCHMARK(BM_1280x720_Avc10MbpsAac2Avc4Mbps);
+TRANSCODER_BENCHMARK(BM_1280x720_Avc10MbpsAac2Avc4MbpsAac);
+TRANSCODER_BENCHMARK(BM_1280x720_Avc10MbpsAac2AvcPassthrough);
+TRANSCODER_BENCHMARK(BM_1280x720_Avc10MbpsAac2AvcAacPassthrough);
+TRANSCODER_BENCHMARK(BM_1280x720_Hevc8Mbps2Avc4Mbps);
+
+TRANSCODER_BENCHMARK(BM_1080x1920_Avc15Mbps2Avc8Mbps);
+TRANSCODER_BENCHMARK(BM_720x1280_Avc10Mbps2Avc4Mbps);
+
+TRANSCODER_BENCHMARK(BM_3840x2160_Hevc42Mbps2Avc20Mbps);
+
+class CustomCsvReporter : public benchmark::BenchmarkReporter {
+public:
+ CustomCsvReporter() : mPrintedHeader(false) {}
+ virtual bool ReportContext(const Context& context);
+ virtual void ReportRuns(const std::vector<Run>& reports);
+
+private:
+ void PrintRunData(const Run& report);
+
+ bool mPrintedHeader;
+ std::vector<std::string> mHeaders = {
+ "File", "Resolution", "SourceMime", "VideoTrackDuration(ms)",
+ "IncludeAudio", "TranscodeVideo", "TargetMime", "TargetBirate(bps)",
+ "real_time(ms)", "cpu_time(ms)", PARAM_VIDEO_FRAME_RATE
+ };
+};
+
+bool CustomCsvReporter::ReportContext(const Context& context __unused) {
+ return true;
+}
+
+void CustomCsvReporter::ReportRuns(const std::vector<Run>& reports) {
+ std::ostream& Out = GetOutputStream();
+
+ if (!mPrintedHeader) {
+ // print the header
+ for (auto header = mHeaders.begin(); header != mHeaders.end();) {
+ Out << *header++;
+ if (header != mHeaders.end()) Out << ",";
+ }
+ Out << "\n";
+ mPrintedHeader = true;
+ }
+
+ // print results for each run
+ for (const auto& run : reports) {
+ PrintRunData(run);
+ }
+}
+
+void CustomCsvReporter::PrintRunData(const Run& run) {
+ if (run.error_occurred) {
+ return;
+ }
+ std::ostream& Out = GetOutputStream();
+ // Log the transcoding params reported through label
+ Out << run.report_label << ",";
+ Out << run.GetAdjustedRealTime() << ",";
+ Out << run.GetAdjustedCPUTime() << ",";
+ auto frameRate = run.counters.find(PARAM_VIDEO_FRAME_RATE);
+ if (frameRate == run.counters.end()) {
+ Out << "NA"
+ << ",";
+ } else {
+ Out << frameRate->second << ",";
+ }
+ Out << '\n';
+}
+
+int main(int argc, char** argv) {
+ android::ProcessState::self()->startThreadPool();
+ std::unique_ptr<benchmark::BenchmarkReporter> fileReporter;
+ for (int i = 1; i < argc; ++i) {
+ if (std::string(argv[i]).find("--benchmark_out") != std::string::npos) {
+ fileReporter.reset(new CustomCsvReporter);
+ break;
+ }
+ }
+ ::benchmark::Initialize(&argc, argv);
+ if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1;
+ ::benchmark::RunSpecifiedBenchmarks(nullptr, fileReporter.get());
+}
diff --git a/media/libmediatranscoding/transcoder/include/media/MediaSample.h b/media/libmediatranscoding/transcoder/include/media/MediaSample.h
new file mode 100644
index 0000000..8a239a6
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/include/media/MediaSample.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_SAMPLE_H
+#define ANDROID_MEDIA_SAMPLE_H
+
+#include <cstdint>
+#include <functional>
+#include <memory>
+
+namespace android {
+
+/**
+ * Media sample flags.
+ * These flags purposely match the media NDK's buffer and extractor flags with one exception. The
+ * NDK extractor's flag for encrypted samples (AMEDIAEXTRACTOR_SAMPLE_FLAG_ENCRYPTED) is equal to 2,
+ * i.e. the same as SAMPLE_FLAG_CODEC_CONFIG below and NDK's AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG.
+ * Sample producers based on the NDK's extractor is responsible for catching those values.
+ * Note that currently the media transcoder does not support encrypted samples.
+ */
+enum : uint32_t {
+ SAMPLE_FLAG_SYNC_SAMPLE = 1,
+ SAMPLE_FLAG_CODEC_CONFIG = 2,
+ SAMPLE_FLAG_END_OF_STREAM = 4,
+ SAMPLE_FLAG_PARTIAL_FRAME = 8,
+};
+
+/**
+ * MediaSampleInfo is an object that carries information about a compressed media sample without
+ * holding any sample data.
+ */
+struct MediaSampleInfo {
+ /** The sample's presentation timestamp in microseconds. */
+ int64_t presentationTimeUs = 0;
+
+ /** The size of the compressed sample data in bytes. */
+ size_t size = 0;
+
+ /** Sample flags. */
+ uint32_t flags = 0;
+};
+
+/**
+ * MediaSample holds a compressed media sample in memory.
+ */
+struct MediaSample {
+ /**
+ * Callback to notify that a media sample is about to be released, giving the creator a chance
+ * to reclaim the data buffer backing the sample. Once this callback returns, the media sample
+ * instance *will* be released so it cannot be used outside of the callback. To enable the
+ * callback, create the media sample with {@link #createWithReleaseCallback}.
+ * @param sample The sample to be released.
+ */
+ using OnSampleReleasedCallback = std::function<void(MediaSample* sample)>;
+
+ /**
+ * Creates a new media sample instance with a registered release callback. The release callback
+ * will get called right before the media sample is released giving the creator a chance to
+ * reclaim the buffer.
+ * @param buffer Byte buffer containing the sample's compressed data.
+ * @param dataOffset Offset, in bytes, to the sample's compressed data inside the buffer.
+ * @param bufferId Buffer identifier that can be used to identify the buffer on release.
+ * @param releaseCallback The sample release callback.
+ * @return A new media sample instance.
+ */
+ static std::shared_ptr<MediaSample> createWithReleaseCallback(
+ uint8_t* buffer, size_t dataOffset, uint32_t bufferId,
+ OnSampleReleasedCallback releaseCallback) {
+ MediaSample* sample = new MediaSample(buffer, dataOffset, bufferId, releaseCallback);
+ return std::shared_ptr<MediaSample>(
+ sample, std::bind(&MediaSample::releaseSample, std::placeholders::_1));
+ }
+
+ /**
+ * Byte buffer containing the sample's compressed data. The media sample instance does not take
+ * ownership of the buffer and will not automatically release the memory, but the caller can
+ * register a release callback by creating the media sample with
+ * {@link #createWithReleaseCallback}.
+ */
+ const uint8_t* buffer = nullptr;
+
+ /** Offset, in bytes, to the sample's compressed data inside the buffer. */
+ size_t dataOffset = 0;
+
+ /**
+ * Buffer identifier. This identifier is likely only meaningful to the sample data producer and
+ * can be used for reclaiming the buffer once a consumer is done processing it.
+ */
+ uint32_t bufferId = 0xBAADF00D;
+
+ /** Media sample information. */
+ MediaSampleInfo info;
+
+ MediaSample() = default;
+
+private:
+ MediaSample(uint8_t* buffer, size_t dataOffset, uint32_t bufferId,
+ OnSampleReleasedCallback releaseCallback)
+ : buffer(buffer),
+ dataOffset(dataOffset),
+ bufferId(bufferId),
+ mReleaseCallback(releaseCallback){};
+
+ static void releaseSample(MediaSample* sample) {
+ if (sample->mReleaseCallback != nullptr) {
+ sample->mReleaseCallback(sample);
+ }
+ delete sample;
+ }
+
+ // Do not allow copying to prevent dangling pointers in the copied object after the original is
+ // released.
+ MediaSample(const MediaSample&) = delete;
+ MediaSample& operator=(const MediaSample&) = delete;
+
+ const OnSampleReleasedCallback mReleaseCallback = nullptr;
+};
+
+} // namespace android
+#endif // ANDROID_MEDIA_SAMPLE_H
diff --git a/media/libmediatranscoding/transcoder/include/media/MediaSampleQueue.h b/media/libmediatranscoding/transcoder/include/media/MediaSampleQueue.h
new file mode 100644
index 0000000..c6cf1a4
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/include/media/MediaSampleQueue.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_SAMPLE_QUEUE_H
+#define ANDROID_MEDIA_SAMPLE_QUEUE_H
+
+#include <media/MediaSample.h>
+#include <utils/Mutex.h>
+
+#include <memory>
+#include <mutex>
+#include <queue>
+
+namespace android {
+
+/**
+ * MediaSampleQueue asynchronously connects a producer and a consumer of media samples.
+ * Media samples flows through the queue in FIFO order. If the queue is empty the consumer will be
+ * blocked until a new media sample is added or until the producer aborts the queue operation.
+ */
+class MediaSampleQueue {
+public:
+ /**
+ * Enqueues a media sample at the end of the queue and notifies potentially waiting consumers.
+ * If the queue has previously been aborted this method does nothing.
+ * @param sample The media sample to enqueue.
+ * @return True if the queue has been aborted.
+ */
+ bool enqueue(const std::shared_ptr<MediaSample>& sample);
+
+ /**
+ * Removes the next media sample from the queue and returns it. If the queue has previously been
+ * aborted this method returns null. Note that this method will block while the queue is empty.
+ * @param[out] sample The next media sample in the queue.
+ * @return True if the queue has been aborted.
+ */
+ bool dequeue(std::shared_ptr<MediaSample>* sample /* nonnull */);
+
+ /**
+ * Checks if the queue currently holds any media samples.
+ * @return True if the queue is empty or has been aborted. False otherwise.
+ */
+ bool isEmpty();
+
+ /**
+ * Aborts the queue operation. This clears the queue and notifies waiting consumers. After the
+ * has been aborted it is not possible to enqueue more samples, and dequeue will return null.
+ */
+ void abort();
+
+private:
+ std::queue<std::shared_ptr<MediaSample>> mSampleQueue GUARDED_BY(mMutex);
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ bool mAborted GUARDED_BY(mMutex) = false;
+};
+
+} // namespace android
+#endif // ANDROID_MEDIA_SAMPLE_QUEUE_H
diff --git a/media/libmediatranscoding/transcoder/include/media/MediaSampleReader.h b/media/libmediatranscoding/transcoder/include/media/MediaSampleReader.h
new file mode 100644
index 0000000..5c7eeac
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/include/media/MediaSampleReader.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_SAMPLE_READER_H
+#define ANDROID_MEDIA_SAMPLE_READER_H
+
+#include <media/MediaSample.h>
+#include <media/NdkMediaError.h>
+#include <media/NdkMediaFormat.h>
+
+namespace android {
+
+/**
+ * MediaSampleReader is an interface for reading media samples from a container. MediaSampleReader
+ * allows for reading samples from multiple tracks on individual threads independently of each other
+ * while preserving the order of samples. Due to poor non-sequential access performance of the
+ * underlying extractor, MediaSampleReader can optionally enforce sequential sample access by
+ * blocking requests for tracks that the underlying extractor does not currently point to. Waiting
+ * threads are serviced once the reader advances to a sample from the specified track. Due to this
+ * it is important to read samples and advance the reader from all selected tracks to avoid hanging
+ * other tracks. MediaSampleReader implementations are thread safe and sample access should be done
+ * on one thread per selected track.
+ */
+class MediaSampleReader {
+public:
+ /**
+ * Returns the file format of the media container as a AMediaFormat.
+ * The caller is responsible for releasing the format when finished with it using
+ * AMediaFormat_delete().
+ * @return The file media format.
+ */
+ virtual AMediaFormat* getFileFormat() = 0;
+
+ /**
+ * Returns the number of tracks in the media container.
+ * @return The number of tracks.
+ */
+ virtual size_t getTrackCount() const = 0;
+
+ /**
+ * Returns the media format of a specific track as a AMediaFormat.
+ * The caller is responsible for releasing the format when finished with it using
+ * AMediaFormat_delete().
+ * @param trackIndex The track index (zero-based).
+ * @return The track media format.
+ */
+ virtual AMediaFormat* getTrackFormat(int trackIndex) = 0;
+
+ /**
+ * Select a track for sample access. Tracks must be selected in order for sample information and
+ * sample data to be available for that track. Samples for selected tracks must be accessed on
+ * its own thread to avoid blocking other tracks.
+ * @param trackIndex The track to select.
+ * @return AMEDIA_OK on success.
+ */
+ virtual media_status_t selectTrack(int trackIndex) = 0;
+
+ /**
+ * Undo a track selection.
+ * @param trackIndex The track to un-select.
+ * @return AMEDIA_OK on success.
+ */
+ virtual media_status_t unselectTrack(int trackIndex) = 0;
+
+ /**
+ * Toggles sequential access enforcement on or off. When the reader enforces sequential access
+ * calls to read sample information will block unless the underlying extractor points to the
+ * specified track.
+ * @param enforce True to enforce sequential access.
+ * @return AMEDIA_OK on success.
+ */
+ virtual media_status_t setEnforceSequentialAccess(bool enforce) = 0;
+
+ /**
+ * Estimates the bitrate of a source track by sampling sample sizes. The bitrate is returned in
+ * megabits per second (Mbps). This method will fail if the track only contains a single sample
+ * and does not have an associated duration.
+ * @param trackIndex The source track index.
+ * @param bitrate Output param for the bitrate.
+ * @return AMEDIA_OK on success.
+ */
+ virtual media_status_t getEstimatedBitrateForTrack(int trackIndex, int32_t* bitrate);
+
+ /**
+ * Returns the sample information for the current sample in the specified track. Note that this
+ * method will block until the reader advances to a sample belonging to the requested track if
+ * the reader is in sequential access mode.
+ * @param trackIndex The track index (zero-based).
+ * @param info Pointer to a MediaSampleInfo object where the sample information is written.
+ * @return AMEDIA_OK on success, AMEDIA_ERROR_END_OF_STREAM if there are no more samples to read
+ * from the track and AMEDIA_ERROR_INVALID_PARAMETER if trackIndex is out of bounds or the
+ * info pointer is NULL. Other AMEDIA_ERROR_* return values may not be recoverable.
+ */
+ virtual media_status_t getSampleInfoForTrack(int trackIndex, MediaSampleInfo* info) = 0;
+
+ /**
+ * Returns the sample data for the current sample in the specified track into the supplied
+ * buffer. Note that this method will block until the reader advances to a sample belonging to
+ * the requested track if the reader is in sequential access mode. Upon successful return this
+ * method will also advance the specified track to the next sample.
+ * @param trackIndex The track index (zero-based).
+ * @param buffer The buffer to write the sample's data to.
+ * @param bufferSize The size of the supplied buffer.
+ * @return AMEDIA_OK on success, AMEDIA_ERROR_END_OF_STREAM if there are no more samples to read
+ * from the track and AMEDIA_ERROR_INVALID_PARAMETER if trackIndex is out of bounds, if the
+ * buffer pointer is NULL or if bufferSize is too small for the sample. Other AMEDIA_ERROR_*
+ * return values may not be recoverable.
+ */
+ virtual media_status_t readSampleDataForTrack(int trackIndex, uint8_t* buffer,
+ size_t bufferSize) = 0;
+
+ /**
+ * Advance the specified track to the next sample. If the reader is in sequential access mode
+ * and the current sample belongs to the specified track, the reader will also advance to the
+ * next sample and wake up any threads waiting on the new track.
+ * @param trackIndex The track index (zero-based).
+ */
+ virtual void advanceTrack(int trackIndex) = 0;
+
+ /** Destructor. */
+ virtual ~MediaSampleReader() = default;
+
+ /** Constructor. */
+ MediaSampleReader() = default;
+
+private:
+ MediaSampleReader(const MediaSampleReader&) = delete;
+ MediaSampleReader& operator=(const MediaSampleReader&) = delete;
+};
+
+} // namespace android
+#endif // ANDROID_MEDIA_SAMPLE_READER_H
diff --git a/media/libmediatranscoding/transcoder/include/media/MediaSampleReaderNDK.h b/media/libmediatranscoding/transcoder/include/media/MediaSampleReaderNDK.h
new file mode 100644
index 0000000..30cc37f
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/include/media/MediaSampleReaderNDK.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_SAMPLE_READER_NDK_H
+#define ANDROID_MEDIA_SAMPLE_READER_NDK_H
+
+#include <media/MediaSampleReader.h>
+#include <media/NdkMediaExtractor.h>
+
+#include <map>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+namespace android {
+
+/**
+ * MediaSampleReaderNDK is a concrete implementation of the MediaSampleReader interface based on the
+ * media NDK extractor.
+ */
+class MediaSampleReaderNDK : public MediaSampleReader {
+public:
+ /**
+ * Creates a new MediaSampleReaderNDK instance wrapped in a shared pointer.
+ * @param fd Source file descriptor. The caller is responsible for closing the fd and it is safe
+ * to do so when this method returns.
+ * @param offset Source data offset.
+ * @param size Source data size.
+ * @return A shared pointer referencing the new MediaSampleReaderNDK instance on success, or an
+ * empty shared pointer if an error occurred.
+ */
+ static std::shared_ptr<MediaSampleReader> createFromFd(int fd, size_t offset, size_t size);
+
+ AMediaFormat* getFileFormat() override;
+ size_t getTrackCount() const override;
+ AMediaFormat* getTrackFormat(int trackIndex) override;
+ media_status_t selectTrack(int trackIndex) override;
+ media_status_t unselectTrack(int trackIndex) override;
+ media_status_t setEnforceSequentialAccess(bool enforce) override;
+ media_status_t getEstimatedBitrateForTrack(int trackIndex, int32_t* bitrate) override;
+ media_status_t getSampleInfoForTrack(int trackIndex, MediaSampleInfo* info) override;
+ media_status_t readSampleDataForTrack(int trackIndex, uint8_t* buffer,
+ size_t bufferSize) override;
+ void advanceTrack(int trackIndex) override;
+
+ virtual ~MediaSampleReaderNDK() override;
+
+private:
+ /**
+ * SamplePosition describes the position of a single sample in the media file using its
+ * timestamp and index in the file.
+ */
+ struct SamplePosition {
+ uint64_t index = 0;
+ int64_t timeStampUs = 0;
+ bool isSet = false;
+
+ void set(uint64_t sampleIndex, int64_t sampleTimeUs) {
+ index = sampleIndex;
+ timeStampUs = sampleTimeUs;
+ isSet = true;
+ }
+
+ void reset() { isSet = false; }
+ };
+
+ /**
+ * SampleCursor keeps track of the sample position for a specific track. When the track is
+ * advanced, previous is set to current, current to next and next is reset. As the extractor
+ * advances over the combined timeline of tracks, it updates current and next for the track it
+ * points to if they are not already set.
+ */
+ struct SampleCursor {
+ SamplePosition previous;
+ SamplePosition current;
+ SamplePosition next;
+ };
+
+ /**
+ * Creates a new MediaSampleReaderNDK object from an AMediaExtractor. The extractor needs to be
+ * initialized with a valid data source before attempting to create a MediaSampleReaderNDK.
+ * @param extractor The initialized media extractor.
+ */
+ MediaSampleReaderNDK(AMediaExtractor* extractor);
+
+ /** Advances the track to next sample. */
+ void advanceTrack_l(int trackIndex);
+
+ /** Advances the extractor to next sample. */
+ bool advanceExtractor_l();
+
+ /** Moves the extractor backwards to the specified sample. */
+ media_status_t seekExtractorBackwards_l(int64_t targetTimeUs, int targetTrackIndex,
+ uint64_t targetSampleIndex);
+
+ /** Moves the extractor to the specified sample. */
+ media_status_t moveToSample_l(SamplePosition& pos, int trackIndex);
+
+ /** Moves the extractor to the next sample of the specified track. */
+ media_status_t moveToTrack_l(int trackIndex);
+
+ /** In sequential mode, waits for the extractor to reach the next sample for the track. */
+ media_status_t waitForTrack_l(int trackIndex, std::unique_lock<std::mutex>& lockHeld);
+
+ /**
+ * Ensures the extractor is ready for the next sample of the track regardless of access mode.
+ */
+ media_status_t primeExtractorForTrack_l(int trackIndex, std::unique_lock<std::mutex>& lockHeld);
+
+ AMediaExtractor* mExtractor = nullptr;
+ std::mutex mExtractorMutex;
+ const size_t mTrackCount;
+
+ int mExtractorTrackIndex = -1;
+ uint64_t mExtractorSampleIndex = 0;
+
+ bool mEosReached = false;
+ bool mEnforceSequentialAccess = false;
+
+ // Maps selected track indices to condition variables for sequential sample access control.
+ std::map<int, std::condition_variable> mTrackSignals;
+
+ // Samples cursor for each track in the file.
+ std::vector<SampleCursor> mTrackCursors;
+};
+
+} // namespace android
+#endif // ANDROID_MEDIA_SAMPLE_READER_NDK_H
diff --git a/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h b/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h
new file mode 100644
index 0000000..23a234b
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_SAMPLE_WRITER_H
+#define ANDROID_MEDIA_SAMPLE_WRITER_H
+
+#include <media/MediaSample.h>
+#include <media/NdkMediaCodec.h>
+#include <media/NdkMediaError.h>
+#include <media/NdkMediaFormat.h>
+#include <utils/Mutex.h>
+
+#include <condition_variable>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <queue>
+#include <thread>
+#include <unordered_map>
+
+namespace android {
+
+/**
+ * Muxer interface used by MediaSampleWriter.
+ * Methods in this interface are guaranteed to be called sequentially by MediaSampleWriter.
+ */
+class MediaSampleWriterMuxerInterface {
+public:
+ /**
+ * Adds a new track to the muxer.
+ * @param trackFormat Format of the new track.
+ * @return A non-negative track index on success, or a negative number on failure.
+ */
+ virtual ssize_t addTrack(AMediaFormat* trackFormat) = 0;
+
+ /** Starts the muxer. */
+ virtual media_status_t start() = 0;
+ /**
+ * Writes sample data to a previously added track.
+ * @param trackIndex Index of the track the sample data belongs to.
+ * @param data The sample data.
+ * @param info The sample information.
+ * @return The number of bytes written.
+ */
+ virtual media_status_t writeSampleData(size_t trackIndex, const uint8_t* data,
+ const AMediaCodecBufferInfo* info) = 0;
+
+ /** Stops the muxer. */
+ virtual media_status_t stop() = 0;
+ virtual ~MediaSampleWriterMuxerInterface() = default;
+};
+
+/**
+ * MediaSampleWriter is a wrapper around a muxer. The sample writer puts samples on a queue that
+ * is serviced by an internal thread to minimize blocking time for clients. MediaSampleWriter also
+ * provides progress reporting. The default muxer interface implementation is based
+ * directly on AMediaMuxer.
+ */
+class MediaSampleWriter : public std::enable_shared_from_this<MediaSampleWriter> {
+public:
+ /** Function prototype for delivering media samples to the writer. */
+ using MediaSampleConsumerFunction =
+ std::function<void(const std::shared_ptr<MediaSample>& sample)>;
+
+ /** Callback interface. */
+ class CallbackInterface {
+ public:
+ /**
+ * Sample writer finished. The finished callback is only called after the sample writer has
+ * been successfully started.
+ */
+ virtual void onFinished(const MediaSampleWriter* writer, media_status_t status) = 0;
+
+ /** Sample writer was stopped before it was finished. */
+ virtual void onStopped(const MediaSampleWriter* writer) = 0;
+
+ /** Sample writer progress update in percent. */
+ virtual void onProgressUpdate(const MediaSampleWriter* writer, int32_t progress) = 0;
+
+ /** Sample writer heart-beat signal. */
+ virtual void onHeartBeat(const MediaSampleWriter* writer) = 0;
+
+ virtual ~CallbackInterface() = default;
+ };
+
+ static std::shared_ptr<MediaSampleWriter> Create();
+
+ /**
+ * Initializes the sample writer with its default muxer implementation. MediaSampleWriter needs
+ * to be initialized before tracks are added and can only be initialized once.
+ * @param fd An open file descriptor to write to. The caller is responsible for closing this
+ * file descriptor and it is safe to do so once this method returns.
+ * @param callbacks Client callback object that gets called by the sample writer.
+ * @param heartBeatIntervalUs Interval (in microsecond) at which the sample writer should send a
+ * heart-beat to onProgressUpdate() to indicate it's making progress. Value <=0 indicates
+ * that the heartbeat is not required.
+ * @return True if the writer was successfully initialized.
+ */
+ bool init(int fd, const std::weak_ptr<CallbackInterface>& callbacks /* nonnull */,
+ int64_t heartBeatIntervalUs = -1);
+
+ /**
+ * Initializes the sample writer with a custom muxer interface implementation.
+ * @param muxer The custom muxer interface implementation.
+ * @param @param callbacks Client callback object that gets called by the sample writer.
+ * @param heartBeatIntervalUs Interval (in microsecond) at which the sample writer should send a
+ * heart-beat to onProgressUpdate() to indicate it's making progress.
+ * @return True if the writer was successfully initialized.
+ */
+ bool init(const std::shared_ptr<MediaSampleWriterMuxerInterface>& muxer /* nonnull */,
+ const std::weak_ptr<CallbackInterface>& callbacks /* nonnull */,
+ int64_t heartBeatIntervalUs = -1);
+
+ /**
+ * Adds a new track to the sample writer. Tracks must be added after the sample writer has been
+ * initialized and before it is started.
+ * @param trackFormat The format of the track to add.
+ * @return A sample consumer to add samples to if the track was successfully added, or nullptr
+ * if the track could not be added.
+ */
+ MediaSampleConsumerFunction addTrack(
+ const std::shared_ptr<AMediaFormat>& trackFormat /* nonnull */);
+
+ /**
+ * Starts the sample writer. The sample writer will start processing samples and writing them to
+ * its muxer on an internal thread. MediaSampleWriter can only be started once.
+ * @return True if the sample writer was successfully started.
+ */
+ bool start();
+
+ /**
+ * Stops the sample writer. If the sample writer is not yet finished, its operation will be
+ * aborted and the onStopped callback will fire. If the sample writer has already finished and
+ * the onFinished callback has fired the writer has already automatically stopped and there is
+ * no need to call stop manually. Once the sample writer has been stopped it cannot be
+ * restarted. This method is asynchronous and will not wait for the sample writer to stop before
+ * returning.
+ */
+ void stop();
+
+ /** Destructor. */
+ ~MediaSampleWriter();
+
+private:
+ struct TrackRecord {
+ TrackRecord(int64_t durationUs)
+ : mDurationUs(durationUs),
+ mFirstSampleTimeUs(0),
+ mPrevSampleTimeUs(INT64_MIN),
+ mFirstSampleTimeSet(false),
+ mReachedEos(false){};
+
+ TrackRecord() : TrackRecord(0){};
+
+ int64_t mDurationUs;
+ int64_t mFirstSampleTimeUs;
+ int64_t mPrevSampleTimeUs;
+ bool mFirstSampleTimeSet;
+ bool mReachedEos;
+ };
+
+ // Track index and sample.
+ using SampleEntry = std::pair<size_t, std::shared_ptr<MediaSample>>;
+
+ struct SampleComparator {
+ // Return true if lhs should come after rhs in the sample queue.
+ bool operator()(const SampleEntry& lhs, const SampleEntry& rhs) {
+ const bool lhsEos = lhs.second->info.flags & SAMPLE_FLAG_END_OF_STREAM;
+ const bool rhsEos = rhs.second->info.flags & SAMPLE_FLAG_END_OF_STREAM;
+
+ if (lhsEos && !rhsEos) {
+ return true;
+ } else if (!lhsEos && rhsEos) {
+ return false;
+ } else if (lhsEos && rhsEos) {
+ return lhs.first > rhs.first;
+ }
+
+ return lhs.second->info.presentationTimeUs > rhs.second->info.presentationTimeUs;
+ }
+ };
+
+ std::weak_ptr<CallbackInterface> mCallbacks;
+ std::shared_ptr<MediaSampleWriterMuxerInterface> mMuxer;
+ int64_t mHeartBeatIntervalUs;
+
+ std::mutex mMutex; // Protects sample queue and state.
+ std::condition_variable mSampleSignal;
+ std::unordered_map<size_t, TrackRecord> mTracks;
+ std::priority_queue<SampleEntry, std::vector<SampleEntry>, SampleComparator> mSampleQueue
+ GUARDED_BY(mMutex);
+
+ enum : int {
+ UNINITIALIZED,
+ INITIALIZED,
+ STARTED,
+ STOPPED,
+ } mState GUARDED_BY(mMutex);
+
+ MediaSampleWriter() : mState(UNINITIALIZED){};
+ void addSampleToTrack(size_t trackIndex, const std::shared_ptr<MediaSample>& sample);
+ media_status_t writeSamples(bool* wasStopped);
+ media_status_t runWriterLoop(bool* wasStopped);
+};
+
+} // namespace android
+#endif // ANDROID_MEDIA_SAMPLE_WRITER_H
diff --git a/media/libmediatranscoding/transcoder/include/media/MediaTrackTranscoder.h b/media/libmediatranscoding/transcoder/include/media/MediaTrackTranscoder.h
new file mode 100644
index 0000000..724b919
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/include/media/MediaTrackTranscoder.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TRACK_TRANSCODER_H
+#define ANDROID_MEDIA_TRACK_TRANSCODER_H
+
+#include <media/MediaSampleQueue.h>
+#include <media/MediaSampleReader.h>
+#include <media/MediaSampleWriter.h>
+#include <media/NdkMediaError.h>
+#include <media/NdkMediaFormat.h>
+#include <utils/Mutex.h>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <thread>
+
+namespace android {
+
+class MediaTrackTranscoderCallback;
+
+/**
+ * Base class for all track transcoders. MediaTrackTranscoder operates asynchronously on an internal
+ * thread and communicates through a MediaTrackTranscoderCallback instance. Transcoded samples are
+ * enqueued on the MediaTrackTranscoder's output queue. Samples need to be dequeued from the output
+ * queue or the transcoder will run out of buffers and stall. Once the consumer is done with a
+ * transcoded sample it is the consumer's responsibility to as soon as possible release all
+ * references to that sample in order to return the buffer to the transcoder. MediaTrackTranscoder
+ * is an abstract class and instances are created through one of the concrete subclasses.
+ *
+ * The base class MediaTrackTranscoder is responsible for thread and state management and guarantees
+ * that operations {configure, start, stop} are sent to the derived class in correct order.
+ * MediaTrackTranscoder is also responsible for delivering callback notifications once the
+ * transcoder has been successfully started.
+ */
+class MediaTrackTranscoder {
+public:
+ /**
+ * Configures the track transcoder with an input MediaSampleReader and a destination format.
+ * A track transcoder have to be configured before it is started.
+ * @param mediaSampleReader The MediaSampleReader to read input samples from.
+ * @param trackIndex The index of the track to transcode in mediaSampleReader.
+ * @param destinationFormat The destination format.
+ * @return AMEDIA_OK if the track transcoder was successfully configured.
+ */
+ media_status_t configure(const std::shared_ptr<MediaSampleReader>& mediaSampleReader,
+ int trackIndex,
+ const std::shared_ptr<AMediaFormat>& destinationFormat);
+
+ /**
+ * Starts the track transcoder. After the track transcoder is successfully started it will run
+ * until a callback signals that transcoding has ended. Start should only be called once.
+ * @return True if the track transcoder started, or false if it had already been started.
+ */
+ bool start();
+
+ /**
+ * Stops the track transcoder. Once the transcoding has been stopped it cannot be restarted
+ * again. It is safe to call stop multiple times. Stop is an asynchronous operation. Once the
+ * track transcoder has stopped the onTrackStopped callback will get called, unless the
+ * transcoding finished or encountered an error before it could be stopped in which case the
+ * callbacks corresponding to those events will be called instead.
+ * @param stopOnSyncSample Request the transcoder to stop after emitting a sync sample.
+ */
+ void stop(bool stopOnSyncSample = false);
+
+ /**
+ * Set the sample consumer function. The MediaTrackTranscoder will deliver transcoded samples to
+ * this function. If the MediaTrackTranscoder is started before a consumer is set the transcoder
+ * will buffer a limited number of samples internally before stalling. Once a consumer has been
+ * set the internally buffered samples will be delivered to the consumer.
+ * @param sampleConsumer The sample consumer function.
+ */
+ void setSampleConsumer(const MediaSampleWriter::MediaSampleConsumerFunction& sampleConsumer);
+
+ /**
+ * Retrieves the track transcoder's final output format. The output is available after the
+ * track transcoder has been successfully configured.
+ * @return The track output format.
+ */
+ virtual std::shared_ptr<AMediaFormat> getOutputFormat() const = 0;
+
+ virtual ~MediaTrackTranscoder() = default;
+
+protected:
+ MediaTrackTranscoder(const std::weak_ptr<MediaTrackTranscoderCallback>& transcoderCallback)
+ : mTranscoderCallback(transcoderCallback){};
+
+ // Called by subclasses when the actual track format becomes available.
+ void notifyTrackFormatAvailable();
+
+ // Called by subclasses when a transcoded sample is available. Samples must not hold a strong
+ // reference to the track transcoder in order to avoid retain cycles through the track
+ // transcoder's sample queue.
+ void onOutputSampleAvailable(const std::shared_ptr<MediaSample>& sample);
+
+ // configureDestinationFormat needs to be implemented by subclasses, and gets called on an
+ // external thread before start.
+ virtual media_status_t configureDestinationFormat(
+ const std::shared_ptr<AMediaFormat>& destinationFormat) = 0;
+
+ // runTranscodeLoop needs to be implemented by subclasses, and gets called on
+ // MediaTrackTranscoder's internal thread when the track transcoder is started.
+ virtual media_status_t runTranscodeLoop(bool* stopped) = 0;
+
+ // abortTranscodeLoop needs to be implemented by subclasses, and should request transcoding to
+ // be aborted as soon as possible. It should be safe to call abortTranscodeLoop multiple times.
+ virtual void abortTranscodeLoop() = 0;
+
+ std::shared_ptr<MediaSampleReader> mMediaSampleReader;
+ int mTrackIndex;
+ std::shared_ptr<AMediaFormat> mSourceFormat;
+
+ enum StopRequest {
+ NONE,
+ STOP_NOW,
+ STOP_ON_SYNC,
+ };
+ std::atomic<StopRequest> mStopRequest = NONE;
+
+private:
+ std::mutex mSampleMutex;
+ // SampleQueue for buffering output samples before a sample consumer has been set.
+ MediaSampleQueue mSampleQueue GUARDED_BY(mSampleMutex);
+ MediaSampleWriter::MediaSampleConsumerFunction mSampleConsumer GUARDED_BY(mSampleMutex);
+ const std::weak_ptr<MediaTrackTranscoderCallback> mTranscoderCallback;
+ std::mutex mStateMutex;
+ enum {
+ UNINITIALIZED,
+ CONFIGURED,
+ STARTED,
+ STOPPED,
+ } mState GUARDED_BY(mStateMutex) = UNINITIALIZED;
+};
+
+} // namespace android
+#endif // ANDROID_MEDIA_TRACK_TRANSCODER_H
diff --git a/media/libmediatranscoding/transcoder/include/media/MediaTrackTranscoderCallback.h b/media/libmediatranscoding/transcoder/include/media/MediaTrackTranscoderCallback.h
new file mode 100644
index 0000000..7b62d46
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/include/media/MediaTrackTranscoderCallback.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TRACK_TRANSCODER_CALLBACK_H
+#define ANDROID_MEDIA_TRACK_TRANSCODER_CALLBACK_H
+
+#include <media/NdkMediaError.h>
+
+namespace android {
+
+class MediaTrackTranscoder;
+
+/** Callback interface for MediaTrackTranscoder. */
+class MediaTrackTranscoderCallback {
+public:
+ /**
+ * Called when the MediaTrackTranscoder's actual track format becomes available.
+ * @param transcoder The MediaTrackTranscoder whose track format becomes available.
+ */
+ virtual void onTrackFormatAvailable(const MediaTrackTranscoder* transcoder);
+ /**
+ * Called when the MediaTrackTranscoder instance have finished transcoding all media samples
+ * successfully.
+ * @param transcoder The MediaTrackTranscoder that finished the transcoding.
+ */
+ virtual void onTrackFinished(const MediaTrackTranscoder* transcoder);
+
+ /**
+ * Called when the MediaTrackTranscoder instance was explicitly stopped before it was finished.
+ * @param transcoder The MediaTrackTranscoder that was stopped.
+ */
+ virtual void onTrackStopped(const MediaTrackTranscoder* transcoder);
+
+ /**
+ * Called when the MediaTrackTranscoder instance encountered an error it could not recover from.
+ * @param transcoder The MediaTrackTranscoder that encountered the error.
+ * @param status The non-zero error code describing the encountered error.
+ */
+ virtual void onTrackError(const MediaTrackTranscoder* transcoder, media_status_t status);
+
+protected:
+ virtual ~MediaTrackTranscoderCallback() = default;
+};
+
+} // namespace android
+#endif // ANDROID_MEDIA_TRACK_TRANSCODER_CALLBACK_H
diff --git a/media/libmediatranscoding/transcoder/include/media/MediaTranscoder.h b/media/libmediatranscoding/transcoder/include/media/MediaTranscoder.h
new file mode 100644
index 0000000..8776dc9
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/include/media/MediaTranscoder.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TRANSCODER_H
+#define ANDROID_MEDIA_TRANSCODER_H
+
+#include <android/binder_auto_utils.h>
+#include <media/MediaSampleWriter.h>
+#include <media/MediaTrackTranscoderCallback.h>
+#include <media/NdkMediaCodecPlatform.h>
+#include <media/NdkMediaError.h>
+#include <media/NdkMediaFormat.h>
+#include <utils/Mutex.h>
+
+#include <atomic>
+#include <memory>
+#include <mutex>
+#include <unordered_set>
+
+namespace android {
+
+class MediaSampleReader;
+
+class MediaTranscoder : public std::enable_shared_from_this<MediaTranscoder>,
+ public MediaTrackTranscoderCallback,
+ public MediaSampleWriter::CallbackInterface {
+public:
+ /** Callbacks from transcoder to client. */
+ class CallbackInterface {
+ public:
+ /** Transcoder finished successfully. */
+ virtual void onFinished(const MediaTranscoder* transcoder) = 0;
+
+ /** Transcoder encountered an unrecoverable error. */
+ virtual void onError(const MediaTranscoder* transcoder, media_status_t error) = 0;
+
+ /** Transcoder progress update reported in percent from 0 to 100. */
+ virtual void onProgressUpdate(const MediaTranscoder* transcoder, int32_t progress) = 0;
+
+ /** Transcoder heart-beat signal. */
+ virtual void onHeartBeat(const MediaTranscoder* transcoder) = 0;
+
+ /**
+ * Transcoder lost codec resources and paused operations. The client can resume transcoding
+ * again when resources are available by either:
+ * 1) Calling resume on the same MediaTranscoder instance.
+ * 2) Creating a new MediaTranscoding instance with the paused state and then calling
+ * resume.
+ */
+ virtual void onCodecResourceLost(
+ const MediaTranscoder* transcoder,
+ const std::shared_ptr<ndk::ScopedAParcel>& pausedState) = 0;
+
+ virtual ~CallbackInterface() = default;
+ };
+
+ /**
+ * Creates a new MediaTranscoder instance. If the supplied paused state is valid, the transcoder
+ * will be initialized with the paused state and be ready to be resumed right away. It is not
+ * possible to change any configurations on a paused transcoder.
+ */
+ static std::shared_ptr<MediaTranscoder> create(
+ const std::shared_ptr<CallbackInterface>& callbacks, int64_t heartBeatIntervalUs = -1,
+ pid_t pid = AMEDIACODEC_CALLING_PID, uid_t uid = AMEDIACODEC_CALLING_UID,
+ const std::shared_ptr<ndk::ScopedAParcel>& pausedState = nullptr);
+
+ /** Configures source from path fd. */
+ media_status_t configureSource(int fd);
+
+ /** Gets the media formats of all tracks in the file. */
+ std::vector<std::shared_ptr<AMediaFormat>> getTrackFormats() const;
+
+ /**
+ * Configures transcoding of a track. Tracks that are not configured will not present in the
+ * final transcoded file, i.e. tracks will be dropped by default. Passing nullptr for
+ * trackFormat means the track will be copied unchanged ("passthrough") to the destination.
+ * Track configurations must be done after the source has been configured.
+ * Note: trackFormat is not modified but cannot be const.
+ */
+ media_status_t configureTrackFormat(size_t trackIndex, AMediaFormat* trackFormat);
+
+ /** Configures destination from fd. */
+ media_status_t configureDestination(int fd);
+
+ /** Starts transcoding. No configurations can be made once the transcoder has started. */
+ media_status_t start();
+
+ /**
+ * Pauses transcoding and finalizes the partial transcoded file to disk. Pause is a synchronous
+ * operation and will wait until all internal components are done. Once this method returns it
+ * is safe to release the transcoder instance. No callback will be called if the transcoder was
+ * paused successfully. But if the transcoding finishes or encountered an error during pause,
+ * the corresponding callback will be called.
+ */
+ media_status_t pause(std::shared_ptr<ndk::ScopedAParcel>* pausedState);
+
+ /** Resumes a paused transcoding. */
+ media_status_t resume();
+
+ /**
+ * Cancels the transcoding. Once canceled the transcoding can not be restarted. Client
+ * will be responsible for cleaning up the abandoned file. Cancel is a synchronous operation and
+ * will wait until all internal components are done. Once this method returns it is safe to
+ * release the transcoder instance. Normally no callback will be called when the transcoder is
+ * cancelled. But if the transcoding finishes or encountered an error during cancel, the
+ * corresponding callback will be called.
+ */
+ media_status_t cancel();
+
+ virtual ~MediaTranscoder() = default;
+
+private:
+ MediaTranscoder(const std::shared_ptr<CallbackInterface>& callbacks,
+ int64_t heartBeatIntervalUs, pid_t pid, uid_t uid);
+
+ // MediaTrackTranscoderCallback
+ virtual void onTrackFormatAvailable(const MediaTrackTranscoder* transcoder) override;
+ virtual void onTrackFinished(const MediaTrackTranscoder* transcoder) override;
+ virtual void onTrackStopped(const MediaTrackTranscoder* transcoder) override;
+ virtual void onTrackError(const MediaTrackTranscoder* transcoder,
+ media_status_t status) override;
+ // ~MediaTrackTranscoderCallback
+
+ // MediaSampleWriter::CallbackInterface
+ virtual void onFinished(const MediaSampleWriter* writer, media_status_t status) override;
+ virtual void onStopped(const MediaSampleWriter* writer) override;
+ virtual void onProgressUpdate(const MediaSampleWriter* writer, int32_t progress) override;
+ virtual void onHeartBeat(const MediaSampleWriter* writer) override;
+ // ~MediaSampleWriter::CallbackInterface
+
+ void onThreadFinished(const void* thread, media_status_t threadStatus, bool threadStopped);
+ media_status_t requestStop(bool stopOnSync);
+ void waitForThreads();
+
+ std::shared_ptr<CallbackInterface> mCallbacks;
+ std::shared_ptr<MediaSampleReader> mSampleReader;
+ std::shared_ptr<MediaSampleWriter> mSampleWriter;
+ std::vector<std::shared_ptr<AMediaFormat>> mSourceTrackFormats;
+ std::vector<std::shared_ptr<MediaTrackTranscoder>> mTrackTranscoders;
+ std::mutex mTracksAddedMutex;
+ std::unordered_set<const MediaTrackTranscoder*> mTracksAdded GUARDED_BY(mTracksAddedMutex);
+ int64_t mHeartBeatIntervalUs;
+ pid_t mPid;
+ uid_t mUid;
+
+ enum ThreadState {
+ PENDING = 0, // Not yet started.
+ RUNNING, // Currently running.
+ DONE, // Done running (can be finished, stopped or error).
+ };
+ std::mutex mThreadStateMutex;
+ std::condition_variable mThreadsDoneSignal;
+ std::unordered_map<const void*, ThreadState> mThreadStates GUARDED_BY(mThreadStateMutex);
+ media_status_t mTranscoderStatus GUARDED_BY(mThreadStateMutex) = AMEDIA_OK;
+ bool mTranscoderStopped GUARDED_BY(mThreadStateMutex) = false;
+ bool mThreadsDone GUARDED_BY(mThreadStateMutex) = false;
+ bool mCallbackSent GUARDED_BY(mThreadStateMutex) = false;
+ bool mSampleWriterStopped GUARDED_BY(mThreadStateMutex) = false;
+
+ std::atomic_bool mCancelled = false;
+};
+
+} // namespace android
+#endif // ANDROID_MEDIA_TRANSCODER_H
diff --git a/media/libmediatranscoding/transcoder/include/media/NdkCommon.h b/media/libmediatranscoding/transcoder/include/media/NdkCommon.h
new file mode 100644
index 0000000..2441922
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/include/media/NdkCommon.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TRANSCODING_NDK_COMMON_H
+#define ANDROID_MEDIA_TRANSCODING_NDK_COMMON_H
+
+#include <media/NdkMediaFormat.h>
+
+#include <vector>
+
+extern const char* AMEDIA_MIMETYPE_VIDEO_VP8;
+extern const char* AMEDIA_MIMETYPE_VIDEO_VP9;
+extern const char* AMEDIA_MIMETYPE_VIDEO_AV1;
+extern const char* AMEDIA_MIMETYPE_VIDEO_AVC;
+extern const char* AMEDIA_MIMETYPE_VIDEO_HEVC;
+extern const char* AMEDIA_MIMETYPE_VIDEO_MPEG4;
+extern const char* AMEDIA_MIMETYPE_VIDEO_H263;
+
+// TODO(b/146420990)
+// TODO: make MediaTranscoder use the consts from this header.
+typedef enum {
+ OUTPUT_FORMAT_START = 0,
+ OUTPUT_FORMAT_MPEG_4 = OUTPUT_FORMAT_START,
+ OUTPUT_FORMAT_WEBM = OUTPUT_FORMAT_START + 1,
+ OUTPUT_FORMAT_THREE_GPP = OUTPUT_FORMAT_START + 2,
+ OUTPUT_FORMAT_HEIF = OUTPUT_FORMAT_START + 3,
+ OUTPUT_FORMAT_OGG = OUTPUT_FORMAT_START + 4,
+ OUTPUT_FORMAT_LIST_END = OUTPUT_FORMAT_START + 4,
+} MuxerFormat;
+
+// Color formats supported by encoder - should mirror supportedColorList
+// from MediaCodecConstants.h (are these going to be deprecated)
+static constexpr int COLOR_FormatYUV420SemiPlanar = 21;
+static constexpr int COLOR_FormatYUV420Flexible = 0x7F420888;
+static constexpr int COLOR_FormatSurface = 0x7f000789;
+
+// Color transfer functions defined by MediaCodecConstants.h but not in NDK
+static constexpr int32_t COLOR_TRANSFER_HLG = 7;
+static constexpr int32_t COLOR_TRANSFER_LINEAR = 1;
+static constexpr int32_t COLOR_TRANSFER_SDR_VIDEO = 3;
+static constexpr int32_t COLOR_TRANSFER_ST2084 = 6;
+
+// constants not defined in NDK
+extern const char* TBD_AMEDIACODEC_PARAMETER_KEY_ALLOW_FRAME_DROP;
+extern const char* TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME;
+extern const char* TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE;
+extern const char* TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES;
+extern const char* TBD_AMEDIACODEC_PARAMETER_KEY_COLOR_TRANSFER_REQUEST;
+extern const char* TBD_AMEDIACODEC_PARAMETER_KEY_BACKGROUND_MODE;
+static constexpr int TBD_AMEDIACODEC_BUFFER_FLAG_KEY_FRAME = 0x1;
+
+static constexpr int kBitrateModeConstant = 2;
+
+namespace AMediaFormatUtils {
+
+typedef struct {
+ const char* key;
+ bool (*copy)(const char* key, AMediaFormat* from, AMediaFormat* to);
+ bool (*copy2)(const char* key, AMediaFormat* from, AMediaFormat* to);
+} EntryCopier;
+
+#define ENTRY_COPIER(keyName, typeName) \
+ { keyName, AMediaFormatUtils::CopyFormatEntry##typeName, nullptr }
+#define ENTRY_COPIER2(keyName, typeName, typeName2) \
+ { \
+ keyName, AMediaFormatUtils::CopyFormatEntry##typeName, \
+ AMediaFormatUtils::CopyFormatEntry##typeName2 \
+ }
+
+bool CopyFormatEntryString(const char* key, AMediaFormat* from, AMediaFormat* to);
+bool CopyFormatEntryInt64(const char* key, AMediaFormat* from, AMediaFormat* to);
+bool CopyFormatEntryInt32(const char* key, AMediaFormat* from, AMediaFormat* to);
+bool CopyFormatEntryFloat(const char* key, AMediaFormat* from, AMediaFormat* to);
+
+void CopyFormatEntries(AMediaFormat* from, AMediaFormat* to,
+ const std::vector<EntryCopier>& entries);
+
+bool SetDefaultFormatValueFloat(const char* key, AMediaFormat* format, float value);
+bool SetDefaultFormatValueInt32(const char* key, AMediaFormat* format, int32_t value);
+
+bool VideoIsHdr(AMediaFormat* format);
+
+} // namespace AMediaFormatUtils
+#endif // ANDROID_MEDIA_TRANSCODING_NDK_COMMON_H
diff --git a/media/libmediatranscoding/transcoder/include/media/PassthroughTrackTranscoder.h b/media/libmediatranscoding/transcoder/include/media/PassthroughTrackTranscoder.h
new file mode 100644
index 0000000..c074831
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/include/media/PassthroughTrackTranscoder.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PASSTHROUGH_TRACK_TRANSCODER_H
+#define ANDROID_PASSTHROUGH_TRACK_TRANSCODER_H
+
+#include <media/MediaTrackTranscoder.h>
+#include <media/NdkMediaFormat.h>
+
+#include <condition_variable>
+#include <map>
+#include <mutex>
+#include <unordered_map>
+
+namespace android {
+
+/**
+ * Track transcoder for passthrough mode. Passthrough mode copies sample data from a track unchanged
+ * from source file to destination file. This track transcoder uses an internal pool of buffers.
+ * When the maximum number of buffers are allocated and all of them are waiting on the output queue
+ * the transcoder will stall until samples are dequeued from the output queue and released.
+ */
+class PassthroughTrackTranscoder : public MediaTrackTranscoder {
+public:
+ /** Maximum number of buffers to be allocated at a given time. */
+ static constexpr int kMaxBufferCountDefault = 16;
+
+ PassthroughTrackTranscoder(
+ const std::weak_ptr<MediaTrackTranscoderCallback>& transcoderCallback)
+ : MediaTrackTranscoder(transcoderCallback),
+ mBufferPool(std::make_shared<BufferPool>(kMaxBufferCountDefault)){};
+ virtual ~PassthroughTrackTranscoder() override = default;
+
+private:
+ friend class BufferPoolTests;
+
+ /** Class to pool and reuse buffers. */
+ class BufferPool {
+ public:
+ explicit BufferPool(int maxBufferCount) : mMaxBufferCount(maxBufferCount){};
+ ~BufferPool();
+
+ /**
+ * Retrieve a buffer from the pool. Buffers are allocated on demand. This method will block
+ * if the maximum number of buffers is reached and there are no free buffers available.
+ * @param minimumBufferSize The minimum size of the buffer.
+ * @return The buffer or nullptr if allocation failed or the pool was aborted.
+ */
+ uint8_t* getBufferWithSize(size_t minimumBufferSize);
+
+ /**
+ * Return a buffer to the pool.
+ * @param buffer The buffer to return.
+ */
+ void returnBuffer(uint8_t* buffer);
+
+ /** Wakes up threads waiting on buffers and prevents new buffers from being returned. */
+ void abort();
+
+ private:
+ // Maximum number of active buffers at a time.
+ const int mMaxBufferCount;
+
+ // Map containing all tracked buffers.
+ std::unordered_map<uint8_t*, size_t> mAddressSizeMap GUARDED_BY(mMutex);
+
+ // Map containing the currently free buffers.
+ std::multimap<size_t, uint8_t*> mFreeBufferMap GUARDED_BY(mMutex);
+
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ bool mAborted GUARDED_BY(mMutex) = false;
+ };
+
+ // MediaTrackTranscoder
+ media_status_t runTranscodeLoop(bool* stopped) override;
+ void abortTranscodeLoop() override;
+ media_status_t configureDestinationFormat(
+ const std::shared_ptr<AMediaFormat>& destinationFormat) override;
+ std::shared_ptr<AMediaFormat> getOutputFormat() const override;
+ // ~MediaTrackTranscoder
+
+ std::shared_ptr<BufferPool> mBufferPool;
+};
+
+} // namespace android
+#endif // ANDROID_PASSTHROUGH_TRACK_TRANSCODER_H
diff --git a/media/libmediatranscoding/transcoder/include/media/VideoTrackTranscoder.h b/media/libmediatranscoding/transcoder/include/media/VideoTrackTranscoder.h
new file mode 100644
index 0000000..3e72882
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/include/media/VideoTrackTranscoder.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VIDEO_TRACK_TRANSCODER_H
+#define ANDROID_VIDEO_TRACK_TRANSCODER_H
+
+#include <android/native_window.h>
+#include <media/MediaTrackTranscoder.h>
+#include <media/NdkMediaCodecPlatform.h>
+#include <media/NdkMediaFormat.h>
+
+#include <condition_variable>
+#include <deque>
+#include <mutex>
+
+namespace android {
+
+/**
+ * Track transcoder for video tracks. VideoTrackTranscoder uses AMediaCodec from the Media NDK
+ * internally. The two media codecs are run in asynchronous mode and shares uncompressed buffers
+ * using a native surface (ANativeWindow). Codec callback events are placed on a message queue and
+ * serviced in order on the transcoding thread managed by MediaTrackTranscoder.
+ */
+class VideoTrackTranscoder : public std::enable_shared_from_this<VideoTrackTranscoder>,
+ public MediaTrackTranscoder {
+public:
+ static std::shared_ptr<VideoTrackTranscoder> create(
+ const std::weak_ptr<MediaTrackTranscoderCallback>& transcoderCallback,
+ pid_t pid = AMEDIACODEC_CALLING_PID, uid_t uid = AMEDIACODEC_CALLING_UID);
+
+ virtual ~VideoTrackTranscoder() override;
+
+private:
+ friend struct AsyncCodecCallbackDispatch;
+ friend class VideoTrackTranscoderTests;
+
+ // Minimal blocking queue used as a message queue by VideoTrackTranscoder.
+ template <typename T>
+ class BlockingQueue {
+ public:
+ void push(T const& value, bool front = false);
+ T pop();
+ void abort();
+
+ private:
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ std::deque<T> mQueue;
+ bool mAborted = false;
+ };
+ class CodecWrapper;
+
+ VideoTrackTranscoder(const std::weak_ptr<MediaTrackTranscoderCallback>& transcoderCallback,
+ pid_t pid, uid_t uid)
+ : MediaTrackTranscoder(transcoderCallback), mPid(pid), mUid(uid){};
+
+ // MediaTrackTranscoder
+ media_status_t runTranscodeLoop(bool* stopped) override;
+ void abortTranscodeLoop() override;
+ media_status_t configureDestinationFormat(
+ const std::shared_ptr<AMediaFormat>& destinationFormat) override;
+ std::shared_ptr<AMediaFormat> getOutputFormat() const override;
+ // ~MediaTrackTranscoder
+
+ // Enqueues an input sample with the decoder.
+ void enqueueInputSample(int32_t bufferIndex);
+
+ // Moves a decoded buffer from the decoder's output to the encoder's input.
+ void transferBuffer(int32_t bufferIndex, AMediaCodecBufferInfo bufferInfo);
+
+ // Dequeues an encoded buffer from the encoder and adds it to the output queue.
+ void dequeueOutputSample(int32_t bufferIndex, AMediaCodecBufferInfo bufferInfo);
+
+ // Updates the video track's actual format based on encoder and decoder output format.
+ void updateTrackFormat(AMediaFormat* outputFormat, bool fromDecoder);
+
+ AMediaCodec* mDecoder = nullptr;
+ std::shared_ptr<CodecWrapper> mEncoder;
+ ANativeWindow* mSurface = nullptr;
+ bool mEosFromSource = false;
+ bool mEosFromEncoder = false;
+ bool mLastSampleWasSync = false;
+ media_status_t mStatus = AMEDIA_OK;
+ MediaSampleInfo mSampleInfo;
+ BlockingQueue<std::function<void()>> mCodecMessageQueue;
+ std::shared_ptr<AMediaFormat> mDestinationFormat;
+ std::shared_ptr<AMediaFormat> mActualOutputFormat;
+ pid_t mPid;
+ uid_t mUid;
+ uint64_t mInputFrameCount = 0;
+ uint64_t mOutputFrameCount = 0;
+ int32_t mConfiguredBitrate = 0;
+};
+
+} // namespace android
+#endif // ANDROID_VIDEO_TRACK_TRANSCODER_H
diff --git a/media/libmediatranscoding/transcoder/setloglevel.sh b/media/libmediatranscoding/transcoder/setloglevel.sh
new file mode 100755
index 0000000..b0f0a2e
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/setloglevel.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+if [ $# -ne 1 ]
+then
+ echo "Usage 1: $0 <loglevel>"
+ echo " Set all transcoder log tags to <loglevel>"
+ echo "Usage 2: $0 -l"
+ echo " List all transcoder log tags and exit"
+ exit 1
+fi
+
+# List all log tags
+declare -a tags=(
+ MediaTranscoder MediaTrackTranscoder VideoTrackTranscoder PassthroughTrackTranscoder
+ MediaSampleWriter MediaSampleReader MediaSampleQueue MediaTranscoderTests
+ MediaTrackTranscoderTests VideoTrackTranscoderTests PassthroughTrackTranscoderTests
+ MediaSampleWriterTests MediaSampleReaderNDKTests MediaSampleQueueTests HdrTranscodeTests)
+
+if [ "$1" == "-l" ]; then
+ echo "Transcoder log tags:"
+ for tag in "${tags[@]}"; do echo -n "$tag "; done
+ echo
+ exit 0
+fi
+
+level=$1
+echo Setting transcoder log level to $level
+
+# Set log level for all tags
+for tag in "${tags[@]}"
+do
+ adb shell setprop log.tag.${tag} $level
+done
+
+# Pick up new settings
+adb shell stop && adb shell start
diff --git a/media/libmediatranscoding/transcoder/tests/Android.bp b/media/libmediatranscoding/transcoder/tests/Android.bp
new file mode 100644
index 0000000..11b19c9
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/tests/Android.bp
@@ -0,0 +1,104 @@
+// Unit tests for libmediatranscoder.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_defaults {
+ name: "testdefaults",
+
+ header_libs: [
+ "libbase_headers",
+ "libmedia_headers",
+ ],
+
+ static_libs: [
+ "libmediatranscoder",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libcrypto",
+ "libcutils",
+ "libmediandk",
+ "libnativewindow",
+ "libutils",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ cfi: true,
+ },
+
+ data: [":test_assets"],
+ test_config_template: "AndroidTestTemplate.xml",
+ test_suites: ["device-tests", "TranscoderTests"],
+}
+
+// MediaSampleReaderNDK unit test
+cc_test {
+ name: "MediaSampleReaderNDKTests",
+ defaults: ["testdefaults"],
+ srcs: ["MediaSampleReaderNDKTests.cpp"],
+}
+
+// MediaSampleQueue unit test
+cc_test {
+ name: "MediaSampleQueueTests",
+ defaults: ["testdefaults"],
+ srcs: ["MediaSampleQueueTests.cpp"],
+}
+
+// MediaTrackTranscoder unit test
+cc_test {
+ name: "MediaTrackTranscoderTests",
+ defaults: ["testdefaults"],
+ srcs: ["MediaTrackTranscoderTests.cpp"],
+}
+
+// VideoTrackTranscoder unit test
+cc_test {
+ name: "VideoTrackTranscoderTests",
+ defaults: ["testdefaults"],
+ srcs: ["VideoTrackTranscoderTests.cpp"],
+}
+
+// PassthroughTrackTranscoder unit test
+cc_test {
+ name: "PassthroughTrackTranscoderTests",
+ defaults: ["testdefaults"],
+ srcs: ["PassthroughTrackTranscoderTests.cpp"],
+}
+
+// MediaSampleWriter unit test
+cc_test {
+ name: "MediaSampleWriterTests",
+ defaults: ["testdefaults"],
+ srcs: ["MediaSampleWriterTests.cpp"],
+}
+
+// HDR Transcode unit test
+cc_test {
+ name: "HdrTranscodeTests",
+ defaults: ["testdefaults"],
+ srcs: ["HdrTranscodeTests.cpp"],
+}
+
+// MediaTranscoder unit test
+cc_test {
+ name: "MediaTranscoderTests",
+ defaults: ["testdefaults"],
+ srcs: ["MediaTranscoderTests.cpp"],
+}
diff --git a/media/libmediatranscoding/transcoder/tests/AndroidTestTemplate.xml b/media/libmediatranscoding/transcoder/tests/AndroidTestTemplate.xml
new file mode 100644
index 0000000..c3a0ced
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/tests/AndroidTestTemplate.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Unit test configuration for {MODULE}">
+ <option name="test-suite-tag" value="TranscoderTests" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="false" />
+ <option name="push-file" key="TranscodingTestAssets" value="/data/local/tmp/TranscodingTestAssets" />
+ <option name="push-file" key="{MODULE}" value="/data/local/tmp/{MODULE}" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="{MODULE}" />
+ <option name="native-test-timeout" value="30m" />
+ </test>
+</configuration>
+
diff --git a/media/libmediatranscoding/transcoder/tests/HdrTranscodeTests.cpp b/media/libmediatranscoding/transcoder/tests/HdrTranscodeTests.cpp
new file mode 100644
index 0000000..3a2882b
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/tests/HdrTranscodeTests.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Unit Test for HDR to SDR transcoding.
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "HdrTranscodeTests"
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/binder_process.h>
+#include <fcntl.h>
+#include <gtest/gtest.h>
+#include <media/MediaSampleReaderNDK.h>
+#include <media/MediaTranscoder.h>
+#include <media/NdkCommon.h>
+
+#include "TranscoderTestUtils.h"
+
+namespace android {
+
+// Debug property to load the sample HDR plugin.
+static const std::string kLoadSamplePluginProperty{"debug.codec2.force-sample-plugin"};
+
+// SDR color standard, from MediaFormat.
+static constexpr int COLOR_STANDARD_BT709 = 1;
+
+class HdrTranscodeTests : public ::testing::Test {
+public:
+ HdrTranscodeTests() { LOG(DEBUG) << "HdrTranscodeTests created"; }
+ ~HdrTranscodeTests() { LOG(DEBUG) << "HdrTranscodeTests destroyed"; }
+
+ void SetUp() override {
+ LOG(DEBUG) << "HdrTranscodeTests set up";
+ mCallbacks = std::make_shared<TestTranscoderCallbacks>();
+ ABinderProcess_startThreadPool();
+ }
+
+ void TearDown() override {
+ LOG(DEBUG) << "HdrTranscodeTests tear down";
+ mCallbacks.reset();
+ }
+
+ media_status_t transcode(const char* srcFile, const char* dstFile, const char* dstMime) {
+ std::string srcPath = mSrcDir + srcFile;
+ std::string dstPath = mDstDir + dstFile;
+
+ auto transcoder = MediaTranscoder::create(mCallbacks, -1 /*heartBeatIntervalUs*/);
+ EXPECT_NE(transcoder, nullptr);
+
+ const int srcFd = open(srcPath.c_str(), O_RDONLY);
+ EXPECT_EQ(transcoder->configureSource(srcFd), AMEDIA_OK);
+ close(srcFd);
+
+ std::vector<std::shared_ptr<AMediaFormat>> trackFormats = transcoder->getTrackFormats();
+ EXPECT_GT(trackFormats.size(), 0);
+
+ for (int i = 0; i < trackFormats.size(); ++i) {
+ std::shared_ptr<AMediaFormat> format;
+ const char* mime = nullptr;
+
+ AMediaFormat_getString(trackFormats[i].get(), AMEDIAFORMAT_KEY_MIME, &mime);
+ if (strncmp(mime, "video/", 6) == 0) {
+ format = std::shared_ptr<AMediaFormat>(AMediaFormat_new(), &AMediaFormat_delete);
+ AMediaFormat_setString(format.get(), AMEDIAFORMAT_KEY_MIME, dstMime);
+ }
+
+ media_status_t status = transcoder->configureTrackFormat(i, format.get());
+ if (status != AMEDIA_OK) {
+ return status;
+ }
+ }
+
+ const int dstFd = open(dstPath.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+ EXPECT_EQ(transcoder->configureDestination(dstFd), AMEDIA_OK);
+ close(dstFd);
+
+ media_status_t startStatus = transcoder->start();
+ EXPECT_EQ(startStatus, AMEDIA_OK);
+ if (startStatus != AMEDIA_OK) {
+ return startStatus;
+ }
+
+ mCallbacks->waitForTranscodingFinished();
+ return mCallbacks->mStatus;
+ }
+
+ media_status_t validateOutput(const char* dstFile __unused) {
+ std::string path = mDstDir + dstFile;
+
+ auto format = TranscoderTestUtils::GetVideoFormat(path);
+ EXPECT_NE(format.get(), nullptr);
+
+ int32_t value;
+ EXPECT_TRUE(AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_COLOR_STANDARD, &value));
+ EXPECT_EQ(value, COLOR_STANDARD_BT709);
+
+ EXPECT_TRUE(AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_COLOR_TRANSFER, &value));
+ EXPECT_EQ(value, COLOR_TRANSFER_SDR_VIDEO);
+
+ // TODO(lnilsson): Validate decoded pixels as well. Either by comparing similarity against a
+ // known good "golden master" corresponding SDR video, or by looking at the histogram.
+ return AMEDIA_OK;
+ }
+
+ bool hdrToSdrConversionSupported(const char* hdrFile) {
+ std::string srcPath = mSrcDir + hdrFile;
+
+ std::string mime;
+ auto format = TranscoderTestUtils::GetVideoFormat(srcPath, &mime);
+ EXPECT_NE(format.get(), nullptr);
+
+ AMediaCodec* decoder = AMediaCodec_createDecoderByType(mime.c_str());
+ EXPECT_NE(decoder, nullptr);
+
+ AMediaFormat_setInt32(format.get(), TBD_AMEDIACODEC_PARAMETER_KEY_COLOR_TRANSFER_REQUEST,
+ COLOR_TRANSFER_SDR_VIDEO);
+
+ EXPECT_EQ(AMediaCodec_configure(decoder, format.get(), nullptr /*surface*/,
+ nullptr /*crypto*/, 0 /*flags*/),
+ AMEDIA_OK);
+
+ AMediaFormat* inputFormat = AMediaCodec_getInputFormat(decoder);
+ EXPECT_NE(inputFormat, nullptr);
+
+ int32_t transferFunc;
+ bool conversionSupported =
+ AMediaFormat_getInt32(inputFormat,
+ TBD_AMEDIACODEC_PARAMETER_KEY_COLOR_TRANSFER_REQUEST,
+ &transferFunc) &&
+ transferFunc == COLOR_TRANSFER_SDR_VIDEO;
+
+ AMediaFormat_delete(inputFormat);
+ AMediaCodec_delete(decoder);
+
+ return conversionSupported;
+ }
+
+ std::shared_ptr<TestTranscoderCallbacks> mCallbacks;
+ const std::string mSrcDir{"/data/local/tmp/TranscodingTestAssets/"};
+ const std::string mDstDir{"/data/local/tmp/"};
+};
+
+TEST_F(HdrTranscodeTests, TestHdrSamplePluginTranscode) {
+ const char* hdrFile = "video_1280x720_hevc_hdr10_static_3mbps.mp4";
+ const char* dstFile = "video_1280x720_hevc_hdr10_static_3mbps_transcoded.mp4";
+
+ EXPECT_TRUE(android::base::SetProperty(kLoadSamplePluginProperty, "true"));
+
+ if (hdrToSdrConversionSupported(hdrFile)) {
+ LOG(INFO) << "HDR -> SDR supported, validating output..";
+ EXPECT_EQ(transcode(hdrFile, dstFile, AMEDIA_MIMETYPE_VIDEO_AVC), AMEDIA_OK);
+ EXPECT_EQ(validateOutput(dstFile), AMEDIA_OK);
+ } else {
+ LOG(INFO) << "HDR -> SDR *not* supported";
+ EXPECT_EQ(transcode(hdrFile, dstFile, AMEDIA_MIMETYPE_VIDEO_AVC), AMEDIA_ERROR_UNSUPPORTED);
+ }
+
+ EXPECT_TRUE(android::base::SetProperty(kLoadSamplePluginProperty, "false"));
+}
+} // namespace android
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/media/libmediatranscoding/transcoder/tests/MediaSampleQueueTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaSampleQueueTests.cpp
new file mode 100644
index 0000000..6357e4d
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/tests/MediaSampleQueueTests.cpp
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Unit Test for MediaSampleQueue
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "MediaSampleQueueTests"
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+#include <media/MediaSampleQueue.h>
+
+#include <thread>
+
+namespace android {
+
+/** Duration to use when delaying threads to order operations. */
+static constexpr int64_t kThreadDelayDurationMs = 100;
+
+class MediaSampleQueueTests : public ::testing::Test {
+public:
+ MediaSampleQueueTests() { LOG(DEBUG) << "MediaSampleQueueTests created"; }
+ ~MediaSampleQueueTests() { LOG(DEBUG) << "MediaSampleQueueTests destroyed"; }
+};
+
+static std::shared_ptr<MediaSample> newSample(uint32_t id) {
+ return MediaSample::createWithReleaseCallback(nullptr /* buffer */, 0 /* offset */, id,
+ nullptr /* callback */);
+}
+
+TEST_F(MediaSampleQueueTests, TestSequentialDequeueOrder) {
+ LOG(DEBUG) << "TestSequentialDequeueOrder Starts";
+
+ static constexpr int kNumSamples = 4;
+ MediaSampleQueue sampleQueue;
+ EXPECT_TRUE(sampleQueue.isEmpty());
+
+ // Enqueue loop.
+ for (int i = 0; i < kNumSamples; ++i) {
+ sampleQueue.enqueue(newSample(i));
+ EXPECT_FALSE(sampleQueue.isEmpty());
+ }
+
+ // Dequeue loop.
+ for (int i = 0; i < kNumSamples; ++i) {
+ std::shared_ptr<MediaSample> sample;
+ bool aborted = sampleQueue.dequeue(&sample);
+ EXPECT_NE(sample, nullptr);
+ EXPECT_EQ(sample->bufferId, i);
+ EXPECT_FALSE(aborted);
+ }
+ EXPECT_TRUE(sampleQueue.isEmpty());
+}
+
+TEST_F(MediaSampleQueueTests, TestInterleavedDequeueOrder) {
+ LOG(DEBUG) << "TestInterleavedDequeueOrder Starts";
+
+ static constexpr int kNumSamples = 4;
+ MediaSampleQueue sampleQueue;
+
+ // Enqueue and dequeue.
+ for (int i = 0; i < kNumSamples; ++i) {
+ sampleQueue.enqueue(newSample(i));
+ EXPECT_FALSE(sampleQueue.isEmpty());
+
+ std::shared_ptr<MediaSample> sample;
+ bool aborted = sampleQueue.dequeue(&sample);
+ EXPECT_NE(sample, nullptr);
+ EXPECT_EQ(sample->bufferId, i);
+ EXPECT_FALSE(aborted);
+ EXPECT_TRUE(sampleQueue.isEmpty());
+ }
+}
+
+TEST_F(MediaSampleQueueTests, TestBlockingDequeue) {
+ LOG(DEBUG) << "TestBlockingDequeue Starts";
+
+ MediaSampleQueue sampleQueue;
+
+ std::thread enqueueThread([&sampleQueue] {
+ // Note: This implementation is a bit racy. Any amount of sleep will not guarantee that the
+ // main thread will be blocked on the sample queue by the time this thread calls enqueue.
+ // But we can say with high confidence that it will and the test will not fail regardless.
+ std::this_thread::sleep_for(std::chrono::milliseconds(kThreadDelayDurationMs));
+ sampleQueue.enqueue(newSample(1));
+ });
+
+ std::shared_ptr<MediaSample> sample;
+ bool aborted = sampleQueue.dequeue(&sample);
+ EXPECT_NE(sample, nullptr);
+ EXPECT_EQ(sample->bufferId, 1);
+ EXPECT_FALSE(aborted);
+ EXPECT_TRUE(sampleQueue.isEmpty());
+
+ enqueueThread.join();
+}
+
+TEST_F(MediaSampleQueueTests, TestDequeueBufferRelease) {
+ LOG(DEBUG) << "TestDequeueBufferRelease Starts";
+
+ static constexpr int kNumSamples = 4;
+ std::vector<bool> bufferReleased(kNumSamples, false);
+
+ MediaSample::OnSampleReleasedCallback callback = [&bufferReleased](MediaSample* sample) {
+ bufferReleased[sample->bufferId] = true;
+ };
+
+ MediaSampleQueue sampleQueue;
+ for (int i = 0; i < kNumSamples; ++i) {
+ bool aborted = sampleQueue.enqueue(
+ MediaSample::createWithReleaseCallback(nullptr, 0, i, callback));
+ EXPECT_FALSE(aborted);
+ }
+
+ for (int i = 0; i < kNumSamples; ++i) {
+ EXPECT_FALSE(bufferReleased[i]);
+ }
+
+ for (int i = 0; i < kNumSamples; ++i) {
+ {
+ std::shared_ptr<MediaSample> sample;
+ bool aborted = sampleQueue.dequeue(&sample);
+ EXPECT_NE(sample, nullptr);
+ EXPECT_EQ(sample->bufferId, i);
+ EXPECT_FALSE(bufferReleased[i]);
+ EXPECT_FALSE(aborted);
+ }
+
+ for (int j = 0; j < kNumSamples; ++j) {
+ EXPECT_EQ(bufferReleased[j], j <= i);
+ }
+ }
+}
+
+TEST_F(MediaSampleQueueTests, TestAbortBufferRelease) {
+ LOG(DEBUG) << "TestAbortBufferRelease Starts";
+
+ static constexpr int kNumSamples = 4;
+ std::vector<bool> bufferReleased(kNumSamples, false);
+
+ MediaSample::OnSampleReleasedCallback callback = [&bufferReleased](MediaSample* sample) {
+ bufferReleased[sample->bufferId] = true;
+ };
+
+ MediaSampleQueue sampleQueue;
+ for (int i = 0; i < kNumSamples; ++i) {
+ bool aborted = sampleQueue.enqueue(
+ MediaSample::createWithReleaseCallback(nullptr, 0, i, callback));
+ EXPECT_FALSE(aborted);
+ }
+
+ for (int i = 0; i < kNumSamples; ++i) {
+ EXPECT_FALSE(bufferReleased[i]);
+ }
+
+ EXPECT_FALSE(sampleQueue.isEmpty());
+ sampleQueue.abort();
+ EXPECT_TRUE(sampleQueue.isEmpty());
+
+ for (int i = 0; i < kNumSamples; ++i) {
+ EXPECT_TRUE(bufferReleased[i]);
+ }
+}
+
+TEST_F(MediaSampleQueueTests, TestNonEmptyAbort) {
+ LOG(DEBUG) << "TestNonEmptyAbort Starts";
+
+ MediaSampleQueue sampleQueue;
+ bool aborted = sampleQueue.enqueue(newSample(1));
+ EXPECT_FALSE(aborted);
+
+ sampleQueue.abort();
+
+ std::shared_ptr<MediaSample> sample;
+ aborted = sampleQueue.dequeue(&sample);
+ EXPECT_TRUE(aborted);
+ EXPECT_EQ(sample, nullptr);
+
+ aborted = sampleQueue.enqueue(sample);
+ EXPECT_TRUE(aborted);
+}
+
+TEST_F(MediaSampleQueueTests, TestEmptyAbort) {
+ LOG(DEBUG) << "TestEmptyAbort Starts";
+
+ MediaSampleQueue sampleQueue;
+ sampleQueue.abort();
+
+ std::shared_ptr<MediaSample> sample;
+ bool aborted = sampleQueue.dequeue(&sample);
+ EXPECT_TRUE(aborted);
+ EXPECT_EQ(sample, nullptr);
+
+ aborted = sampleQueue.enqueue(sample);
+ EXPECT_TRUE(aborted);
+}
+
+TEST_F(MediaSampleQueueTests, TestBlockingAbort) {
+ LOG(DEBUG) << "TestBlockingAbort Starts";
+
+ MediaSampleQueue sampleQueue;
+
+ std::thread abortingThread([&sampleQueue] {
+ // Note: This implementation is a bit racy. Any amount of sleep will not guarantee that the
+ // main thread will be blocked on the sample queue by the time this thread calls abort.
+ // But we can say with high confidence that it will and the test will not fail regardless.
+ std::this_thread::sleep_for(std::chrono::milliseconds(kThreadDelayDurationMs));
+ sampleQueue.abort();
+ });
+
+ std::shared_ptr<MediaSample> sample;
+ bool aborted = sampleQueue.dequeue(&sample);
+ EXPECT_TRUE(aborted);
+ EXPECT_EQ(sample, nullptr);
+
+ abortingThread.join();
+}
+
+} // namespace android
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/media/libmediatranscoding/transcoder/tests/MediaSampleReaderNDKTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaSampleReaderNDKTests.cpp
new file mode 100644
index 0000000..3a499e5
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/tests/MediaSampleReaderNDKTests.cpp
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Unit Test for MediaSampleReaderNDK
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "MediaSampleReaderNDKTests"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <fcntl.h>
+#include <gtest/gtest.h>
+#include <media/MediaSampleReaderNDK.h>
+#include <openssl/md5.h>
+#include <utils/Timers.h>
+
+#include <cmath>
+#include <mutex>
+#include <thread>
+
+// TODO(b/153453392): Test more asset types (frame reordering?).
+
+namespace android {
+
+#define SEC_TO_USEC(s) ((s)*1000 * 1000)
+
+/** Helper class for comparing sample data using checksums. */
+class Sample {
+public:
+ Sample(uint32_t flags, int64_t timestamp, size_t size, const uint8_t* buffer)
+ : mFlags{flags}, mTimestamp{timestamp}, mSize{size} {
+ initChecksum(buffer);
+ }
+
+ Sample(AMediaExtractor* extractor) {
+ mFlags = AMediaExtractor_getSampleFlags(extractor);
+ mTimestamp = AMediaExtractor_getSampleTime(extractor);
+ mSize = static_cast<size_t>(AMediaExtractor_getSampleSize(extractor));
+
+ auto buffer = std::make_unique<uint8_t[]>(mSize);
+ AMediaExtractor_readSampleData(extractor, buffer.get(), mSize);
+
+ initChecksum(buffer.get());
+ }
+
+ void initChecksum(const uint8_t* buffer) {
+ MD5_CTX md5Ctx;
+ MD5_Init(&md5Ctx);
+ MD5_Update(&md5Ctx, buffer, mSize);
+ MD5_Final(mChecksum, &md5Ctx);
+ }
+
+ bool operator==(const Sample& rhs) const {
+ return mSize == rhs.mSize && mFlags == rhs.mFlags && mTimestamp == rhs.mTimestamp &&
+ memcmp(mChecksum, rhs.mChecksum, MD5_DIGEST_LENGTH) == 0;
+ }
+
+ uint32_t mFlags;
+ int64_t mTimestamp;
+ size_t mSize;
+ uint8_t mChecksum[MD5_DIGEST_LENGTH];
+};
+
+/** Constant for selecting all samples. */
+static constexpr int SAMPLE_COUNT_ALL = -1;
+
+/**
+ * Utility class to test different sample access patterns combined with sequential or parallel
+ * sample access modes.
+ */
+class SampleAccessTester {
+public:
+ SampleAccessTester(int sourceFd, size_t fileSize) {
+ mSampleReader = MediaSampleReaderNDK::createFromFd(sourceFd, 0, fileSize);
+ EXPECT_TRUE(mSampleReader);
+
+ mTrackCount = mSampleReader->getTrackCount();
+
+ for (int trackIndex = 0; trackIndex < mTrackCount; trackIndex++) {
+ EXPECT_EQ(mSampleReader->selectTrack(trackIndex), AMEDIA_OK);
+ }
+
+ mSamples.resize(mTrackCount);
+ mTrackThreads.resize(mTrackCount);
+ }
+
+ void getSampleInfo(int trackIndex) {
+ MediaSampleInfo info;
+ media_status_t status = mSampleReader->getSampleInfoForTrack(trackIndex, &info);
+ EXPECT_EQ(status, AMEDIA_OK);
+ }
+
+ void readSamplesAsync(int trackIndex, int sampleCount) {
+ mTrackThreads[trackIndex] = std::thread{[this, trackIndex, sampleCount] {
+ int samplesRead = 0;
+ MediaSampleInfo info;
+ while (samplesRead < sampleCount || sampleCount == SAMPLE_COUNT_ALL) {
+ media_status_t status = mSampleReader->getSampleInfoForTrack(trackIndex, &info);
+ if (status != AMEDIA_OK) {
+ EXPECT_EQ(status, AMEDIA_ERROR_END_OF_STREAM);
+ EXPECT_TRUE((info.flags & SAMPLE_FLAG_END_OF_STREAM) != 0);
+ break;
+ }
+ ASSERT_TRUE((info.flags & SAMPLE_FLAG_END_OF_STREAM) == 0);
+
+ auto buffer = std::make_unique<uint8_t[]>(info.size);
+ status = mSampleReader->readSampleDataForTrack(trackIndex, buffer.get(), info.size);
+ EXPECT_EQ(status, AMEDIA_OK);
+
+ mSampleMutex.lock();
+ const uint8_t* bufferPtr = buffer.get();
+ mSamples[trackIndex].emplace_back(info.flags, info.presentationTimeUs, info.size,
+ bufferPtr);
+ mSampleMutex.unlock();
+ ++samplesRead;
+ }
+ }};
+ }
+
+ void readSamplesAsync(int sampleCount) {
+ for (int trackIndex = 0; trackIndex < mTrackCount; trackIndex++) {
+ readSamplesAsync(trackIndex, sampleCount);
+ }
+ }
+
+ void waitForTrack(int trackIndex) {
+ ASSERT_TRUE(mTrackThreads[trackIndex].joinable());
+ mTrackThreads[trackIndex].join();
+ }
+
+ void waitForTracks() {
+ for (int trackIndex = 0; trackIndex < mTrackCount; trackIndex++) {
+ waitForTrack(trackIndex);
+ }
+ }
+
+ void setEnforceSequentialAccess(bool enforce) {
+ media_status_t status = mSampleReader->setEnforceSequentialAccess(enforce);
+ EXPECT_EQ(status, AMEDIA_OK);
+ }
+
+ std::vector<std::vector<Sample>>& getSamples() { return mSamples; }
+
+ std::shared_ptr<MediaSampleReader> mSampleReader;
+ size_t mTrackCount;
+ std::mutex mSampleMutex;
+ std::vector<std::thread> mTrackThreads;
+ std::vector<std::vector<Sample>> mSamples;
+};
+
+class MediaSampleReaderNDKTests : public ::testing::Test {
+public:
+ MediaSampleReaderNDKTests() { LOG(DEBUG) << "MediaSampleReaderNDKTests created"; }
+
+ void SetUp() override {
+ LOG(DEBUG) << "MediaSampleReaderNDKTests set up";
+
+ // Need to start a thread pool to prevent AMediaExtractor binder calls from starving
+ // (b/155663561).
+ ABinderProcess_startThreadPool();
+
+ const char* sourcePath =
+ "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
+
+ mSourceFd = open(sourcePath, O_RDONLY);
+ ASSERT_GT(mSourceFd, 0);
+
+ mFileSize = lseek(mSourceFd, 0, SEEK_END);
+ lseek(mSourceFd, 0, SEEK_SET);
+
+ mExtractor = AMediaExtractor_new();
+ ASSERT_NE(mExtractor, nullptr);
+
+ media_status_t status =
+ AMediaExtractor_setDataSourceFd(mExtractor, mSourceFd, 0, mFileSize);
+ ASSERT_EQ(status, AMEDIA_OK);
+
+ mTrackCount = AMediaExtractor_getTrackCount(mExtractor);
+ for (size_t trackIndex = 0; trackIndex < mTrackCount; trackIndex++) {
+ AMediaExtractor_selectTrack(mExtractor, trackIndex);
+ }
+ }
+
+ void initExtractorSamples() {
+ if (mExtractorSamples.size() == mTrackCount) return;
+
+ // Save sample information, per track, as reported by the extractor.
+ mExtractorSamples.resize(mTrackCount);
+ do {
+ const int trackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor);
+ mExtractorSamples[trackIndex].emplace_back(mExtractor);
+ } while (AMediaExtractor_advance(mExtractor));
+
+ AMediaExtractor_seekTo(mExtractor, 0, AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC);
+ }
+
+ std::vector<int32_t> getTrackBitrates() {
+ size_t totalSize[mTrackCount];
+ memset(totalSize, 0, sizeof(totalSize));
+
+ do {
+ const int trackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor);
+ totalSize[trackIndex] += AMediaExtractor_getSampleSize(mExtractor);
+ } while (AMediaExtractor_advance(mExtractor));
+
+ AMediaExtractor_seekTo(mExtractor, 0, AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC);
+
+ std::vector<int32_t> bitrates;
+ for (int trackIndex = 0; trackIndex < mTrackCount; trackIndex++) {
+ int64_t durationUs;
+ AMediaFormat* trackFormat = AMediaExtractor_getTrackFormat(mExtractor, trackIndex);
+ EXPECT_NE(trackFormat, nullptr);
+ EXPECT_TRUE(AMediaFormat_getInt64(trackFormat, AMEDIAFORMAT_KEY_DURATION, &durationUs));
+ bitrates.push_back(roundf((float)totalSize[trackIndex] * 8 * 1000000 / durationUs));
+ }
+
+ return bitrates;
+ }
+
+ void compareSamples(std::vector<std::vector<Sample>>& readerSamples) {
+ initExtractorSamples();
+ EXPECT_EQ(readerSamples.size(), mTrackCount);
+
+ for (int trackIndex = 0; trackIndex < mTrackCount; trackIndex++) {
+ LOG(DEBUG) << "Track " << trackIndex << ", comparing "
+ << readerSamples[trackIndex].size() << " samples.";
+ EXPECT_EQ(readerSamples[trackIndex].size(), mExtractorSamples[trackIndex].size());
+ for (size_t sampleIndex = 0; sampleIndex < readerSamples[trackIndex].size();
+ sampleIndex++) {
+ EXPECT_EQ(readerSamples[trackIndex][sampleIndex],
+ mExtractorSamples[trackIndex][sampleIndex]);
+ }
+ }
+ }
+
+ void TearDown() override {
+ LOG(DEBUG) << "MediaSampleReaderNDKTests tear down";
+ AMediaExtractor_delete(mExtractor);
+ close(mSourceFd);
+ }
+
+ ~MediaSampleReaderNDKTests() { LOG(DEBUG) << "MediaSampleReaderNDKTests destroyed"; }
+
+ AMediaExtractor* mExtractor = nullptr;
+ size_t mTrackCount;
+ int mSourceFd;
+ size_t mFileSize;
+ std::vector<std::vector<Sample>> mExtractorSamples;
+};
+
+/** Reads all samples from all tracks in parallel. */
+TEST_F(MediaSampleReaderNDKTests, TestParallelSampleAccess) {
+ LOG(DEBUG) << "TestParallelSampleAccess Starts";
+
+ SampleAccessTester tester{mSourceFd, mFileSize};
+ tester.readSamplesAsync(SAMPLE_COUNT_ALL);
+ tester.waitForTracks();
+ compareSamples(tester.getSamples());
+}
+
+/** Reads all samples except the last in each track, before finishing. */
+TEST_F(MediaSampleReaderNDKTests, TestLastSampleBeforeEOS) {
+ LOG(DEBUG) << "TestLastSampleBeforeEOS Starts";
+ initExtractorSamples();
+
+ { // Natural track order
+ SampleAccessTester tester{mSourceFd, mFileSize};
+ for (int trackIndex = 0; trackIndex < mTrackCount; ++trackIndex) {
+ tester.readSamplesAsync(trackIndex, mExtractorSamples[trackIndex].size() - 1);
+ }
+ tester.waitForTracks();
+ for (int trackIndex = 0; trackIndex < mTrackCount; ++trackIndex) {
+ tester.readSamplesAsync(trackIndex, SAMPLE_COUNT_ALL);
+ tester.waitForTrack(trackIndex);
+ }
+ compareSamples(tester.getSamples());
+ }
+
+ { // Reverse track order
+ SampleAccessTester tester{mSourceFd, mFileSize};
+ for (int trackIndex = mTrackCount - 1; trackIndex >= 0; --trackIndex) {
+ tester.readSamplesAsync(trackIndex, mExtractorSamples[trackIndex].size() - 1);
+ }
+ tester.waitForTracks();
+ for (int trackIndex = mTrackCount - 1; trackIndex >= 0; --trackIndex) {
+ tester.readSamplesAsync(trackIndex, SAMPLE_COUNT_ALL);
+ tester.waitForTrack(trackIndex);
+ }
+ compareSamples(tester.getSamples());
+ }
+}
+
+/** Reads all samples from all tracks sequentially. */
+TEST_F(MediaSampleReaderNDKTests, TestSequentialSampleAccess) {
+ LOG(DEBUG) << "TestSequentialSampleAccess Starts";
+
+ SampleAccessTester tester{mSourceFd, mFileSize};
+ tester.setEnforceSequentialAccess(true);
+ tester.readSamplesAsync(SAMPLE_COUNT_ALL);
+ tester.waitForTracks();
+ compareSamples(tester.getSamples());
+}
+
+/** Reads all samples from one track in parallel mode before switching to sequential mode. */
+TEST_F(MediaSampleReaderNDKTests, TestMixedSampleAccessTrackEOS) {
+ LOG(DEBUG) << "TestMixedSampleAccessTrackEOS Starts";
+
+ for (int readSampleInfoFlag = 0; readSampleInfoFlag <= 1; readSampleInfoFlag++) {
+ for (int trackIndToEOS = 0; trackIndToEOS < mTrackCount; ++trackIndToEOS) {
+ LOG(DEBUG) << "Testing EOS of track " << trackIndToEOS;
+
+ SampleAccessTester tester{mSourceFd, mFileSize};
+
+ // If the flag is set, read sample info from a different track before draining the track
+ // under test to force the reader to save the extractor position.
+ if (readSampleInfoFlag) {
+ tester.getSampleInfo((trackIndToEOS + 1) % mTrackCount);
+ }
+
+ // Read all samples from one track before enabling sequential access
+ tester.readSamplesAsync(trackIndToEOS, SAMPLE_COUNT_ALL);
+ tester.waitForTrack(trackIndToEOS);
+ tester.setEnforceSequentialAccess(true);
+
+ for (int trackIndex = 0; trackIndex < mTrackCount; ++trackIndex) {
+ if (trackIndex == trackIndToEOS) continue;
+
+ tester.readSamplesAsync(trackIndex, SAMPLE_COUNT_ALL);
+ tester.waitForTrack(trackIndex);
+ }
+
+ compareSamples(tester.getSamples());
+ }
+ }
+}
+
+/**
+ * Reads different combinations of sample counts from all tracks in parallel mode before switching
+ * to sequential mode and reading the rest of the samples.
+ */
+TEST_F(MediaSampleReaderNDKTests, TestMixedSampleAccess) {
+ LOG(DEBUG) << "TestMixedSampleAccess Starts";
+ initExtractorSamples();
+
+ for (int trackIndToTest = 0; trackIndToTest < mTrackCount; ++trackIndToTest) {
+ for (int sampleCount = 0; sampleCount <= (mExtractorSamples[trackIndToTest].size() + 1);
+ ++sampleCount) {
+ SampleAccessTester tester{mSourceFd, mFileSize};
+
+ for (int trackIndex = 0; trackIndex < mTrackCount; ++trackIndex) {
+ if (trackIndex == trackIndToTest) {
+ tester.readSamplesAsync(trackIndex, sampleCount);
+ } else {
+ tester.readSamplesAsync(trackIndex, mExtractorSamples[trackIndex].size() / 2);
+ }
+ }
+
+ tester.waitForTracks();
+ tester.setEnforceSequentialAccess(true);
+
+ tester.readSamplesAsync(SAMPLE_COUNT_ALL);
+ tester.waitForTracks();
+
+ compareSamples(tester.getSamples());
+ }
+ }
+}
+
+TEST_F(MediaSampleReaderNDKTests, TestEstimatedBitrateAccuracy) {
+ // Just put a somewhat reasonable upper bound on the estimated bitrate expected in our test
+ // assets. This is mostly to make sure the estimation is not way off.
+ static constexpr int32_t kMaxEstimatedBitrate = 100 * 1000 * 1000; // 100 Mbps
+
+ auto sampleReader = MediaSampleReaderNDK::createFromFd(mSourceFd, 0, mFileSize);
+ ASSERT_TRUE(sampleReader);
+
+ std::vector<int32_t> actualTrackBitrates = getTrackBitrates();
+ for (int trackIndex = 0; trackIndex < mTrackCount; ++trackIndex) {
+ EXPECT_EQ(sampleReader->selectTrack(trackIndex), AMEDIA_OK);
+
+ int32_t bitrate;
+ EXPECT_EQ(sampleReader->getEstimatedBitrateForTrack(trackIndex, &bitrate), AMEDIA_OK);
+ EXPECT_GT(bitrate, 0);
+ EXPECT_LT(bitrate, kMaxEstimatedBitrate);
+
+ // Note: The test asset currently used in this test is shorter than the sampling duration
+ // used to estimate the bitrate in the sample reader. So for now the estimation should be
+ // exact but if/when a longer asset is used a reasonable delta needs to be defined.
+ EXPECT_EQ(bitrate, actualTrackBitrates[trackIndex]);
+ }
+}
+
+TEST_F(MediaSampleReaderNDKTests, TestInvalidFd) {
+ std::shared_ptr<MediaSampleReader> sampleReader =
+ MediaSampleReaderNDK::createFromFd(0, 0, mFileSize);
+ ASSERT_TRUE(sampleReader == nullptr);
+
+ sampleReader = MediaSampleReaderNDK::createFromFd(-1, 0, mFileSize);
+ ASSERT_TRUE(sampleReader == nullptr);
+}
+
+TEST_F(MediaSampleReaderNDKTests, TestZeroSize) {
+ std::shared_ptr<MediaSampleReader> sampleReader =
+ MediaSampleReaderNDK::createFromFd(mSourceFd, 0, 0);
+ ASSERT_TRUE(sampleReader == nullptr);
+}
+
+TEST_F(MediaSampleReaderNDKTests, TestInvalidOffset) {
+ std::shared_ptr<MediaSampleReader> sampleReader =
+ MediaSampleReaderNDK::createFromFd(mSourceFd, mFileSize, mFileSize);
+ ASSERT_TRUE(sampleReader == nullptr);
+}
+
+} // namespace android
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp
new file mode 100644
index 0000000..0a8a06e
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp
@@ -0,0 +1,601 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Unit Test for MediaSampleWriter
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "MediaSampleWriterTests"
+
+#include <android-base/logging.h>
+#include <fcntl.h>
+#include <gtest/gtest.h>
+#include <media/MediaSampleQueue.h>
+#include <media/MediaSampleWriter.h>
+#include <media/NdkMediaExtractor.h>
+
+#include <condition_variable>
+#include <list>
+#include <mutex>
+
+namespace android {
+
+/** Muxer interface to enable MediaSampleWriter testing. */
+class TestMuxer : public MediaSampleWriterMuxerInterface {
+public:
+ // MuxerInterface
+ ssize_t addTrack(AMediaFormat* trackFormat) override {
+ mEventQueue.push_back(AddTrack(trackFormat));
+ return mTrackCount++;
+ }
+ media_status_t start() override {
+ mEventQueue.push_back(Start());
+ return AMEDIA_OK;
+ }
+
+ media_status_t writeSampleData(size_t trackIndex, const uint8_t* data,
+ const AMediaCodecBufferInfo* info) override {
+ mEventQueue.push_back(WriteSample(trackIndex, data, info));
+ return AMEDIA_OK;
+ }
+ media_status_t stop() override {
+ mEventQueue.push_back(Stop());
+ return AMEDIA_OK;
+ }
+ // ~MuxerInterface
+
+ struct Event {
+ enum { NoEvent, AddTrack, Start, WriteSample, Stop } type = NoEvent;
+ const AMediaFormat* format = nullptr;
+ size_t trackIndex = 0;
+ const uint8_t* data = nullptr;
+ AMediaCodecBufferInfo info{};
+ };
+
+ static constexpr Event NoEvent = {Event::NoEvent, nullptr, 0, nullptr, {}};
+
+ static Event AddTrack(const AMediaFormat* format) {
+ return {.type = Event::AddTrack, .format = format};
+ }
+
+ static Event Start() { return {.type = Event::Start}; }
+ static Event Stop() { return {.type = Event::Stop}; }
+
+ static Event WriteSample(size_t trackIndex, const uint8_t* data,
+ const AMediaCodecBufferInfo* info) {
+ return {.type = Event::WriteSample, .trackIndex = trackIndex, .data = data, .info = *info};
+ }
+
+ static Event WriteSampleWithPts(size_t trackIndex, int64_t pts) {
+ return {.type = Event::WriteSample, .trackIndex = trackIndex, .info = {0, 0, pts, 0}};
+ }
+
+ void pushEvent(const Event& e) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mEventQueue.push_back(e);
+ mCondition.notify_one();
+ }
+
+ const Event& popEvent(bool wait = false) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ while (wait && mEventQueue.empty()) {
+ mCondition.wait_for(lock, std::chrono::milliseconds(200));
+ }
+
+ if (mEventQueue.empty()) {
+ mPoppedEvent = NoEvent;
+ } else {
+ mPoppedEvent = *mEventQueue.begin();
+ mEventQueue.pop_front();
+ }
+ return mPoppedEvent;
+ }
+
+private:
+ Event mPoppedEvent;
+ std::list<Event> mEventQueue;
+ ssize_t mTrackCount = 0;
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+};
+
+bool operator==(const AMediaCodecBufferInfo& lhs, const AMediaCodecBufferInfo& rhs) {
+ return lhs.offset == rhs.offset && lhs.size == rhs.size &&
+ lhs.presentationTimeUs == rhs.presentationTimeUs && lhs.flags == rhs.flags;
+}
+
+bool operator==(const TestMuxer::Event& lhs, const TestMuxer::Event& rhs) {
+ // Don't test format pointer equality since the writer can make a copy.
+ return lhs.type == rhs.type /*&& lhs.format == rhs.format*/ &&
+ lhs.trackIndex == rhs.trackIndex && lhs.data == rhs.data && lhs.info == rhs.info;
+}
+
+/** Represents a media source file. */
+class TestMediaSource {
+public:
+ void init() {
+ static const char* sourcePath =
+ "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
+
+ mExtractor = AMediaExtractor_new();
+ ASSERT_NE(mExtractor, nullptr);
+
+ int sourceFd = open(sourcePath, O_RDONLY);
+ ASSERT_GT(sourceFd, 0);
+
+ off_t fileSize = lseek(sourceFd, 0, SEEK_END);
+ lseek(sourceFd, 0, SEEK_SET);
+
+ media_status_t status = AMediaExtractor_setDataSourceFd(mExtractor, sourceFd, 0, fileSize);
+ ASSERT_EQ(status, AMEDIA_OK);
+ close(sourceFd);
+
+ mTrackCount = AMediaExtractor_getTrackCount(mExtractor);
+ ASSERT_GT(mTrackCount, 1);
+ for (size_t trackIndex = 0; trackIndex < mTrackCount; trackIndex++) {
+ AMediaFormat* trackFormat = AMediaExtractor_getTrackFormat(mExtractor, trackIndex);
+ ASSERT_NE(trackFormat, nullptr);
+
+ const char* mime = nullptr;
+ AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+ if (strncmp(mime, "video/", 6) == 0) {
+ mVideoTrackIndex = trackIndex;
+ } else if (strncmp(mime, "audio/", 6) == 0) {
+ mAudioTrackIndex = trackIndex;
+ }
+
+ mTrackFormats.push_back(
+ std::shared_ptr<AMediaFormat>(trackFormat, &AMediaFormat_delete));
+
+ AMediaExtractor_selectTrack(mExtractor, trackIndex);
+ }
+ EXPECT_GE(mVideoTrackIndex, 0);
+ EXPECT_GE(mAudioTrackIndex, 0);
+ }
+
+ void reset() const {
+ media_status_t status = AMediaExtractor_seekTo(mExtractor, 0 /* seekPosUs */,
+ AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC);
+ ASSERT_EQ(status, AMEDIA_OK);
+ }
+
+ AMediaExtractor* mExtractor = nullptr;
+ size_t mTrackCount = 0;
+ std::vector<std::shared_ptr<AMediaFormat>> mTrackFormats;
+ int mVideoTrackIndex = -1;
+ int mAudioTrackIndex = -1;
+};
+
+class TestCallbacks : public MediaSampleWriter::CallbackInterface {
+public:
+ bool hasFinished() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ return mFinished;
+ }
+
+ // MediaSampleWriter::CallbackInterface
+ virtual void onFinished(const MediaSampleWriter* writer __unused,
+ media_status_t status) override {
+ std::unique_lock<std::mutex> lock(mMutex);
+ EXPECT_FALSE(mFinished);
+ mFinished = true;
+ mStatus = status;
+ mCondition.notify_all();
+ }
+
+ virtual void onStopped(const MediaSampleWriter* writer __unused) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ EXPECT_FALSE(mFinished);
+ mStopped = true;
+ mCondition.notify_all();
+ }
+
+ virtual void onProgressUpdate(const MediaSampleWriter* writer __unused,
+ int32_t progress) override {
+ EXPECT_GT(progress, mLastProgress);
+ EXPECT_GE(progress, 0);
+ EXPECT_LE(progress, 100);
+
+ mLastProgress = progress;
+ mProgressUpdateCount++;
+ }
+
+ virtual void onHeartBeat(const MediaSampleWriter* writer __unused) override {}
+ // ~MediaSampleWriter::CallbackInterface
+
+ void waitForWritingFinished() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ while (!mFinished && !mStopped) {
+ mCondition.wait(lock);
+ }
+ }
+
+ uint32_t getProgressUpdateCount() const { return mProgressUpdateCount; }
+ bool wasStopped() const { return mStopped; }
+
+private:
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ bool mFinished = false;
+ bool mStopped = false;
+ media_status_t mStatus = AMEDIA_OK;
+ int32_t mLastProgress = -1;
+ uint32_t mProgressUpdateCount = 0;
+};
+
+class MediaSampleWriterTests : public ::testing::Test {
+public:
+ MediaSampleWriterTests() { LOG(DEBUG) << "MediaSampleWriterTests created"; }
+ ~MediaSampleWriterTests() { LOG(DEBUG) << "MediaSampleWriterTests destroyed"; }
+
+ static const TestMediaSource& getMediaSource() {
+ static TestMediaSource sMediaSource;
+ static std::once_flag sOnceToken;
+
+ std::call_once(sOnceToken, [] { sMediaSource.init(); });
+
+ sMediaSource.reset();
+ return sMediaSource;
+ }
+
+ static std::shared_ptr<MediaSample> newSample(int64_t ptsUs, uint32_t flags, size_t size,
+ size_t offset, const uint8_t* buffer) {
+ auto sample = std::make_shared<MediaSample>();
+ sample->info.presentationTimeUs = ptsUs;
+ sample->info.flags = flags;
+ sample->info.size = size;
+ sample->dataOffset = offset;
+ sample->buffer = buffer;
+ return sample;
+ }
+
+ static std::shared_ptr<MediaSample> newSampleEos() {
+ return newSample(0, SAMPLE_FLAG_END_OF_STREAM, 0, 0, nullptr);
+ }
+
+ static std::shared_ptr<MediaSample> newSampleWithPts(int64_t ptsUs) {
+ static uint32_t sampleCount = 0;
+
+ // Use sampleCount to get a unique mock sample.
+ uint32_t sampleId = ++sampleCount;
+ return newSample(ptsUs, 0, sampleId, sampleId, reinterpret_cast<const uint8_t*>(sampleId));
+ }
+
+ static std::shared_ptr<MediaSample> newSampleWithPtsOnly(int64_t ptsUs) {
+ return newSample(ptsUs, 0, 0, 0, nullptr);
+ }
+
+ void SetUp() override {
+ LOG(DEBUG) << "MediaSampleWriterTests set up";
+ mTestMuxer = std::make_shared<TestMuxer>();
+ }
+
+ void TearDown() override {
+ LOG(DEBUG) << "MediaSampleWriterTests tear down";
+ mTestMuxer.reset();
+ }
+
+protected:
+ std::shared_ptr<TestMuxer> mTestMuxer;
+ std::shared_ptr<TestCallbacks> mTestCallbacks = std::make_shared<TestCallbacks>();
+};
+
+TEST_F(MediaSampleWriterTests, TestAddTrackWithoutInit) {
+ const TestMediaSource& mediaSource = getMediaSource();
+
+ std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
+ EXPECT_EQ(writer->addTrack(mediaSource.mTrackFormats[0]), nullptr);
+}
+
+TEST_F(MediaSampleWriterTests, TestStartWithoutInit) {
+ std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
+ EXPECT_FALSE(writer->start());
+}
+
+TEST_F(MediaSampleWriterTests, TestStartWithoutTracks) {
+ std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
+ EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
+ EXPECT_FALSE(writer->start());
+ EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
+}
+
+TEST_F(MediaSampleWriterTests, TestAddInvalidTrack) {
+ std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
+ EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
+
+ EXPECT_EQ(writer->addTrack(nullptr), nullptr);
+ EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
+}
+
+TEST_F(MediaSampleWriterTests, TestDoubleStartStop) {
+ std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
+
+ std::shared_ptr<TestCallbacks> callbacks = std::make_shared<TestCallbacks>();
+ EXPECT_TRUE(writer->init(mTestMuxer, callbacks));
+
+ const TestMediaSource& mediaSource = getMediaSource();
+ EXPECT_NE(writer->addTrack(mediaSource.mTrackFormats[0]), nullptr);
+ EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(mediaSource.mTrackFormats[0].get()));
+
+ ASSERT_TRUE(writer->start());
+ EXPECT_FALSE(writer->start());
+
+ writer->stop();
+ writer->stop();
+ callbacks->waitForWritingFinished();
+ EXPECT_TRUE(callbacks->wasStopped());
+}
+
+TEST_F(MediaSampleWriterTests, TestStopWithoutStart) {
+ std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
+ EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
+
+ const TestMediaSource& mediaSource = getMediaSource();
+ EXPECT_NE(writer->addTrack(mediaSource.mTrackFormats[0]), nullptr);
+ EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(mediaSource.mTrackFormats[0].get()));
+
+ writer->stop();
+ EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
+}
+
+TEST_F(MediaSampleWriterTests, TestStartWithoutCallback) {
+ std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
+
+ std::weak_ptr<MediaSampleWriter::CallbackInterface> unassignedWp;
+ EXPECT_FALSE(writer->init(mTestMuxer, unassignedWp));
+
+ std::shared_ptr<MediaSampleWriter::CallbackInterface> unassignedSp;
+ EXPECT_FALSE(writer->init(mTestMuxer, unassignedSp));
+
+ const TestMediaSource& mediaSource = getMediaSource();
+ EXPECT_EQ(writer->addTrack(mediaSource.mTrackFormats[0]), nullptr);
+ ASSERT_FALSE(writer->start());
+}
+
+TEST_F(MediaSampleWriterTests, TestProgressUpdate) {
+ const TestMediaSource& mediaSource = getMediaSource();
+
+ std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
+ EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
+
+ std::shared_ptr<AMediaFormat> videoFormat =
+ std::shared_ptr<AMediaFormat>(AMediaFormat_new(), &AMediaFormat_delete);
+ AMediaFormat_copy(videoFormat.get(),
+ mediaSource.mTrackFormats[mediaSource.mVideoTrackIndex].get());
+
+ AMediaFormat_setInt64(videoFormat.get(), AMEDIAFORMAT_KEY_DURATION, 100);
+ auto sampleConsumer = writer->addTrack(videoFormat);
+ EXPECT_NE(sampleConsumer, nullptr);
+ ASSERT_TRUE(writer->start());
+
+ for (int64_t pts = 0; pts < 100; ++pts) {
+ sampleConsumer(newSampleWithPts(pts));
+ }
+ sampleConsumer(newSampleEos());
+ mTestCallbacks->waitForWritingFinished();
+
+ EXPECT_EQ(mTestCallbacks->getProgressUpdateCount(), 100);
+}
+
+TEST_F(MediaSampleWriterTests, TestInterleaving) {
+ std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
+ EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
+
+ // Use two tracks for this test.
+ static constexpr int kNumTracks = 2;
+ MediaSampleWriter::MediaSampleConsumerFunction sampleConsumers[kNumTracks];
+ std::vector<std::pair<std::shared_ptr<MediaSample>, size_t>> addedSamples;
+ const TestMediaSource& mediaSource = getMediaSource();
+
+ for (int trackIdx = 0; trackIdx < kNumTracks; ++trackIdx) {
+ auto trackFormat = mediaSource.mTrackFormats[trackIdx % mediaSource.mTrackCount];
+ sampleConsumers[trackIdx] = writer->addTrack(trackFormat);
+ EXPECT_NE(sampleConsumers[trackIdx], nullptr);
+ EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(trackFormat.get()));
+ }
+
+ // Create samples in the expected interleaved order for easy verification.
+ auto addSampleToTrackWithPts = [&addedSamples, &sampleConsumers](int trackIndex, int64_t pts) {
+ auto sample = newSampleWithPts(pts);
+ sampleConsumers[trackIndex](sample);
+ addedSamples.emplace_back(sample, trackIndex);
+ };
+
+ addSampleToTrackWithPts(0, 0);
+ addSampleToTrackWithPts(1, 4);
+
+ addSampleToTrackWithPts(0, 1);
+ addSampleToTrackWithPts(0, 2);
+ addSampleToTrackWithPts(0, 3);
+ addSampleToTrackWithPts(0, 10);
+
+ addSampleToTrackWithPts(1, 5);
+ addSampleToTrackWithPts(1, 6);
+ addSampleToTrackWithPts(1, 11);
+
+ addSampleToTrackWithPts(0, 12);
+ addSampleToTrackWithPts(1, 13);
+
+ for (int trackIndex = 0; trackIndex < kNumTracks; ++trackIndex) {
+ sampleConsumers[trackIndex](newSampleEos());
+ }
+
+ // Start the writer.
+ ASSERT_TRUE(writer->start());
+
+ // Wait for writer to complete.
+ mTestCallbacks->waitForWritingFinished();
+ EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Start());
+
+ std::sort(addedSamples.begin(), addedSamples.end(),
+ [](const std::pair<std::shared_ptr<MediaSample>, size_t>& left,
+ const std::pair<std::shared_ptr<MediaSample>, size_t>& right) {
+ return left.first->info.presentationTimeUs < right.first->info.presentationTimeUs;
+ });
+
+ // Verify sample order.
+ for (auto entry : addedSamples) {
+ auto sample = entry.first;
+ auto trackIndex = entry.second;
+
+ const TestMuxer::Event& event = mTestMuxer->popEvent();
+ EXPECT_EQ(event.type, TestMuxer::Event::WriteSample);
+ EXPECT_EQ(event.trackIndex, trackIndex);
+ EXPECT_EQ(event.data, sample->buffer);
+ EXPECT_EQ(event.info.offset, sample->dataOffset);
+ EXPECT_EQ(event.info.size, sample->info.size);
+ EXPECT_EQ(event.info.presentationTimeUs, sample->info.presentationTimeUs);
+ EXPECT_EQ(event.info.flags, sample->info.flags);
+ }
+
+ // Verify EOS samples.
+ for (int trackIndex = 0; trackIndex < kNumTracks; ++trackIndex) {
+ auto trackFormat = mediaSource.mTrackFormats[trackIndex % mediaSource.mTrackCount];
+ int64_t duration = 0;
+ AMediaFormat_getInt64(trackFormat.get(), AMEDIAFORMAT_KEY_DURATION, &duration);
+
+ // EOS timestamp = first sample timestamp + duration.
+ const int64_t endTime = duration + (trackIndex == 1 ? 4 : 0);
+ const AMediaCodecBufferInfo info = {0, 0, endTime, AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM};
+
+ EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::WriteSample(trackIndex, nullptr, &info));
+ }
+
+ EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Stop());
+ EXPECT_TRUE(mTestCallbacks->hasFinished());
+}
+
+// Convenience function for reading a sample from an AMediaExtractor represented as a MediaSample.
+static std::shared_ptr<MediaSample> readSampleAndAdvance(AMediaExtractor* extractor,
+ size_t* trackIndexOut) {
+ int trackIndex = AMediaExtractor_getSampleTrackIndex(extractor);
+ if (trackIndex < 0) {
+ return nullptr;
+ }
+
+ if (trackIndexOut != nullptr) {
+ *trackIndexOut = trackIndex;
+ }
+
+ ssize_t sampleSize = AMediaExtractor_getSampleSize(extractor);
+ int64_t sampleTimeUs = AMediaExtractor_getSampleTime(extractor);
+ uint32_t flags = AMediaExtractor_getSampleFlags(extractor);
+
+ size_t bufferSize = static_cast<size_t>(sampleSize);
+ uint8_t* buffer = new uint8_t[bufferSize];
+
+ ssize_t dataRead = AMediaExtractor_readSampleData(extractor, buffer, bufferSize);
+ EXPECT_EQ(dataRead, sampleSize);
+
+ auto sample = MediaSample::createWithReleaseCallback(
+ buffer, 0 /* offset */, 0 /* id */, [buffer](MediaSample*) { delete[] buffer; });
+ sample->info.size = bufferSize;
+ sample->info.presentationTimeUs = sampleTimeUs;
+ sample->info.flags = flags;
+
+ (void)AMediaExtractor_advance(extractor);
+ return sample;
+}
+
+TEST_F(MediaSampleWriterTests, TestDefaultMuxer) {
+ // Write samples straight from an extractor and validate output file.
+ static const char* destinationPath =
+ "/data/local/tmp/MediaSampleWriterTests_TestDefaultMuxer_output.MP4";
+ const int destinationFd =
+ open(destinationPath, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IROTH);
+ ASSERT_GT(destinationFd, 0);
+
+ // Initialize writer.
+ std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
+ EXPECT_TRUE(writer->init(destinationFd, mTestCallbacks));
+ close(destinationFd);
+
+ // Add tracks.
+ const TestMediaSource& mediaSource = getMediaSource();
+ std::vector<MediaSampleWriter::MediaSampleConsumerFunction> sampleConsumers;
+
+ for (size_t trackIndex = 0; trackIndex < mediaSource.mTrackCount; trackIndex++) {
+ auto consumer = writer->addTrack(mediaSource.mTrackFormats[trackIndex]);
+ sampleConsumers.push_back(consumer);
+ }
+
+ // Start the writer.
+ ASSERT_TRUE(writer->start());
+
+ // Enqueue samples and finally End Of Stream.
+ std::shared_ptr<MediaSample> sample;
+ size_t trackIndex;
+ while ((sample = readSampleAndAdvance(mediaSource.mExtractor, &trackIndex)) != nullptr) {
+ sampleConsumers[trackIndex](sample);
+ }
+ for (trackIndex = 0; trackIndex < mediaSource.mTrackCount; trackIndex++) {
+ sampleConsumers[trackIndex](newSampleEos());
+ }
+
+ // Wait for writer.
+ mTestCallbacks->waitForWritingFinished();
+
+ // Compare output file with source.
+ mediaSource.reset();
+
+ AMediaExtractor* extractor = AMediaExtractor_new();
+ ASSERT_NE(extractor, nullptr);
+
+ int sourceFd = open(destinationPath, O_RDONLY);
+ ASSERT_GT(sourceFd, 0);
+
+ off_t fileSize = lseek(sourceFd, 0, SEEK_END);
+ lseek(sourceFd, 0, SEEK_SET);
+
+ media_status_t status = AMediaExtractor_setDataSourceFd(extractor, sourceFd, 0, fileSize);
+ ASSERT_EQ(status, AMEDIA_OK);
+ close(sourceFd);
+
+ size_t trackCount = AMediaExtractor_getTrackCount(extractor);
+ EXPECT_EQ(trackCount, mediaSource.mTrackCount);
+
+ for (size_t trackIndex = 0; trackIndex < trackCount; trackIndex++) {
+ AMediaFormat* trackFormat = AMediaExtractor_getTrackFormat(extractor, trackIndex);
+ ASSERT_NE(trackFormat, nullptr);
+
+ AMediaExtractor_selectTrack(extractor, trackIndex);
+ }
+
+ // Compare samples.
+ std::shared_ptr<MediaSample> sample1 = readSampleAndAdvance(mediaSource.mExtractor, nullptr);
+ std::shared_ptr<MediaSample> sample2 = readSampleAndAdvance(extractor, nullptr);
+
+ while (sample1 != nullptr && sample2 != nullptr) {
+ EXPECT_EQ(sample1->info.presentationTimeUs, sample2->info.presentationTimeUs);
+ EXPECT_EQ(sample1->info.size, sample2->info.size);
+ EXPECT_EQ(sample1->info.flags, sample2->info.flags);
+
+ EXPECT_EQ(memcmp(sample1->buffer, sample2->buffer, sample1->info.size), 0);
+
+ sample1 = readSampleAndAdvance(mediaSource.mExtractor, nullptr);
+ sample2 = readSampleAndAdvance(extractor, nullptr);
+ }
+ EXPECT_EQ(sample1, nullptr);
+ EXPECT_EQ(sample2, nullptr);
+
+ AMediaExtractor_delete(extractor);
+}
+
+} // namespace android
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/media/libmediatranscoding/transcoder/tests/MediaTrackTranscoderTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaTrackTranscoderTests.cpp
new file mode 100644
index 0000000..791e983
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/tests/MediaTrackTranscoderTests.cpp
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Unit Test for MediaTrackTranscoder
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "MediaTrackTranscoderTests"
+
+#include <android-base/logging.h>
+#include <android/binder_process.h>
+#include <fcntl.h>
+#include <gtest/gtest.h>
+#include <media/MediaSampleReaderNDK.h>
+#include <media/MediaTrackTranscoder.h>
+#include <media/PassthroughTrackTranscoder.h>
+#include <media/VideoTrackTranscoder.h>
+
+#include "TranscoderTestUtils.h"
+
+namespace android {
+
+/** TrackTranscoder types to test. */
+enum TrackTranscoderType {
+ VIDEO,
+ PASSTHROUGH,
+};
+
+class MediaTrackTranscoderTests : public ::testing::TestWithParam<TrackTranscoderType> {
+public:
+ MediaTrackTranscoderTests() { LOG(DEBUG) << "MediaTrackTranscoderTests created"; }
+
+ void SetUp() override {
+ LOG(DEBUG) << "MediaTrackTranscoderTests set up";
+
+ // Need to start a thread pool to prevent AMediaExtractor binder calls from starving
+ // (b/155663561).
+ ABinderProcess_startThreadPool();
+
+ mCallback = std::make_shared<TestTrackTranscoderCallback>();
+
+ switch (GetParam()) {
+ case VIDEO:
+ mTranscoder = VideoTrackTranscoder::create(mCallback);
+ break;
+ case PASSTHROUGH:
+ mTranscoder = std::make_shared<PassthroughTrackTranscoder>(mCallback);
+ break;
+ }
+ ASSERT_NE(mTranscoder, nullptr);
+
+ initSampleReader("/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4");
+ }
+
+ void initSampleReader(const char* sourcePath) {
+ const int sourceFd = open(sourcePath, O_RDONLY);
+ ASSERT_GT(sourceFd, 0);
+
+ const size_t fileSize = lseek(sourceFd, 0, SEEK_END);
+ lseek(sourceFd, 0, SEEK_SET);
+
+ mMediaSampleReader = MediaSampleReaderNDK::createFromFd(sourceFd, 0 /* offset */, fileSize);
+ ASSERT_NE(mMediaSampleReader, nullptr);
+ close(sourceFd);
+
+ for (size_t trackIndex = 0; trackIndex < mMediaSampleReader->getTrackCount();
+ ++trackIndex) {
+ AMediaFormat* trackFormat = mMediaSampleReader->getTrackFormat(trackIndex);
+ ASSERT_NE(trackFormat, nullptr);
+
+ const char* mime = nullptr;
+ AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+ ASSERT_NE(mime, nullptr);
+
+ if (GetParam() == VIDEO && strncmp(mime, "video/", 6) == 0) {
+ mTrackIndex = trackIndex;
+
+ mSourceFormat = std::shared_ptr<AMediaFormat>(trackFormat, &AMediaFormat_delete);
+ ASSERT_NE(mSourceFormat, nullptr);
+
+ mDestinationFormat =
+ TrackTranscoderTestUtils::getDefaultVideoDestinationFormat(trackFormat);
+ ASSERT_NE(mDestinationFormat, nullptr);
+ break;
+ } else if (GetParam() == PASSTHROUGH && strncmp(mime, "audio/", 6) == 0) {
+ // TODO(lnilsson): Test metadata track passthrough after hkuang@ provides sample.
+ mTrackIndex = trackIndex;
+
+ mSourceFormat = std::shared_ptr<AMediaFormat>(trackFormat, &AMediaFormat_delete);
+ ASSERT_NE(mSourceFormat, nullptr);
+ break;
+ }
+
+ AMediaFormat_delete(trackFormat);
+ }
+
+ ASSERT_NE(mSourceFormat, nullptr);
+ EXPECT_EQ(mMediaSampleReader->selectTrack(mTrackIndex), AMEDIA_OK);
+ }
+
+ // Drains the transcoder's output queue in a loop.
+ void drainOutputSamples(int numSamplesToSave = 0) {
+ mTranscoder->setSampleConsumer(
+ [this, numSamplesToSave](const std::shared_ptr<MediaSample>& sample) {
+ ASSERT_NE(sample, nullptr);
+
+ mGotEndOfStream = (sample->info.flags & SAMPLE_FLAG_END_OF_STREAM) != 0;
+
+ if (mSavedSamples.size() < numSamplesToSave) {
+ mSavedSamples.push_back(sample);
+ }
+
+ if (mSavedSamples.size() == numSamplesToSave || mGotEndOfStream) {
+ mSamplesSavedSemaphore.signal();
+ }
+ });
+ }
+
+ void TearDown() override { LOG(DEBUG) << "MediaTrackTranscoderTests tear down"; }
+
+ ~MediaTrackTranscoderTests() { LOG(DEBUG) << "MediaTrackTranscoderTests destroyed"; }
+
+protected:
+ std::shared_ptr<MediaTrackTranscoder> mTranscoder;
+ std::shared_ptr<TestTrackTranscoderCallback> mCallback;
+
+ std::shared_ptr<MediaSampleReader> mMediaSampleReader;
+ int mTrackIndex;
+
+ std::shared_ptr<AMediaFormat> mSourceFormat;
+ std::shared_ptr<AMediaFormat> mDestinationFormat;
+
+ std::vector<std::shared_ptr<MediaSample>> mSavedSamples;
+ OneShotSemaphore mSamplesSavedSemaphore;
+ bool mGotEndOfStream = false;
+};
+
+TEST_P(MediaTrackTranscoderTests, WaitNormalOperation) {
+ LOG(DEBUG) << "Testing WaitNormalOperation";
+ EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
+ AMEDIA_OK);
+ ASSERT_TRUE(mTranscoder->start());
+ drainOutputSamples();
+ EXPECT_EQ(mCallback->waitUntilFinished(), AMEDIA_OK);
+ EXPECT_TRUE(mCallback->transcodingFinished());
+ EXPECT_TRUE(mGotEndOfStream);
+}
+
+TEST_P(MediaTrackTranscoderTests, StopNormalOperation) {
+ LOG(DEBUG) << "Testing StopNormalOperation";
+
+ // Use a longer test asset to make sure that transcoding can be stopped.
+ initSampleReader("/data/local/tmp/TranscodingTestAssets/longtest_15s.mp4");
+
+ EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
+ AMEDIA_OK);
+ EXPECT_TRUE(mTranscoder->start());
+ mCallback->waitUntilTrackFormatAvailable();
+ mTranscoder->stop();
+ EXPECT_EQ(mCallback->waitUntilFinished(), AMEDIA_OK);
+ EXPECT_TRUE(mCallback->transcodingWasStopped());
+}
+
+TEST_P(MediaTrackTranscoderTests, StartWithoutConfigure) {
+ LOG(DEBUG) << "Testing StartWithoutConfigure";
+ EXPECT_FALSE(mTranscoder->start());
+}
+
+TEST_P(MediaTrackTranscoderTests, StopWithoutStart) {
+ LOG(DEBUG) << "Testing StopWithoutStart";
+ EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
+ AMEDIA_OK);
+ mTranscoder->stop();
+}
+
+TEST_P(MediaTrackTranscoderTests, DoubleStartStop) {
+ LOG(DEBUG) << "Testing DoubleStartStop";
+
+ // Use a longer test asset to make sure that transcoding can be stopped.
+ initSampleReader("/data/local/tmp/TranscodingTestAssets/longtest_15s.mp4");
+
+ EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
+ AMEDIA_OK);
+ EXPECT_TRUE(mTranscoder->start());
+ EXPECT_FALSE(mTranscoder->start());
+ mTranscoder->stop();
+ mTranscoder->stop();
+ EXPECT_EQ(mCallback->waitUntilFinished(), AMEDIA_OK);
+ EXPECT_TRUE(mCallback->transcodingWasStopped());
+}
+
+TEST_P(MediaTrackTranscoderTests, DoubleConfigure) {
+ LOG(DEBUG) << "Testing DoubleConfigure";
+ EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
+ AMEDIA_OK);
+ EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
+ AMEDIA_ERROR_UNSUPPORTED);
+}
+
+TEST_P(MediaTrackTranscoderTests, ConfigureAfterFail) {
+ LOG(DEBUG) << "Testing ConfigureAfterFail";
+ EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, -1, mDestinationFormat),
+ AMEDIA_ERROR_INVALID_PARAMETER);
+ EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
+ AMEDIA_OK);
+}
+
+TEST_P(MediaTrackTranscoderTests, RestartAfterStop) {
+ LOG(DEBUG) << "Testing RestartAfterStop";
+ EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
+ AMEDIA_OK);
+ EXPECT_TRUE(mTranscoder->start());
+ mTranscoder->stop();
+ EXPECT_EQ(mCallback->waitUntilFinished(), AMEDIA_OK);
+ EXPECT_FALSE(mTranscoder->start());
+}
+
+TEST_P(MediaTrackTranscoderTests, RestartAfterFinish) {
+ LOG(DEBUG) << "Testing RestartAfterFinish";
+ EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
+ AMEDIA_OK);
+ ASSERT_TRUE(mTranscoder->start());
+ drainOutputSamples();
+ EXPECT_EQ(mCallback->waitUntilFinished(), AMEDIA_OK);
+ mTranscoder->stop();
+ EXPECT_FALSE(mTranscoder->start());
+ EXPECT_TRUE(mGotEndOfStream);
+}
+
+TEST_P(MediaTrackTranscoderTests, HoldSampleAfterTranscoderRelease) {
+ LOG(DEBUG) << "Testing HoldSampleAfterTranscoderRelease";
+ EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
+ AMEDIA_OK);
+ ASSERT_TRUE(mTranscoder->start());
+ drainOutputSamples(1 /* numSamplesToSave */);
+ EXPECT_EQ(mCallback->waitUntilFinished(), AMEDIA_OK);
+ mTranscoder->stop();
+ EXPECT_TRUE(mGotEndOfStream);
+
+ mTranscoder.reset();
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(20));
+ mSavedSamples.clear();
+}
+
+TEST_P(MediaTrackTranscoderTests, HoldSampleAfterTranscoderStop) {
+ LOG(DEBUG) << "Testing HoldSampleAfterTranscoderStop";
+ EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
+ AMEDIA_OK);
+ ASSERT_TRUE(mTranscoder->start());
+ drainOutputSamples(1 /* numSamplesToSave */);
+ mSamplesSavedSemaphore.wait();
+ mTranscoder->stop();
+ EXPECT_EQ(mCallback->waitUntilFinished(), AMEDIA_OK);
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(20));
+ mSavedSamples.clear();
+}
+
+TEST_P(MediaTrackTranscoderTests, NullSampleReader) {
+ LOG(DEBUG) << "Testing NullSampleReader";
+ std::shared_ptr<MediaSampleReader> nullSampleReader;
+ EXPECT_NE(mTranscoder->configure(nullSampleReader, mTrackIndex, mDestinationFormat), AMEDIA_OK);
+ ASSERT_FALSE(mTranscoder->start());
+}
+
+TEST_P(MediaTrackTranscoderTests, InvalidTrackIndex) {
+ LOG(DEBUG) << "Testing InvalidTrackIndex";
+ EXPECT_NE(mTranscoder->configure(mMediaSampleReader, -1, mDestinationFormat), AMEDIA_OK);
+ EXPECT_NE(mTranscoder->configure(mMediaSampleReader, mMediaSampleReader->getTrackCount(),
+ mDestinationFormat),
+ AMEDIA_OK);
+}
+
+TEST_P(MediaTrackTranscoderTests, StopOnSync) {
+ LOG(DEBUG) << "Testing StopOnSync";
+
+ // Use a longer test asset to make sure there is a GOP to finish.
+ initSampleReader("/data/local/tmp/TranscodingTestAssets/longtest_15s.mp4");
+
+ EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
+ AMEDIA_OK);
+
+ bool lastSampleWasEos = false;
+ bool lastRealSampleWasSync = false;
+ OneShotSemaphore samplesReceivedSemaphore;
+ uint32_t sampleCount = 0;
+
+ mTranscoder->setSampleConsumer([&](const std::shared_ptr<MediaSample>& sample) {
+ ASSERT_NE(sample, nullptr);
+
+ if ((lastSampleWasEos = sample->info.flags & SAMPLE_FLAG_END_OF_STREAM)) {
+ samplesReceivedSemaphore.signal();
+ return;
+ }
+ lastRealSampleWasSync = sample->info.flags & SAMPLE_FLAG_SYNC_SAMPLE;
+
+ if (++sampleCount >= 10) { // Wait for a few samples before stopping.
+ samplesReceivedSemaphore.signal();
+ }
+ });
+
+ ASSERT_TRUE(mTranscoder->start());
+ samplesReceivedSemaphore.wait();
+ mTranscoder->stop(true /* stopOnSync */);
+ EXPECT_EQ(mCallback->waitUntilFinished(), AMEDIA_OK);
+
+ EXPECT_TRUE(lastSampleWasEos);
+ EXPECT_TRUE(lastRealSampleWasSync);
+ EXPECT_TRUE(mCallback->transcodingWasStopped());
+}
+
+}; // namespace android
+
+using namespace android;
+
+INSTANTIATE_TEST_SUITE_P(MediaTrackTranscoderTestsAll, MediaTrackTranscoderTests,
+ ::testing::Values(VIDEO, PASSTHROUGH));
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp
new file mode 100644
index 0000000..b7c7bd8
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Unit Test for MediaTranscoder
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "MediaTranscoderTests"
+
+#include <android-base/logging.h>
+#include <android/binder_process.h>
+#include <fcntl.h>
+#include <gtest/gtest.h>
+#include <media/MediaSampleReaderNDK.h>
+#include <media/MediaTranscoder.h>
+#include <media/NdkCommon.h>
+
+#include "TranscoderTestUtils.h"
+
+namespace android {
+
+#define DEFINE_FORMAT_VALUE_EQUAL_FUNC(_type, _typeName) \
+ static bool equal##_typeName(const char* key, AMediaFormat* src, AMediaFormat* dst) { \
+ _type srcVal, dstVal; \
+ bool srcPresent = AMediaFormat_get##_typeName(src, key, &srcVal); \
+ bool dstPresent = AMediaFormat_get##_typeName(dst, key, &dstVal); \
+ return (srcPresent == dstPresent) && (!srcPresent || (srcVal == dstVal)); \
+ }
+
+DEFINE_FORMAT_VALUE_EQUAL_FUNC(int64_t, Int64);
+DEFINE_FORMAT_VALUE_EQUAL_FUNC(int32_t, Int32);
+
+struct FormatVerifierEntry {
+ const char* key;
+ std::function<bool(const char*, AMediaFormat*, AMediaFormat*)> equal;
+};
+
+static const FormatVerifierEntry kFieldsToPreserve[] = {
+ {AMEDIAFORMAT_KEY_DURATION, equalInt64}, {AMEDIAFORMAT_KEY_WIDTH, equalInt32},
+ {AMEDIAFORMAT_KEY_HEIGHT, equalInt32}, {AMEDIAFORMAT_KEY_FRAME_RATE, equalInt32},
+ {AMEDIAFORMAT_KEY_FRAME_COUNT, equalInt32}, {AMEDIAFORMAT_KEY_DISPLAY_WIDTH, equalInt32},
+ {AMEDIAFORMAT_KEY_DISPLAY_HEIGHT, equalInt32}, {AMEDIAFORMAT_KEY_SAR_WIDTH, equalInt32},
+ {AMEDIAFORMAT_KEY_SAR_HEIGHT, equalInt32}, {AMEDIAFORMAT_KEY_ROTATION, equalInt32},
+};
+
+// Write-only, create file if non-existent, don't overwrite existing file.
+static constexpr int kOpenFlags = O_WRONLY | O_CREAT | O_EXCL;
+// User R+W permission.
+static constexpr int kFileMode = S_IRUSR | S_IWUSR;
+
+class MediaTranscoderTests : public ::testing::Test {
+public:
+ MediaTranscoderTests() { LOG(DEBUG) << "MediaTranscoderTests created"; }
+ ~MediaTranscoderTests() { LOG(DEBUG) << "MediaTranscoderTests destroyed"; }
+
+ void SetUp() override {
+ LOG(DEBUG) << "MediaTranscoderTests set up";
+ mCallbacks = std::make_shared<TestTranscoderCallbacks>();
+ ABinderProcess_startThreadPool();
+ }
+
+ void TearDown() override {
+ LOG(DEBUG) << "MediaTranscoderTests tear down";
+ mCallbacks.reset();
+ }
+
+ void deleteFile(const char* path) { unlink(path); }
+
+ float getFileSizeDiffPercent(const char* path1, const char* path2, bool absolute = false) {
+ struct stat s1, s2;
+ EXPECT_EQ(stat(path1, &s1), 0);
+ EXPECT_EQ(stat(path2, &s2), 0);
+
+ int64_t diff = s2.st_size - s1.st_size;
+ if (absolute && diff < 0) diff = -diff;
+
+ return (float)diff * 100.0f / s1.st_size;
+ }
+
+ typedef enum {
+ kRunToCompletion,
+ kCheckHeartBeat,
+ kCancelAfterProgress,
+ kCancelAfterStart,
+ kPauseAfterProgress,
+ kPauseAfterStart,
+ } TranscodeExecutionControl;
+
+ using FormatConfigurationCallback = std::function<AMediaFormat*(AMediaFormat*)>;
+ media_status_t transcodeHelper(const char* srcPath, const char* destPath,
+ FormatConfigurationCallback formatCallback,
+ TranscodeExecutionControl executionControl = kRunToCompletion,
+ int64_t heartBeatIntervalUs = -1) {
+ auto transcoder = MediaTranscoder::create(mCallbacks, heartBeatIntervalUs);
+ EXPECT_NE(transcoder, nullptr);
+
+ const int srcFd = open(srcPath, O_RDONLY);
+ EXPECT_EQ(transcoder->configureSource(srcFd), AMEDIA_OK);
+
+ std::vector<std::shared_ptr<AMediaFormat>> trackFormats = transcoder->getTrackFormats();
+ EXPECT_GT(trackFormats.size(), 0);
+
+ for (int i = 0; i < trackFormats.size(); ++i) {
+ AMediaFormat* format = formatCallback(trackFormats[i].get());
+ EXPECT_EQ(transcoder->configureTrackFormat(i, format), AMEDIA_OK);
+
+ // Save original video track format for verification.
+ const char* mime = nullptr;
+ AMediaFormat_getString(trackFormats[i].get(), AMEDIAFORMAT_KEY_MIME, &mime);
+ if (strncmp(mime, "video/", 6) == 0) {
+ mSourceVideoFormat = trackFormats[i];
+ }
+
+ if (format != nullptr) {
+ AMediaFormat_delete(format);
+ }
+ }
+ deleteFile(destPath);
+ const int dstFd = open(destPath, kOpenFlags, kFileMode);
+ EXPECT_EQ(transcoder->configureDestination(dstFd), AMEDIA_OK);
+
+ media_status_t startStatus = transcoder->start();
+ EXPECT_EQ(startStatus, AMEDIA_OK);
+
+ if (startStatus == AMEDIA_OK) {
+ std::shared_ptr<ndk::ScopedAParcel> pausedState;
+
+ switch (executionControl) {
+ case kCancelAfterProgress:
+ mCallbacks->waitForProgressMade();
+ FALLTHROUGH_INTENDED;
+ case kCancelAfterStart:
+ transcoder->cancel();
+ break;
+ case kPauseAfterProgress:
+ mCallbacks->waitForProgressMade();
+ FALLTHROUGH_INTENDED;
+ case kPauseAfterStart:
+ transcoder->pause(&pausedState);
+ break;
+ case kCheckHeartBeat: {
+ mCallbacks->waitForProgressMade();
+ auto startTime = std::chrono::system_clock::now();
+ mCallbacks->waitForTranscodingFinished();
+ auto finishTime = std::chrono::system_clock::now();
+ int32_t expectedCount =
+ (finishTime - startTime) / std::chrono::microseconds(heartBeatIntervalUs);
+ // Here we relax the expected count by 1, in case the last heart-beat just
+ // missed the window, other than that the count should be exact.
+ EXPECT_GE(mCallbacks->mHeartBeatCount, expectedCount - 1);
+ break;
+ }
+ case kRunToCompletion:
+ default:
+ mCallbacks->waitForTranscodingFinished();
+ break;
+ }
+ }
+ close(srcFd);
+ close(dstFd);
+
+ return mCallbacks->mStatus;
+ }
+
+ void testTranscodeVideo(const char* srcPath, const char* destPath, const char* dstMime,
+ int32_t bitrate = 0) {
+ EXPECT_EQ(transcodeHelper(srcPath, destPath,
+ [dstMime, bitrate](AMediaFormat* sourceFormat) {
+ AMediaFormat* format = nullptr;
+ const char* mime = nullptr;
+ AMediaFormat_getString(sourceFormat, AMEDIAFORMAT_KEY_MIME,
+ &mime);
+
+ if (strncmp(mime, "video/", 6) == 0 &&
+ (bitrate > 0 || dstMime != nullptr)) {
+ format = AMediaFormat_new();
+
+ if (bitrate > 0) {
+ AMediaFormat_setInt32(
+ format, AMEDIAFORMAT_KEY_BIT_RATE, bitrate);
+ }
+
+ if (dstMime != nullptr) {
+ AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME,
+ dstMime);
+ }
+ }
+ return format;
+ }),
+ AMEDIA_OK);
+
+ if (dstMime != nullptr) {
+ std::vector<FormatVerifierEntry> extraVerifiers = {
+ {AMEDIAFORMAT_KEY_MIME,
+ [dstMime](const char* key, AMediaFormat* src __unused, AMediaFormat* dst) {
+ const char* mime = nullptr;
+ AMediaFormat_getString(dst, key, &mime);
+ return !strcmp(mime, dstMime);
+ }},
+ };
+ verifyOutputFormat(destPath, &extraVerifiers);
+ } else {
+ verifyOutputFormat(destPath);
+ }
+ }
+
+ void verifyOutputFormat(const char* destPath,
+ const std::vector<FormatVerifierEntry>* extraVerifiers = nullptr) {
+ int dstFd = open(destPath, O_RDONLY);
+ EXPECT_GT(dstFd, 0);
+ ssize_t fileSize = lseek(dstFd, 0, SEEK_END);
+ lseek(dstFd, 0, SEEK_SET);
+
+ std::shared_ptr<MediaSampleReader> sampleReader =
+ MediaSampleReaderNDK::createFromFd(dstFd, 0, fileSize);
+ ASSERT_NE(sampleReader, nullptr);
+
+ std::shared_ptr<AMediaFormat> videoFormat;
+ const size_t trackCount = sampleReader->getTrackCount();
+ for (size_t trackIndex = 0; trackIndex < trackCount; ++trackIndex) {
+ AMediaFormat* trackFormat = sampleReader->getTrackFormat(static_cast<int>(trackIndex));
+ if (trackFormat != nullptr) {
+ const char* mime = nullptr;
+ AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+
+ if (strncmp(mime, "video/", 6) == 0) {
+ LOG(INFO) << "Track # " << trackIndex << ": "
+ << AMediaFormat_toString(trackFormat);
+ videoFormat = std::shared_ptr<AMediaFormat>(trackFormat, &AMediaFormat_delete);
+ break;
+ }
+ }
+ }
+
+ EXPECT_NE(videoFormat, nullptr);
+ if (videoFormat != nullptr) {
+ LOG(INFO) << "source video format: " << AMediaFormat_toString(mSourceVideoFormat.get());
+ LOG(INFO) << "transcoded video format: " << AMediaFormat_toString(videoFormat.get());
+
+ for (int i = 0; i < (sizeof(kFieldsToPreserve) / sizeof(kFieldsToPreserve[0])); ++i) {
+ EXPECT_TRUE(kFieldsToPreserve[i].equal(kFieldsToPreserve[i].key,
+ mSourceVideoFormat.get(), videoFormat.get()))
+ << "Failed at key " << kFieldsToPreserve[i].key;
+ }
+
+ if (extraVerifiers != nullptr) {
+ for (int i = 0; i < extraVerifiers->size(); ++i) {
+ const FormatVerifierEntry& entry = (*extraVerifiers)[i];
+ EXPECT_TRUE(
+ entry.equal(entry.key, mSourceVideoFormat.get(), videoFormat.get()));
+ }
+ }
+ }
+
+ close(dstFd);
+ }
+
+ std::shared_ptr<TestTranscoderCallbacks> mCallbacks;
+ std::shared_ptr<AMediaFormat> mSourceVideoFormat;
+};
+
+TEST_F(MediaTranscoderTests, TestPassthrough) {
+ const char* srcPath = "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
+ const char* destPath = "/data/local/tmp/MediaTranscoder_Passthrough.MP4";
+ testTranscodeVideo(srcPath, destPath, nullptr);
+}
+
+TEST_F(MediaTranscoderTests, TestVideoTranscode_AvcToAvc_Basic) {
+ const char* srcPath = "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
+ const char* destPath = "/data/local/tmp/MediaTranscoder_VideoTranscode_AvcToAvc_Basic.MP4";
+ testTranscodeVideo(srcPath, destPath, AMEDIA_MIMETYPE_VIDEO_AVC);
+}
+
+TEST_F(MediaTranscoderTests, TestVideoTranscode_HevcToAvc_Basic) {
+ const char* srcPath = "/data/local/tmp/TranscodingTestAssets/jets_hevc_1280x720_20Mbps.mp4";
+ const char* destPath = "/data/local/tmp/MediaTranscoder_VideoTranscode_HevcToAvc_Basic.MP4";
+ testTranscodeVideo(srcPath, destPath, AMEDIA_MIMETYPE_VIDEO_AVC);
+}
+
+TEST_F(MediaTranscoderTests, TestVideoTranscode_HevcToAvc_Rotation) {
+ const char* srcPath =
+ "/data/local/tmp/TranscodingTestAssets/desk_hevc_1920x1080_aac_48KHz_rot90.mp4";
+ const char* destPath = "/data/local/tmp/MediaTranscoder_VideoTranscode_HevcToAvc_Rotation.MP4";
+ testTranscodeVideo(srcPath, destPath, AMEDIA_MIMETYPE_VIDEO_AVC);
+}
+
+TEST_F(MediaTranscoderTests, TestVideoTranscode_4K) {
+#if defined(__i386__) || defined(__x86_64__)
+ LOG(WARNING) << "Skipping 4K test on x86 as SW encoder does not support 4K.";
+ return;
+#else
+ const char* srcPath = "/data/local/tmp/TranscodingTestAssets/Video_4K_HEVC_10Frames_Audio.mp4";
+ const char* destPath = "/data/local/tmp/MediaTranscoder_4K.MP4";
+ testTranscodeVideo(srcPath, destPath, AMEDIA_MIMETYPE_VIDEO_AVC);
+#endif
+}
+
+TEST_F(MediaTranscoderTests, TestPreserveBitrate) {
+ const char* srcPath = "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
+ const char* destPath = "/data/local/tmp/MediaTranscoder_PreserveBitrate.MP4";
+ testTranscodeVideo(srcPath, destPath, AMEDIA_MIMETYPE_VIDEO_AVC);
+
+ // Require maximum of 25% difference in file size.
+ // TODO(b/174678336): Find a better test asset to tighten the threshold.
+ EXPECT_LT(getFileSizeDiffPercent(srcPath, destPath, true /* absolute*/), 25);
+}
+
+TEST_F(MediaTranscoderTests, TestCustomBitrate) {
+ const char* srcPath = "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
+ const char* destPath1 = "/data/local/tmp/MediaTranscoder_CustomBitrate_2Mbps.MP4";
+ const char* destPath2 = "/data/local/tmp/MediaTranscoder_CustomBitrate_8Mbps.MP4";
+ testTranscodeVideo(srcPath, destPath1, AMEDIA_MIMETYPE_VIDEO_AVC, 2 * 1000 * 1000);
+ mCallbacks = std::make_shared<TestTranscoderCallbacks>();
+ testTranscodeVideo(srcPath, destPath2, AMEDIA_MIMETYPE_VIDEO_AVC, 8 * 1000 * 1000);
+
+ // The source asset is very short and heavily compressed from the beginning so don't expect the
+ // requested bitrate to be exactly matched. However the 8mbps should at least be larger.
+ // TODO(b/174678336): Find a better test asset to tighten the threshold.
+ EXPECT_GT(getFileSizeDiffPercent(destPath1, destPath2), 10);
+}
+
+static AMediaFormat* getAVCVideoFormat(AMediaFormat* sourceFormat) {
+ AMediaFormat* format = nullptr;
+ const char* mime = nullptr;
+ AMediaFormat_getString(sourceFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+
+ if (strncmp(mime, "video/", 6) == 0) {
+ format = AMediaFormat_new();
+ AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_AVC);
+ }
+
+ return format;
+}
+
+TEST_F(MediaTranscoderTests, TestCancelAfterProgress) {
+ const char* srcPath = "/data/local/tmp/TranscodingTestAssets/longtest_15s.mp4";
+ const char* destPath = "/data/local/tmp/MediaTranscoder_Cancel.MP4";
+
+ for (int i = 0; i < 20; ++i) {
+ EXPECT_EQ(transcodeHelper(srcPath, destPath, getAVCVideoFormat, kCancelAfterProgress),
+ AMEDIA_OK);
+ EXPECT_FALSE(mCallbacks->mFinished);
+ mCallbacks = std::make_shared<TestTranscoderCallbacks>();
+ }
+}
+
+TEST_F(MediaTranscoderTests, TestCancelAfterStart) {
+ const char* srcPath = "/data/local/tmp/TranscodingTestAssets/longtest_15s.mp4";
+ const char* destPath = "/data/local/tmp/MediaTranscoder_Cancel.MP4";
+
+ for (int i = 0; i < 20; ++i) {
+ EXPECT_EQ(transcodeHelper(srcPath, destPath, getAVCVideoFormat, kCancelAfterStart),
+ AMEDIA_OK);
+ EXPECT_FALSE(mCallbacks->mFinished);
+ mCallbacks = std::make_shared<TestTranscoderCallbacks>();
+ }
+}
+
+TEST_F(MediaTranscoderTests, TestPauseAfterProgress) {
+ const char* srcPath = "/data/local/tmp/TranscodingTestAssets/longtest_15s.mp4";
+ const char* destPath = "/data/local/tmp/MediaTranscoder_Pause.MP4";
+
+ for (int i = 0; i < 20; ++i) {
+ EXPECT_EQ(transcodeHelper(srcPath, destPath, getAVCVideoFormat, kPauseAfterProgress),
+ AMEDIA_OK);
+ EXPECT_FALSE(mCallbacks->mFinished);
+ mCallbacks = std::make_shared<TestTranscoderCallbacks>();
+ }
+}
+
+TEST_F(MediaTranscoderTests, TestPauseAfterStart) {
+ const char* srcPath = "/data/local/tmp/TranscodingTestAssets/longtest_15s.mp4";
+ const char* destPath = "/data/local/tmp/MediaTranscoder_Pause.MP4";
+
+ for (int i = 0; i < 20; ++i) {
+ EXPECT_EQ(transcodeHelper(srcPath, destPath, getAVCVideoFormat, kPauseAfterStart),
+ AMEDIA_OK);
+ EXPECT_FALSE(mCallbacks->mFinished);
+ mCallbacks = std::make_shared<TestTranscoderCallbacks>();
+ }
+}
+
+TEST_F(MediaTranscoderTests, TestHeartBeat) {
+ const char* srcPath = "/data/local/tmp/TranscodingTestAssets/longtest_15s.mp4";
+ const char* destPath = "/data/local/tmp/MediaTranscoder_HeartBeat.MP4";
+
+ // Use a shorter value of 500ms than the default 1000ms to get more heart beat for testing.
+ const int64_t heartBeatIntervalUs = 500000LL;
+ EXPECT_EQ(transcodeHelper(srcPath, destPath, getAVCVideoFormat, kCheckHeartBeat,
+ heartBeatIntervalUs),
+ AMEDIA_OK);
+ EXPECT_TRUE(mCallbacks->mFinished);
+}
+
+} // namespace android
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/media/libmediatranscoding/transcoder/tests/PassthroughTrackTranscoderTests.cpp b/media/libmediatranscoding/transcoder/tests/PassthroughTrackTranscoderTests.cpp
new file mode 100644
index 0000000..fdbf535
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/tests/PassthroughTrackTranscoderTests.cpp
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Unit Test for PassthroughTrackTranscoder
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "PassthroughTrackTranscoderTests"
+
+#include <android-base/logging.h>
+#include <fcntl.h>
+#include <gtest/gtest.h>
+#include <media/MediaSampleReaderNDK.h>
+#include <media/NdkMediaExtractor.h>
+#include <media/PassthroughTrackTranscoder.h>
+#include <openssl/md5.h>
+
+#include <vector>
+
+#include "TranscoderTestUtils.h"
+
+namespace android {
+
+class PassthroughTrackTranscoderTests : public ::testing::Test {
+public:
+ PassthroughTrackTranscoderTests() { LOG(DEBUG) << "PassthroughTrackTranscoderTests created"; }
+
+ void SetUp() override { LOG(DEBUG) << "PassthroughTrackTranscoderTests set up"; }
+
+ void initSourceAndExtractor() {
+ const char* sourcePath =
+ "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
+
+ mExtractor = AMediaExtractor_new();
+ ASSERT_NE(mExtractor, nullptr);
+
+ mSourceFd = open(sourcePath, O_RDONLY);
+ ASSERT_GT(mSourceFd, 0);
+
+ mSourceFileSize = lseek(mSourceFd, 0, SEEK_END);
+ lseek(mSourceFd, 0, SEEK_SET);
+
+ media_status_t status =
+ AMediaExtractor_setDataSourceFd(mExtractor, mSourceFd, 0, mSourceFileSize);
+ ASSERT_EQ(status, AMEDIA_OK);
+
+ const size_t trackCount = AMediaExtractor_getTrackCount(mExtractor);
+ for (size_t trackIndex = 0; trackIndex < trackCount; trackIndex++) {
+ AMediaFormat* trackFormat = AMediaExtractor_getTrackFormat(mExtractor, trackIndex);
+ ASSERT_NE(trackFormat, nullptr);
+
+ const char* mime = nullptr;
+ AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+ ASSERT_NE(mime, nullptr);
+
+ if (strncmp(mime, "audio/", 6) == 0) {
+ mTrackIndex = trackIndex;
+ AMediaExtractor_selectTrack(mExtractor, trackIndex);
+ break;
+ }
+
+ AMediaFormat_delete(trackFormat);
+ }
+ }
+
+ void TearDown() override {
+ LOG(DEBUG) << "PassthroughTrackTranscoderTests tear down";
+ if (mExtractor != nullptr) {
+ AMediaExtractor_delete(mExtractor);
+ mExtractor = nullptr;
+ }
+ if (mSourceFd > 0) {
+ close(mSourceFd);
+ mSourceFd = -1;
+ }
+ }
+
+ ~PassthroughTrackTranscoderTests() {
+ LOG(DEBUG) << "PassthroughTrackTranscoderTests destroyed";
+ }
+
+ int mSourceFd = -1;
+ size_t mSourceFileSize;
+ int mTrackIndex;
+ AMediaExtractor* mExtractor = nullptr;
+};
+
+/** Helper class for comparing sample data using checksums. */
+class SampleID {
+public:
+ SampleID(const uint8_t* sampleData, ssize_t sampleSize) : mSize{sampleSize} {
+ MD5_CTX md5Ctx;
+ MD5_Init(&md5Ctx);
+ MD5_Update(&md5Ctx, sampleData, sampleSize);
+ MD5_Final(mChecksum, &md5Ctx);
+ }
+
+ bool operator==(const SampleID& rhs) const {
+ return mSize == rhs.mSize && memcmp(mChecksum, rhs.mChecksum, MD5_DIGEST_LENGTH) == 0;
+ }
+
+ uint8_t mChecksum[MD5_DIGEST_LENGTH];
+ ssize_t mSize;
+};
+
+/**
+ * Tests that the output samples of PassthroughTrackTranscoder are identical to the source samples
+ * and in correct order.
+ */
+TEST_F(PassthroughTrackTranscoderTests, SampleEquality) {
+ LOG(DEBUG) << "Testing SampleEquality";
+
+ ssize_t bufferSize = 1024;
+ auto buffer = std::make_unique<uint8_t[]>(bufferSize);
+
+ initSourceAndExtractor();
+
+ // Loop through all samples of a track and store size and checksums.
+ std::vector<SampleID> sampleChecksums;
+
+ int64_t sampleTime = AMediaExtractor_getSampleTime(mExtractor);
+ while (sampleTime != -1) {
+ if (AMediaExtractor_getSampleTrackIndex(mExtractor) == mTrackIndex) {
+ ssize_t sampleSize = AMediaExtractor_getSampleSize(mExtractor);
+ if (bufferSize < sampleSize) {
+ bufferSize = sampleSize;
+ buffer = std::make_unique<uint8_t[]>(bufferSize);
+ }
+
+ ssize_t bytesRead =
+ AMediaExtractor_readSampleData(mExtractor, buffer.get(), bufferSize);
+ ASSERT_EQ(bytesRead, sampleSize);
+
+ SampleID sampleId{buffer.get(), sampleSize};
+ sampleChecksums.push_back(sampleId);
+ }
+
+ AMediaExtractor_advance(mExtractor);
+ sampleTime = AMediaExtractor_getSampleTime(mExtractor);
+ }
+
+ // Create and start the transcoder.
+ auto callback = std::make_shared<TestTrackTranscoderCallback>();
+ PassthroughTrackTranscoder transcoder{callback};
+
+ std::shared_ptr<MediaSampleReader> mediaSampleReader =
+ MediaSampleReaderNDK::createFromFd(mSourceFd, 0, mSourceFileSize);
+ EXPECT_NE(mediaSampleReader, nullptr);
+
+ EXPECT_EQ(mediaSampleReader->selectTrack(mTrackIndex), AMEDIA_OK);
+ EXPECT_EQ(transcoder.configure(mediaSampleReader, mTrackIndex, nullptr /* destinationFormat */),
+ AMEDIA_OK);
+ ASSERT_TRUE(transcoder.start());
+
+ // Pull transcoder's output samples and compare against input checksums.
+ bool eos = false;
+ uint64_t sampleCount = 0;
+ transcoder.setSampleConsumer(
+ [&sampleCount, &sampleChecksums, &eos](const std::shared_ptr<MediaSample>& sample) {
+ ASSERT_NE(sample, nullptr);
+ EXPECT_FALSE(eos);
+
+ if (sample->info.flags & SAMPLE_FLAG_END_OF_STREAM) {
+ eos = true;
+ } else {
+ SampleID sampleId{sample->buffer, static_cast<ssize_t>(sample->info.size)};
+ EXPECT_TRUE(sampleId == sampleChecksums[sampleCount]);
+ ++sampleCount;
+ }
+ });
+
+ callback->waitUntilFinished();
+ EXPECT_EQ(sampleCount, sampleChecksums.size());
+}
+
+/** Class for testing PassthroughTrackTranscoder's built in buffer pool. */
+class BufferPoolTests : public ::testing::Test {
+public:
+ static constexpr int kMaxBuffers = 5;
+
+ void SetUp() override {
+ LOG(DEBUG) << "BufferPoolTests set up";
+ mBufferPool = std::make_shared<PassthroughTrackTranscoder::BufferPool>(kMaxBuffers);
+ }
+
+ void TearDown() override {
+ LOG(DEBUG) << "BufferPoolTests tear down";
+ mBufferPool.reset();
+ }
+
+ std::shared_ptr<PassthroughTrackTranscoder::BufferPool> mBufferPool;
+};
+
+TEST_F(BufferPoolTests, BufferReuse) {
+ LOG(DEBUG) << "Testing BufferReuse";
+
+ uint8_t* buffer1 = mBufferPool->getBufferWithSize(10);
+ EXPECT_NE(buffer1, nullptr);
+
+ uint8_t* buffer2 = mBufferPool->getBufferWithSize(10);
+ EXPECT_NE(buffer2, nullptr);
+ EXPECT_NE(buffer2, buffer1);
+
+ mBufferPool->returnBuffer(buffer1);
+
+ uint8_t* buffer3 = mBufferPool->getBufferWithSize(10);
+ EXPECT_NE(buffer3, nullptr);
+ EXPECT_NE(buffer3, buffer2);
+ EXPECT_EQ(buffer3, buffer1);
+
+ mBufferPool->returnBuffer(buffer2);
+
+ uint8_t* buffer4 = mBufferPool->getBufferWithSize(10);
+ EXPECT_NE(buffer4, nullptr);
+ EXPECT_NE(buffer4, buffer1);
+ EXPECT_EQ(buffer4, buffer2);
+}
+
+TEST_F(BufferPoolTests, SmallestAvailableBuffer) {
+ LOG(DEBUG) << "Testing SmallestAvailableBuffer";
+
+ uint8_t* buffer1 = mBufferPool->getBufferWithSize(10);
+ EXPECT_NE(buffer1, nullptr);
+
+ uint8_t* buffer2 = mBufferPool->getBufferWithSize(15);
+ EXPECT_NE(buffer2, nullptr);
+ EXPECT_NE(buffer2, buffer1);
+
+ uint8_t* buffer3 = mBufferPool->getBufferWithSize(20);
+ EXPECT_NE(buffer3, nullptr);
+ EXPECT_NE(buffer3, buffer1);
+ EXPECT_NE(buffer3, buffer2);
+
+ mBufferPool->returnBuffer(buffer1);
+ mBufferPool->returnBuffer(buffer2);
+ mBufferPool->returnBuffer(buffer3);
+
+ uint8_t* buffer4 = mBufferPool->getBufferWithSize(11);
+ EXPECT_NE(buffer4, nullptr);
+ EXPECT_EQ(buffer4, buffer2);
+
+ uint8_t* buffer5 = mBufferPool->getBufferWithSize(11);
+ EXPECT_NE(buffer5, nullptr);
+ EXPECT_EQ(buffer5, buffer3);
+}
+
+TEST_F(BufferPoolTests, AddAfterAbort) {
+ LOG(DEBUG) << "Testing AddAfterAbort";
+
+ uint8_t* buffer1 = mBufferPool->getBufferWithSize(10);
+ EXPECT_NE(buffer1, nullptr);
+ mBufferPool->returnBuffer(buffer1);
+
+ mBufferPool->abort();
+ uint8_t* buffer2 = mBufferPool->getBufferWithSize(10);
+ EXPECT_EQ(buffer2, nullptr);
+}
+
+TEST_F(BufferPoolTests, MaximumBuffers) {
+ LOG(DEBUG) << "Testing MaximumBuffers";
+
+ static constexpr size_t kBufferBaseSize = 10;
+ std::unordered_map<uint8_t*, size_t> addressSizeMap;
+
+ // Get kMaxBuffers * 2 new buffers with increasing size.
+ // (Note: Once kMaxBuffers have been allocated, the pool will delete old buffers to accommodate
+ // new ones making the deleted buffers free to be reused by the system's heap memory allocator.
+ // So we cannot test that each new pointer is unique here.)
+ for (int i = 0; i < kMaxBuffers * 2; i++) {
+ size_t size = kBufferBaseSize + i;
+ uint8_t* buffer = mBufferPool->getBufferWithSize(size);
+ EXPECT_NE(buffer, nullptr);
+ addressSizeMap[buffer] = size;
+ mBufferPool->returnBuffer(buffer);
+ }
+
+ // Verify that the pool now contains the kMaxBuffers largest buffers allocated above and that
+ // the buffer of matching size is returned.
+ for (int i = kMaxBuffers; i < kMaxBuffers * 2; i++) {
+ size_t size = kBufferBaseSize + i;
+ uint8_t* buffer = mBufferPool->getBufferWithSize(size);
+ EXPECT_NE(buffer, nullptr);
+
+ auto it = addressSizeMap.find(buffer);
+ ASSERT_NE(it, addressSizeMap.end());
+ EXPECT_EQ(it->second, size);
+ mBufferPool->returnBuffer(buffer);
+ }
+}
+
+} // namespace android
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/media/libmediatranscoding/transcoder/tests/README.md b/media/libmediatranscoding/transcoder/tests/README.md
new file mode 100644
index 0000000..59417b0
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/tests/README.md
@@ -0,0 +1,14 @@
+## Transcoder Testing ##
+---
+#### Transcoder unit tests :
+To run all transcoder unit tests, run the supplied script from this folder:
+
+```
+./build_and_run_all_unit_tests.sh
+```
+
+To run individual unit test modules, use atest:
+
+```
+atest MediaSampleReaderNDK
+```
diff --git a/media/libmediatranscoding/transcoder/tests/TranscoderTestUtils.h b/media/libmediatranscoding/transcoder/tests/TranscoderTestUtils.h
new file mode 100644
index 0000000..35fe25b
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/tests/TranscoderTestUtils.h
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/MediaTrackTranscoder.h>
+#include <media/MediaTrackTranscoderCallback.h>
+#include <media/MediaTranscoder.h>
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+
+namespace android {
+
+//
+// This file contains transcoding test utilities.
+//
+
+namespace TranscoderTestUtils {
+
+std::shared_ptr<AMediaFormat> GetVideoFormat(const std::string& path,
+ std::string* mimeOut = nullptr) {
+ int fd = open(path.c_str(), O_RDONLY);
+ EXPECT_GT(fd, 0);
+ ssize_t fileSize = lseek(fd, 0, SEEK_END);
+ lseek(fd, 0, SEEK_SET);
+
+ auto sampleReader = MediaSampleReaderNDK::createFromFd(fd, 0, fileSize);
+ EXPECT_NE(sampleReader, nullptr);
+
+ for (size_t i = 0; i < sampleReader->getTrackCount(); ++i) {
+ AMediaFormat* format = sampleReader->getTrackFormat(i);
+
+ const char* mime = nullptr;
+ AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
+ if (strncmp(mime, "video/", 6) == 0) {
+ if (mimeOut != nullptr) {
+ mimeOut->assign(mime);
+ }
+ return std::shared_ptr<AMediaFormat>(format, &AMediaFormat_delete);
+ }
+
+ AMediaFormat_delete(format);
+ }
+ return nullptr;
+}
+
+}; // namespace TranscoderTestUtils
+
+class TrackTranscoderTestUtils {
+public:
+ static std::shared_ptr<AMediaFormat> getDefaultVideoDestinationFormat(
+ AMediaFormat* sourceFormat, bool includeBitrate = true) {
+ // Default video destination format setup.
+ static constexpr float kFrameRate = 30.0f;
+ static constexpr int32_t kBitRate = 2 * 1000 * 1000;
+
+ AMediaFormat* destinationFormat = AMediaFormat_new();
+ AMediaFormat_copy(destinationFormat, sourceFormat);
+ AMediaFormat_setFloat(destinationFormat, AMEDIAFORMAT_KEY_FRAME_RATE, kFrameRate);
+ if (includeBitrate) {
+ AMediaFormat_setInt32(destinationFormat, AMEDIAFORMAT_KEY_BIT_RATE, kBitRate);
+ }
+
+ return std::shared_ptr<AMediaFormat>(destinationFormat, &AMediaFormat_delete);
+ }
+};
+
+class TestTrackTranscoderCallback : public MediaTrackTranscoderCallback {
+public:
+ TestTrackTranscoderCallback() = default;
+ ~TestTrackTranscoderCallback() = default;
+
+ // MediaTrackTranscoderCallback
+ void onTrackFormatAvailable(const MediaTrackTranscoder* transcoder __unused) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mTrackFormatAvailable = true;
+ mTrackFormatAvailableCondition.notify_all();
+ }
+
+ void onTrackFinished(const MediaTrackTranscoder* transcoder __unused) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mTranscodingFinished = true;
+ mTranscodingFinishedCondition.notify_all();
+ }
+
+ virtual void onTrackStopped(const MediaTrackTranscoder* transcoder __unused) override {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mTranscodingFinished = true;
+ mTranscodingStopped = true;
+ mTranscodingFinishedCondition.notify_all();
+ }
+
+ void onTrackError(const MediaTrackTranscoder* transcoder __unused, media_status_t status) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mTranscodingFinished = true;
+ mStatus = status;
+ mTranscodingFinishedCondition.notify_all();
+ }
+ // ~MediaTrackTranscoderCallback
+
+ media_status_t waitUntilFinished() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ while (!mTranscodingFinished) {
+ mTranscodingFinishedCondition.wait(lock);
+ }
+ return mStatus;
+ }
+
+ void waitUntilTrackFormatAvailable() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ while (!mTrackFormatAvailable) {
+ mTrackFormatAvailableCondition.wait(lock);
+ }
+ }
+
+ bool transcodingWasStopped() const { return mTranscodingFinished && mTranscodingStopped; }
+ bool transcodingFinished() const {
+ return mTranscodingFinished && !mTranscodingStopped && mStatus == AMEDIA_OK;
+ }
+
+private:
+ media_status_t mStatus = AMEDIA_OK;
+ std::mutex mMutex;
+ std::condition_variable mTranscodingFinishedCondition;
+ std::condition_variable mTrackFormatAvailableCondition;
+ bool mTranscodingFinished = false;
+ bool mTranscodingStopped = false;
+ bool mTrackFormatAvailable = false;
+};
+
+class TestTranscoderCallbacks : public MediaTranscoder::CallbackInterface {
+public:
+ virtual void onFinished(const MediaTranscoder* transcoder __unused) override {
+ std::unique_lock<std::mutex> lock(mMutex);
+ EXPECT_FALSE(mFinished);
+ mFinished = true;
+ mCondition.notify_all();
+ }
+
+ virtual void onError(const MediaTranscoder* transcoder __unused,
+ media_status_t error) override {
+ std::unique_lock<std::mutex> lock(mMutex);
+ EXPECT_NE(error, AMEDIA_OK);
+ EXPECT_FALSE(mFinished);
+ mFinished = true;
+ mStatus = error;
+ mCondition.notify_all();
+ }
+
+ virtual void onProgressUpdate(const MediaTranscoder* transcoder __unused,
+ int32_t progress) override {
+ std::unique_lock<std::mutex> lock(mMutex);
+ if (progress > 0 && !mProgressMade) {
+ mProgressMade = true;
+ mCondition.notify_all();
+ }
+ }
+
+ virtual void onHeartBeat(const MediaTranscoder* transcoder __unused) override {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mHeartBeatCount++;
+ }
+
+ virtual void onCodecResourceLost(const MediaTranscoder* transcoder __unused,
+ const std::shared_ptr<ndk::ScopedAParcel>& pausedState
+ __unused) override {}
+
+ void waitForTranscodingFinished() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ while (!mFinished) {
+ mCondition.wait(lock);
+ }
+ }
+
+ void waitForProgressMade() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ while (!mProgressMade && !mFinished) {
+ mCondition.wait(lock);
+ }
+ }
+ media_status_t mStatus = AMEDIA_OK;
+ bool mFinished = false;
+ int32_t mHeartBeatCount = 0;
+
+private:
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ bool mProgressMade = false;
+};
+
+class OneShotSemaphore {
+public:
+ void wait() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ while (!mSignaled) {
+ mCondition.wait(lock);
+ }
+ }
+
+ void signal() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mSignaled = true;
+ mCondition.notify_all();
+ }
+
+private:
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ bool mSignaled = false;
+};
+
+}; // namespace android
diff --git a/media/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp b/media/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp
new file mode 100644
index 0000000..88c3fd3
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Unit Test for VideoTrackTranscoder
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "VideoTrackTranscoderTests"
+
+#include <android-base/logging.h>
+#include <fcntl.h>
+#include <gtest/gtest.h>
+#include <media/MediaSampleReaderNDK.h>
+#include <media/NdkCommon.h>
+#include <media/VideoTrackTranscoder.h>
+#include <utils/Timers.h>
+
+#include "TranscoderTestUtils.h"
+
+namespace android {
+
+// TODO(b/155304421): Implement more advanced video specific tests:
+// - Codec conversions (HEVC -> AVC).
+// - Bitrate validation.
+// - Output frame validation through PSNR.
+
+class VideoTrackTranscoderTests : public ::testing::Test {
+public:
+ VideoTrackTranscoderTests() { LOG(DEBUG) << "VideoTrackTranscoderTests created"; }
+
+ void SetUp() override {
+ LOG(DEBUG) << "VideoTrackTranscoderTests set up";
+ const char* sourcePath =
+ "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
+
+ const int sourceFd = open(sourcePath, O_RDONLY);
+ ASSERT_GT(sourceFd, 0);
+
+ const off_t fileSize = lseek(sourceFd, 0, SEEK_END);
+ lseek(sourceFd, 0, SEEK_SET);
+
+ mMediaSampleReader = MediaSampleReaderNDK::createFromFd(sourceFd, 0, fileSize);
+ ASSERT_NE(mMediaSampleReader, nullptr);
+ close(sourceFd);
+
+ for (size_t trackIndex = 0; trackIndex < mMediaSampleReader->getTrackCount();
+ ++trackIndex) {
+ AMediaFormat* trackFormat = mMediaSampleReader->getTrackFormat(trackIndex);
+ ASSERT_NE(trackFormat, nullptr);
+
+ const char* mime = nullptr;
+ AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+ ASSERT_NE(mime, nullptr);
+
+ if (strncmp(mime, "video/", 6) == 0) {
+ mTrackIndex = trackIndex;
+
+ mSourceFormat = std::shared_ptr<AMediaFormat>(trackFormat, &AMediaFormat_delete);
+ ASSERT_NE(mSourceFormat, nullptr);
+
+ mDestinationFormat =
+ TrackTranscoderTestUtils::getDefaultVideoDestinationFormat(trackFormat);
+ ASSERT_NE(mDestinationFormat, nullptr);
+ break;
+ }
+
+ AMediaFormat_delete(trackFormat);
+ }
+
+ ASSERT_NE(mSourceFormat, nullptr);
+ }
+
+ void TearDown() override { LOG(DEBUG) << "VideoTrackTranscoderTests tear down"; }
+
+ ~VideoTrackTranscoderTests() { LOG(DEBUG) << "VideoTrackTranscoderTests destroyed"; }
+
+ static int32_t getConfiguredBitrate(const std::shared_ptr<VideoTrackTranscoder>& transcoder) {
+ return transcoder->mConfiguredBitrate;
+ }
+
+ std::shared_ptr<MediaSampleReader> mMediaSampleReader;
+ int mTrackIndex;
+ std::shared_ptr<AMediaFormat> mSourceFormat;
+ std::shared_ptr<AMediaFormat> mDestinationFormat;
+};
+
+TEST_F(VideoTrackTranscoderTests, SampleSoundness) {
+ LOG(DEBUG) << "Testing SampleSoundness";
+ auto callback = std::make_shared<TestTrackTranscoderCallback>();
+ auto transcoder = VideoTrackTranscoder::create(callback);
+
+ EXPECT_EQ(mMediaSampleReader->selectTrack(mTrackIndex), AMEDIA_OK);
+ EXPECT_EQ(transcoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
+ AMEDIA_OK);
+ ASSERT_TRUE(transcoder->start());
+
+ bool eos = false;
+ uint64_t sampleCount = 0;
+ transcoder->setSampleConsumer([&sampleCount, &eos](const std::shared_ptr<MediaSample>& sample) {
+ ASSERT_NE(sample, nullptr);
+ const uint32_t flags = sample->info.flags;
+
+ if (sampleCount == 0) {
+ // Expect first sample to be a codec config.
+ EXPECT_TRUE((flags & SAMPLE_FLAG_CODEC_CONFIG) != 0);
+ EXPECT_TRUE((flags & SAMPLE_FLAG_SYNC_SAMPLE) == 0);
+ EXPECT_TRUE((flags & SAMPLE_FLAG_END_OF_STREAM) == 0);
+ EXPECT_TRUE((flags & SAMPLE_FLAG_PARTIAL_FRAME) == 0);
+ } else if (sampleCount == 1) {
+ // Expect second sample to be a sync sample.
+ EXPECT_TRUE((flags & SAMPLE_FLAG_CODEC_CONFIG) == 0);
+ EXPECT_TRUE((flags & SAMPLE_FLAG_SYNC_SAMPLE) != 0);
+ EXPECT_TRUE((flags & SAMPLE_FLAG_END_OF_STREAM) == 0);
+ }
+
+ if (!(flags & SAMPLE_FLAG_END_OF_STREAM)) {
+ // Expect a valid buffer unless it is EOS.
+ EXPECT_NE(sample->buffer, nullptr);
+ EXPECT_NE(sample->bufferId, 0xBAADF00D);
+ EXPECT_GT(sample->info.size, 0);
+ } else {
+ EXPECT_FALSE(eos);
+ eos = true;
+ }
+
+ ++sampleCount;
+ });
+
+ EXPECT_EQ(callback->waitUntilFinished(), AMEDIA_OK);
+}
+
+TEST_F(VideoTrackTranscoderTests, PreserveBitrate) {
+ LOG(DEBUG) << "Testing PreserveBitrate";
+ auto callback = std::make_shared<TestTrackTranscoderCallback>();
+ auto transcoder = VideoTrackTranscoder::create(callback);
+
+ auto destFormat = TrackTranscoderTestUtils::getDefaultVideoDestinationFormat(
+ mSourceFormat.get(), false /* includeBitrate*/);
+ EXPECT_NE(destFormat, nullptr);
+
+ EXPECT_EQ(mMediaSampleReader->selectTrack(mTrackIndex), AMEDIA_OK);
+
+ int32_t srcBitrate;
+ EXPECT_EQ(mMediaSampleReader->getEstimatedBitrateForTrack(mTrackIndex, &srcBitrate), AMEDIA_OK);
+
+ ASSERT_EQ(transcoder->configure(mMediaSampleReader, mTrackIndex, destFormat), AMEDIA_OK);
+ ASSERT_TRUE(transcoder->start());
+
+ callback->waitUntilTrackFormatAvailable();
+ transcoder->stop();
+ EXPECT_EQ(callback->waitUntilFinished(), AMEDIA_OK);
+
+ int32_t outBitrate = getConfiguredBitrate(transcoder);
+ ASSERT_GT(outBitrate, 0);
+
+ EXPECT_EQ(srcBitrate, outBitrate);
+}
+
+// VideoTrackTranscoder needs a valid destination format.
+TEST_F(VideoTrackTranscoderTests, NullDestinationFormat) {
+ LOG(DEBUG) << "Testing NullDestinationFormat";
+ auto callback = std::make_shared<TestTrackTranscoderCallback>();
+ std::shared_ptr<AMediaFormat> nullFormat;
+
+ auto transcoder = VideoTrackTranscoder::create(callback);
+ EXPECT_EQ(transcoder->configure(mMediaSampleReader, 0 /* trackIndex */, nullFormat),
+ AMEDIA_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(VideoTrackTranscoderTests, LingeringEncoder) {
+ OneShotSemaphore semaphore;
+ auto callback = std::make_shared<TestTrackTranscoderCallback>();
+ auto transcoder = VideoTrackTranscoder::create(callback);
+
+ EXPECT_EQ(mMediaSampleReader->selectTrack(mTrackIndex), AMEDIA_OK);
+ EXPECT_EQ(transcoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
+ AMEDIA_OK);
+ ASSERT_TRUE(transcoder->start());
+
+ std::vector<std::shared_ptr<MediaSample>> samples;
+ transcoder->setSampleConsumer(
+ [&samples, &semaphore](const std::shared_ptr<MediaSample>& sample) {
+ if (samples.size() >= 4) return;
+
+ ASSERT_NE(sample, nullptr);
+ samples.push_back(sample);
+
+ if (samples.size() == 4 || sample->info.flags & SAMPLE_FLAG_END_OF_STREAM) {
+ semaphore.signal();
+ }
+ });
+
+ // Wait for the encoder to output samples before stopping and releasing the transcoder.
+ semaphore.wait();
+
+ transcoder->stop();
+ EXPECT_EQ(callback->waitUntilFinished(), AMEDIA_OK);
+ transcoder.reset();
+
+ // Return buffers to the codec so that it can resume processing, but keep one buffer to avoid
+ // the codec being released.
+ samples.resize(1);
+
+ // Wait for async codec events.
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+}
+
+} // namespace android
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/media/libmediatranscoding/transcoder/tests/build_and_run_all_unit_tests.sh b/media/libmediatranscoding/transcoder/tests/build_and_run_all_unit_tests.sh
new file mode 100755
index 0000000..bba2bc5
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/tests/build_and_run_all_unit_tests.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+
+# Exit on compilation error.
+set -e
+
+#
+# Run tests in this directory.
+#
+if [ "$SYNC_FINISHED" != true ]; then
+ if [ -z "$ANDROID_BUILD_TOP" ]; then
+ echo "Android build environment not set"
+ exit -1
+ fi
+
+ # ensure we have mm
+ . $ANDROID_BUILD_TOP/build/envsetup.sh
+
+ mm
+
+ echo "waiting for device"
+
+ adb root && adb wait-for-device remount && adb sync
+fi
+
+# Push the files onto the device.
+. $ANDROID_BUILD_TOP/frameworks/av/media/libmediatranscoding/tests/push_assets.sh
+
+echo "========================================"
+
+# Don't exit if a test fails.
+set +e
+
+echo "testing MediaSampleReaderNDK"
+adb shell ASAN_OPTIONS=detect_container_overflow=0 /data/nativetest64/MediaSampleReaderNDKTests/MediaSampleReaderNDKTests
+
+echo "testing MediaSampleQueue"
+adb shell ASAN_OPTIONS=detect_container_overflow=0 /data/nativetest64/MediaSampleQueueTests/MediaSampleQueueTests
+
+echo "testing MediaTrackTranscoder"
+adb shell ASAN_OPTIONS=detect_container_overflow=0 /data/nativetest64/MediaTrackTranscoderTests/MediaTrackTranscoderTests
+
+echo "testing VideoTrackTranscoder"
+adb shell ASAN_OPTIONS=detect_container_overflow=0 /data/nativetest64/VideoTrackTranscoderTests/VideoTrackTranscoderTests
+
+echo "testing PassthroughTrackTranscoder"
+adb shell ASAN_OPTIONS=detect_container_overflow=0 /data/nativetest64/PassthroughTrackTranscoderTests/PassthroughTrackTranscoderTests
+
+echo "testing MediaSampleWriter"
+adb shell ASAN_OPTIONS=detect_container_overflow=0 /data/nativetest64/MediaSampleWriterTests/MediaSampleWriterTests
+
+echo "testing MediaTranscoder"
+adb shell ASAN_OPTIONS=detect_container_overflow=0 /data/nativetest64/MediaTranscoderTests/MediaTranscoderTests
diff --git a/media/libmediatranscoding/transcoder/tests/fuzzer/Android.bp b/media/libmediatranscoding/transcoder/tests/fuzzer/Android.bp
new file mode 100644
index 0000000..69b2827
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/tests/fuzzer/Android.bp
@@ -0,0 +1,55 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_fuzz {
+ name: "media_transcoder_fuzzer",
+ srcs: [
+ "media_transcoder_fuzzer.cpp",
+ ],
+ static_libs: [
+ "liblog",
+ "libmediatranscoder",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libmediandk",
+ "libnativewindow",
+ "libutils",
+ ],
+ header_libs: [
+ "libcodec2_headers",
+ ],
+ fuzz_config: {
+ cc: [
+ "android-media-fuzzing-reports@google.com",
+ ],
+ componentid: 155276,
+ },
+}
diff --git a/media/libmediatranscoding/transcoder/tests/fuzzer/README.md b/media/libmediatranscoding/transcoder/tests/fuzzer/README.md
new file mode 100644
index 0000000..d376a20
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/tests/fuzzer/README.md
@@ -0,0 +1,59 @@
+# Fuzzer for libmediatranscoder
+
+## Plugin Design Considerations
+The fuzzer plugin for libmediatranscoder is designed based on the understanding of the
+transcoder and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+Transcoder supports the following parameters:
+1. Destination Mime Type (parameter name: `dstMime`)
+2. AVC Profile (parameter name: `profile`)
+3. HEVC Profile (parameter name: `profile`)
+4. AVC Level (parameter name: `level`)
+5. HEVC Level (parameter name: `level`)
+6. Bitrate (parameter name: `bitrate`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `dstMime` | 0. `AMEDIA_MIMETYPE_VIDEO_AVC` 1. `AMEDIA_MIMETYPE_VIDEO_HEVC` | Bit 0 (LSB) of 1st byte of data |
+| `profile` for AVC | 0. `PROFILE_AVC_BASELINE` 1. `PROFILE_AVC_CONSTRAINED_BASELINE` 2. `PROFILE_AVC_MAIN`| All bits of 2nd byte of data modulus 3 |
+| `profile` for HEVC | 0. `PROFILE_HEVC_MAIN` 1. `PROFILE_HEVC_MAIN_STILL` | All bits of 2nd byte of data modulus 2 |
+| `level` for AVC | 0. `LEVEL_AVC_1` 1. `LEVEL_AVC_1B` 2. `LEVEL_AVC_1_1` 3. `LEVEL_AVC_1_2` 4. `LEVEL_AVC_1_3` 5. `LEVEL_AVC_2` 6. `LEVEL_AVC_2_1` 7. `LEVEL_AVC_2_2` 8. `LEVEL_AVC_3` 9. `LEVEL_AVC_3_1` 10. `LEVEL_AVC_3_2` 11. `LEVEL_AVC_4` 12. `LEVEL_AVC_4_1` 13. `LEVEL_AVC_4_2` 14. `LEVEL_AVC_5`| All bits of 3rd byte of data modulus 15 |
+| `level` for HEVC | 0. `LEVEL_HEVC_MAIN_1` 1. `LEVEL_HEVC_MAIN_2` 2. `LEVEL_HEVC_MAIN_2_1` 3. `LEVEL_HEVC_MAIN_3` 4. `LEVEL_HEVC_MAIN_3_1` 5. `LEVEL_HEVC_MAIN_4` 6. `LEVEL_HEVC_MAIN_4_1` 7. `LEVEL_HEVC_MAIN_5` 8. `LEVEL_HEVC_MAIN_5_1` 9. `LEVEL_HEVC_MAIN_5_2` | All bits of 3rd byte of data modulus 10 |
+| `bitrate` | In the range `0` to `500000000` | All bits of 4th and 5th byte of data |
+
+This also ensures that the plugin is always deterministic for any given input.
+##### Maximize utilization of input data
+The plugin feeds the entire input data to the transcoder.
+This ensures that the plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesnt `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build media_transcoder_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+ $ mm -j$(nproc) media_transcoder_fuzzer
+```
+#### Steps to run
+Create a directory CORPUS_DIR
+```
+ $ adb shell mkdir CORPUS_DIR
+```
+To run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/${TARGET_ARCH}/media_transcoder_fuzzer/media_transcoder_fuzzer CORPUS_DIR
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/media/libmediatranscoding/transcoder/tests/fuzzer/media_transcoder_fuzzer.cpp b/media/libmediatranscoding/transcoder/tests/fuzzer/media_transcoder_fuzzer.cpp
new file mode 100644
index 0000000..ec36c0f
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/tests/fuzzer/media_transcoder_fuzzer.cpp
@@ -0,0 +1,211 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+#include <C2Config.h>
+#include <android/binder_process.h>
+#include <fcntl.h>
+#include <media/MediaTranscoder.h>
+#include <media/NdkCommon.h>
+#include <stdio.h>
+
+#define UNUSED_PARAM __attribute__((unused))
+#define SRC_FILE "sourceTranscodingFile"
+#define DEST_FILE "destTranscodingFile"
+
+using namespace std;
+using namespace android;
+
+const char* kMimeType[] = {AMEDIA_MIMETYPE_VIDEO_AVC, AMEDIA_MIMETYPE_VIDEO_HEVC};
+const C2Config::profile_t kAvcProfile[] = {C2Config::PROFILE_AVC_BASELINE,
+ C2Config::PROFILE_AVC_CONSTRAINED_BASELINE,
+ C2Config::PROFILE_AVC_MAIN};
+const C2Config::level_t kAvcLevel[] = {
+ C2Config::LEVEL_AVC_1, C2Config::LEVEL_AVC_1B, C2Config::LEVEL_AVC_1_1,
+ C2Config::LEVEL_AVC_1_2, C2Config::LEVEL_AVC_1_3, C2Config::LEVEL_AVC_2,
+ C2Config::LEVEL_AVC_2_1, C2Config::LEVEL_AVC_2_2, C2Config::LEVEL_AVC_3,
+ C2Config::LEVEL_AVC_3_1, C2Config::LEVEL_AVC_3_2, C2Config::LEVEL_AVC_4,
+ C2Config::LEVEL_AVC_4_1, C2Config::LEVEL_AVC_4_2, C2Config::LEVEL_AVC_5,
+};
+const C2Config::profile_t kHevcProfile[] = {C2Config::PROFILE_HEVC_MAIN,
+ C2Config::PROFILE_HEVC_MAIN_STILL};
+const C2Config::level_t kHevcLevel[] = {
+ C2Config::LEVEL_HEVC_MAIN_1, C2Config::LEVEL_HEVC_MAIN_2, C2Config::LEVEL_HEVC_MAIN_2_1,
+ C2Config::LEVEL_HEVC_MAIN_3, C2Config::LEVEL_HEVC_MAIN_3_1, C2Config::LEVEL_HEVC_MAIN_4,
+ C2Config::LEVEL_HEVC_MAIN_4_1, C2Config::LEVEL_HEVC_MAIN_5, C2Config::LEVEL_HEVC_MAIN_5_1,
+ C2Config::LEVEL_HEVC_MAIN_5_2};
+const size_t kNumAvcProfile = size(kAvcProfile);
+const size_t kNumAvcLevel = size(kAvcLevel);
+const size_t kNumHevcProfile = size(kHevcProfile);
+const size_t kNumHevcLevel = size(kHevcLevel);
+const size_t kMaxBitrate = 500000000;
+
+enum {
+ IDX_MIME_TYPE = 0,
+ IDX_PROFILE,
+ IDX_LEVEL,
+ IDX_BITRATE_BYTE_1,
+ IDX_BITRATE_BYTE_2,
+ IDX_LAST
+};
+
+class TestCallbacks : public MediaTranscoder::CallbackInterface {
+public:
+ virtual void onFinished(const MediaTranscoder* transcoder UNUSED_PARAM) override {
+ unique_lock<mutex> lock(mMutex);
+ mFinished = true;
+ mCondition.notify_all();
+ }
+
+ virtual void onError(const MediaTranscoder* transcoder UNUSED_PARAM,
+ media_status_t error UNUSED_PARAM) override {
+ unique_lock<mutex> lock(mMutex);
+ mFinished = true;
+ mCondition.notify_all();
+ }
+
+ virtual void onProgressUpdate(const MediaTranscoder* transcoder UNUSED_PARAM,
+ int32_t progress) override {
+ unique_lock<mutex> lock(mMutex);
+ if (progress > 0 && !mProgressMade) {
+ mProgressMade = true;
+ mCondition.notify_all();
+ }
+ }
+
+ virtual void onHeartBeat(const MediaTranscoder* transcoder UNUSED_PARAM) override {}
+
+ virtual void onCodecResourceLost(const MediaTranscoder* transcoder UNUSED_PARAM,
+ const shared_ptr<ndk::ScopedAParcel>& pausedState
+ UNUSED_PARAM) override {}
+
+ void waitForTranscodingFinished() {
+ unique_lock<mutex> lock(mMutex);
+ while (!mFinished) {
+ mCondition.wait(lock);
+ }
+ }
+
+private:
+ mutex mMutex;
+ condition_variable mCondition;
+ bool mFinished = false;
+ bool mProgressMade = false;
+};
+
+class MediaTranscoderFuzzer {
+public:
+ void init();
+ void invokeTranscoder(const uint8_t* data, size_t size);
+ void deInit();
+
+private:
+ AMediaFormat* getFormat(AMediaFormat* sourceFormat) {
+ AMediaFormat* format = nullptr;
+ const char* mime = nullptr;
+ AMediaFormat_getString(sourceFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+ if (mime != nullptr) {
+ if (strncmp(mime, "video/", 6) == 0 && (mDestMime != nullptr)) {
+ format = AMediaFormat_new();
+ AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, mDestMime);
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_PROFILE, mProfile);
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_LEVEL, mLevel);
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, mBitrate);
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_OPERATING_RATE, INT32_MAX);
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_PRIORITY, 1);
+ }
+ }
+ return format;
+ }
+
+ shared_ptr<TestCallbacks> mCallbacks;
+ int mSrcFd = 0;
+ int mDestFd = 0;
+ const char* mDestMime;
+ C2Config::profile_t mProfile;
+ C2Config::level_t mLevel;
+ uint64_t mBitrate = 0;
+};
+
+void MediaTranscoderFuzzer::init() {
+ mCallbacks = make_shared<TestCallbacks>();
+ ABinderProcess_startThreadPool();
+}
+
+void MediaTranscoderFuzzer::deInit() {
+ mCallbacks.reset();
+ if (mSrcFd) {
+ close(mSrcFd);
+ }
+ if (mDestFd) {
+ close(mDestFd);
+ }
+}
+
+void MediaTranscoderFuzzer::invokeTranscoder(const uint8_t* data, size_t size) {
+ auto transcoder = MediaTranscoder::create(mCallbacks);
+ if (transcoder == nullptr) {
+ return;
+ }
+
+ mDestMime = kMimeType[data[IDX_MIME_TYPE] & 0x01];
+ mBitrate = (((data[IDX_BITRATE_BYTE_1] << 8) | data[IDX_BITRATE_BYTE_2]) * 1000) % kMaxBitrate;
+ if (mDestMime == AMEDIA_MIMETYPE_VIDEO_AVC) {
+ mProfile = kAvcProfile[data[IDX_PROFILE] % kNumAvcProfile];
+ mLevel = kAvcLevel[data[IDX_LEVEL] % kNumAvcLevel];
+ } else {
+ mProfile = kHevcProfile[data[IDX_PROFILE] % kNumHevcProfile];
+ mLevel = kHevcLevel[data[IDX_LEVEL] % kNumHevcLevel];
+ }
+
+ data += IDX_LAST;
+ size -= IDX_LAST;
+
+ mSrcFd = memfd_create(SRC_FILE, MFD_ALLOW_SEALING);
+ write(mSrcFd, data, size);
+
+ transcoder->configureSource(mSrcFd);
+ vector<shared_ptr<AMediaFormat>> trackFormats = transcoder->getTrackFormats();
+ for (int i = 0; i < trackFormats.size(); ++i) {
+ AMediaFormat* format = getFormat(trackFormats[i].get());
+ transcoder->configureTrackFormat(i, format);
+
+ if (format != nullptr) {
+ AMediaFormat_delete(format);
+ }
+ }
+ mDestFd = memfd_create(DEST_FILE, MFD_ALLOW_SEALING);
+ transcoder->configureDestination(mDestFd);
+ if (transcoder->start() == AMEDIA_OK) {
+ mCallbacks->waitForTranscodingFinished();
+ transcoder->cancel();
+ }
+ close(mSrcFd);
+ close(mDestFd);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (size < IDX_LAST + 1) {
+ return 0;
+ }
+ MediaTranscoderFuzzer transcoderFuzzer;
+ transcoderFuzzer.init();
+ transcoderFuzzer.invokeTranscoder(data, size);
+ transcoderFuzzer.deInit();
+ return 0;
+}
diff --git a/media/libmediatranscoding/transcoder/tools/Android.bp b/media/libmediatranscoding/transcoder/tools/Android.bp
new file mode 100644
index 0000000..ba30d34
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/tools/Android.bp
@@ -0,0 +1,33 @@
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_binary {
+ name: "transcode",
+ srcs: ["Transcode.cpp"],
+
+ shared_libs: [
+ "libmediandk",
+ "libmediatranscoder",
+ ],
+
+ header_libs: [
+ "libbase_headers",
+ ],
+
+ compile_multilib: "32",
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ cfi: true,
+ },
+}
diff --git a/media/libmediatranscoding/transcoder/tools/Transcode.cpp b/media/libmediatranscoding/transcoder/tools/Transcode.cpp
new file mode 100644
index 0000000..1f5649e
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/tools/Transcode.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/macros.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <media/MediaTranscoder.h>
+#include <media/NdkCommon.h>
+
+using namespace android;
+
+#define ERR_MSG(fmt, ...) fprintf(stderr, "Error: " fmt "\n", ##__VA_ARGS__)
+
+class TranscoderCallbacks : public MediaTranscoder::CallbackInterface {
+public:
+ media_status_t waitForTranscodingFinished() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ while (!mFinished) {
+ mCondition.wait(lock);
+ }
+ return mStatus;
+ }
+
+private:
+ virtual void onFinished(const MediaTranscoder* /*transcoder*/) override {
+ notifyTranscoderFinished(AMEDIA_OK);
+ }
+
+ virtual void onError(const MediaTranscoder* /*transcoder*/, media_status_t error) override {
+ ERR_MSG("Transcoder failed with error %d", error);
+ notifyTranscoderFinished(error);
+ }
+
+ virtual void onProgressUpdate(const MediaTranscoder* /*transcoder*/,
+ int32_t /*progress*/) override {}
+
+ virtual void onCodecResourceLost(
+ const MediaTranscoder* /*transcoder*/,
+ const std::shared_ptr<ndk::ScopedAParcel>& /*pausedState*/) override {
+ ERR_MSG("Transcoder lost codec resource while transcoding");
+ notifyTranscoderFinished(AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE);
+ }
+
+ virtual void onHeartBeat(const MediaTranscoder* /*transcoder*/) override {}
+
+ void notifyTranscoderFinished(media_status_t status) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mFinished = true;
+ mStatus = status;
+ mCondition.notify_all();
+ }
+
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ bool mFinished = false;
+ media_status_t mStatus = AMEDIA_OK;
+};
+
+struct TranscodeConfig {
+ std::string srcFile;
+ std::string dstFile;
+
+ std::string dstCodec{AMEDIA_MIMETYPE_VIDEO_AVC};
+ int32_t bitrate = -1;
+};
+
+static int transcode(const struct TranscodeConfig& config) {
+ auto callbacks = std::make_shared<TranscoderCallbacks>();
+ auto transcoder = MediaTranscoder::create(callbacks, -1 /*heartBeatIntervalUs*/);
+
+ const int srcFd = open(config.srcFile.c_str(), O_RDONLY);
+ if (srcFd <= 0) {
+ ERR_MSG("Unable to open source file %s", config.srcFile.c_str());
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ media_status_t status = transcoder->configureSource(srcFd);
+ close(srcFd);
+ if (status != AMEDIA_OK) {
+ ERR_MSG("configureSource returned error %d", status);
+ return status;
+ }
+
+ std::vector<std::shared_ptr<AMediaFormat>> trackFormats = transcoder->getTrackFormats();
+ if (trackFormats.size() <= 0) {
+ ERR_MSG("No tracks found in source file");
+ return AMEDIA_ERROR_MALFORMED;
+ }
+
+ for (int i = 0; i < trackFormats.size(); ++i) {
+ AMediaFormat* dstFormat = nullptr;
+
+ const char* mime = nullptr;
+ AMediaFormat_getString(trackFormats[i].get(), AMEDIAFORMAT_KEY_MIME, &mime);
+
+ if (strncmp(mime, "video/", 6) == 0) {
+ dstFormat = AMediaFormat_new();
+ AMediaFormat_setString(dstFormat, AMEDIAFORMAT_KEY_MIME, config.dstCodec.c_str());
+
+ if (config.bitrate > 0) {
+ AMediaFormat_setInt32(dstFormat, AMEDIAFORMAT_KEY_BIT_RATE, config.bitrate);
+ }
+ }
+
+ status = transcoder->configureTrackFormat(i, dstFormat);
+
+ if (dstFormat != nullptr) {
+ AMediaFormat_delete(dstFormat);
+ }
+
+ if (status != AMEDIA_OK) {
+ ERR_MSG("configureTrack returned error %d", status);
+ return status;
+ }
+ }
+
+ // Note: Overwrites existing file.
+ const int dstFd = open(config.dstFile.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+ if (dstFd <= 0) {
+ ERR_MSG("Unable to open destination file %s", config.dstFile.c_str());
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ status = transcoder->configureDestination(dstFd);
+ close(dstFd);
+ if (status != AMEDIA_OK) {
+ ERR_MSG("configureDestination returned error %d", status);
+ return status;
+ }
+
+ status = transcoder->start();
+ if (status != AMEDIA_OK) {
+ ERR_MSG("start returned error %d", status);
+ return status;
+ }
+
+ return callbacks->waitForTranscodingFinished();
+}
+
+// Options.
+static const struct option kLongOpts[] = {{"help", no_argument, nullptr, 'h'},
+ {"codec", required_argument, nullptr, 'c'},
+ {"bitrate", required_argument, nullptr, 'b'},
+ {0, 0, 0, 0}};
+static const char kShortOpts[] = "hc:b:";
+
+static void printUsageAndExit() {
+ const char* usage =
+ " -h / --help : Print this usage message and exit.\n"
+ " -c / --codec : Specify output video codec type using MediaFormat codec mime "
+ "type.\n"
+ " Defaults to \"video/avc\".\n"
+ " -b / --bitrate : Specify output video bitrate in bits per second.\n"
+ " Defaults to estimating and preserving the original bitrate.\n"
+ "";
+
+ printf("Usage: %s [-h] [-c CODEC] <srcfile> <dstfile>\n%s", getprogname(), usage);
+ exit(-1);
+}
+
+int main(int argc, char** argv) {
+ int c;
+ TranscodeConfig config;
+
+ while ((c = getopt_long(argc, argv, kShortOpts, kLongOpts, nullptr)) >= 0) {
+ switch (c) {
+ case 'c':
+ config.dstCodec.assign(optarg);
+ break;
+
+ case 'b':
+ config.bitrate = atoi(optarg);
+ if (config.bitrate <= 0) {
+ ERR_MSG("Bitrate must an integer larger than zero.");
+ printUsageAndExit();
+ }
+ break;
+
+ case '?':
+ FALLTHROUGH_INTENDED;
+ case 'h':
+ FALLTHROUGH_INTENDED;
+ default:
+ printUsageAndExit();
+ break;
+ }
+ }
+
+ if (optind > (argc - 2)) {
+ ERR_MSG("Source and destination file not specified");
+ printUsageAndExit();
+ }
+ config.srcFile.assign(argv[optind++]);
+ config.dstFile.assign(argv[optind]);
+
+ return transcode(config);
+}
diff --git a/media/libshmem/Android.bp b/media/libshmem/Android.bp
new file mode 100644
index 0000000..6e48078
--- /dev/null
+++ b/media/libshmem/Android.bp
@@ -0,0 +1,79 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+aidl_interface {
+ name: "shared-file-region-aidl",
+ unstable: true,
+ host_supported: true,
+ vendor_available: true,
+ double_loadable: true,
+ local_include_dir: "aidl",
+ srcs: [
+ "aidl/android/media/SharedFileRegion.aidl",
+ ],
+}
+
+cc_library {
+ name: "libshmemcompat",
+ export_include_dirs: ["include"],
+ srcs: ["ShmemCompat.cpp"],
+ host_supported: true,
+ vendor_available: true,
+ double_loadable: true,
+ shared_libs: [
+ "libbinder",
+ "libshmemutil",
+ "libutils",
+ "shared-file-region-aidl-cpp",
+ ],
+ export_shared_lib_headers: [
+ "libbinder",
+ "libutils",
+ "shared-file-region-aidl-cpp",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
+
+cc_library {
+ name: "libshmemutil",
+ export_include_dirs: ["include"],
+ srcs: ["ShmemUtil.cpp"],
+ host_supported: true,
+ vendor_available: true,
+ double_loadable: true,
+ shared_libs: [
+ "shared-file-region-aidl-cpp",
+ ],
+ export_shared_lib_headers: [
+ "shared-file-region-aidl-cpp",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
+
+cc_test {
+ name: "shmemTest",
+ srcs: ["ShmemTest.cpp"],
+ shared_libs: [
+ "libbinder",
+ "libcutils",
+ "libshmemcompat",
+ "libshmemutil",
+ "libutils",
+ "shared-file-region-aidl-cpp",
+ ],
+ test_suites: ["device-tests"],
+}
diff --git a/media/libshmem/OWNERS b/media/libshmem/OWNERS
new file mode 100644
index 0000000..29fa2f5
--- /dev/null
+++ b/media/libshmem/OWNERS
@@ -0,0 +1,3 @@
+ytai@google.com
+mnaganov@google.com
+elaurent@google.com
diff --git a/media/libshmem/README.md b/media/libshmem/README.md
new file mode 100644
index 0000000..c25fa7f
--- /dev/null
+++ b/media/libshmem/README.md
@@ -0,0 +1,6 @@
+# libshmem
+
+This library provides facilities for sharing memory across processes over (stable) AIDL. The main
+feature is the definition of the `android.media.SharedMemory` AIDL type, which represents a block of
+memory that can be shared between processes. In addition, a few utilities are provided to facilitate
+the use of shared memory and to integrate with legacy code that uses older facilities.
\ No newline at end of file
diff --git a/media/libshmem/ShmemCompat.cpp b/media/libshmem/ShmemCompat.cpp
new file mode 100644
index 0000000..246cb24
--- /dev/null
+++ b/media/libshmem/ShmemCompat.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "media/ShmemCompat.h"
+
+#include "binder/MemoryBase.h"
+#include "binder/MemoryHeapBase.h"
+#include "media/ShmemUtil.h"
+
+namespace android {
+namespace media {
+
+bool convertSharedFileRegionToIMemory(const SharedFileRegion& shmem,
+ sp<IMemory>* result) {
+ assert(result != nullptr);
+
+ if (!validateSharedFileRegion(shmem)) {
+ return false;
+ }
+
+ // Heap offset and size must be page aligned.
+ const size_t pageSize = getpagesize();
+ const size_t pageMask = ~(pageSize - 1);
+
+ // OK if this wraps.
+ const uint64_t endOffset = static_cast<uint64_t>(shmem.offset) +
+ static_cast<uint64_t>(shmem.size);
+
+ // Round down to page boundary.
+ const uint64_t heapStartOffset = shmem.offset & pageMask;
+ // Round up to page boundary.
+ const uint64_t heapEndOffset = (endOffset + pageSize - 1) & pageMask;
+ const uint64_t heapSize = heapEndOffset - heapStartOffset;
+
+ if (heapStartOffset > std::numeric_limits<size_t>::max() ||
+ heapSize > std::numeric_limits<size_t>::max()) {
+ return false;
+ }
+
+ uint32_t flags = !shmem.writeable ? IMemoryHeap::READ_ONLY : 0;
+
+ const sp<MemoryHeapBase> heap =
+ new MemoryHeapBase(shmem.fd.get(), heapSize, flags, heapStartOffset);
+ *result = sp<MemoryBase>::make(heap,
+ shmem.offset - heapStartOffset,
+ shmem.size);
+ return true;
+}
+
+bool convertIMemoryToSharedFileRegion(const sp<IMemory>& mem,
+ SharedFileRegion* result) {
+ assert(mem != nullptr);
+ assert(result != nullptr);
+
+ *result = SharedFileRegion();
+
+ ssize_t offset;
+ size_t size;
+
+ sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
+ if (size > 0) {
+ if (heap == nullptr) {
+ return false;
+ }
+ // Make sure the offset and size do not overflow from int64 boundaries.
+ if (size > std::numeric_limits<int64_t>::max() ||
+ offset > std::numeric_limits<int64_t>::max() ||
+ heap->getOffset() > std::numeric_limits<int64_t>::max() ||
+ static_cast<uint64_t>(heap->getOffset()) +
+ static_cast<uint64_t>(offset)
+ > std::numeric_limits<int64_t>::max()) {
+ return false;
+ }
+
+ const int fd = fcntl(heap->getHeapID(), F_DUPFD_CLOEXEC, 0);
+ if (fd < 0) {
+ return false;
+ }
+ result->fd.reset(base::unique_fd(fd));
+ result->size = size;
+ result->offset = heap->getOffset() + offset;
+ result->writeable = (heap->getFlags() & IMemoryHeap::READ_ONLY) == 0;
+ }
+ return true;
+}
+
+bool convertNullableSharedFileRegionToIMemory(const std::optional<SharedFileRegion>& shmem,
+ sp<IMemory>* result) {
+ assert(result != nullptr);
+
+ if (!shmem.has_value()) {
+ result->clear();
+ return true;
+ }
+
+ return convertSharedFileRegionToIMemory(shmem.value(), result);
+}
+
+bool convertNullableIMemoryToSharedFileRegion(const sp<IMemory>& mem,
+ std::optional<SharedFileRegion>* result) {
+ assert(result != nullptr);
+
+ if (mem == nullptr) {
+ result->reset();
+ return true;
+ }
+
+ result->emplace();
+ return convertIMemoryToSharedFileRegion(mem, &result->value());
+}
+
+} // namespace media
+} // namespace android
diff --git a/media/libshmem/ShmemTest.cpp b/media/libshmem/ShmemTest.cpp
new file mode 100644
index 0000000..874f34c
--- /dev/null
+++ b/media/libshmem/ShmemTest.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <gtest/gtest.h>
+
+#include "binder/MemoryBase.h"
+#include "binder/MemoryHeapBase.h"
+#include "cutils/ashmem.h"
+#include "media/ShmemCompat.h"
+#include "media/ShmemUtil.h"
+
+namespace android {
+namespace media {
+namespace {
+
+// Creates a SharedFileRegion instance.
+SharedFileRegion makeSharedFileRegion(int64_t offset, int64_t size) {
+ SharedFileRegion shmem;
+ shmem.offset = offset;
+ shmem.size = size;
+ int fd = ashmem_create_region("", size + offset);
+ assert(fd >= 0);
+ shmem.fd = os::ParcelFileDescriptor(base::unique_fd(fd));
+ return shmem;
+}
+
+// Creates a SharedFileRegion instance with an invalid FD.
+SharedFileRegion makeInvalidSharedFileRegion(int64_t offset, int64_t size) {
+ SharedFileRegion shmem;
+ shmem.offset = offset;
+ shmem.size = size;
+ return shmem;
+}
+
+sp<IMemory> makeIMemory(const std::vector<uint8_t>& content, bool writeable = true) {
+ constexpr size_t kOffset = 19;
+
+ sp<MemoryHeapBase> heap = new MemoryHeapBase(content.size(),
+ !writeable ? IMemoryHeap::READ_ONLY : 0);
+ sp<IMemory> result = sp<MemoryBase>::make(heap, kOffset, content.size());
+ memcpy(result->unsecurePointer(), content.data(), content.size());
+ return result;
+}
+
+TEST(ShmemTest, Validate) {
+ EXPECT_TRUE(validateSharedFileRegion(makeSharedFileRegion(0, 0)));
+ EXPECT_TRUE(validateSharedFileRegion(makeSharedFileRegion(1, 2)));
+ EXPECT_FALSE(validateSharedFileRegion(makeSharedFileRegion(-1, 2)));
+ EXPECT_FALSE(validateSharedFileRegion(makeSharedFileRegion(2, -1)));
+ EXPECT_FALSE(validateSharedFileRegion(makeInvalidSharedFileRegion(1, 2)));
+}
+
+TEST(ShmemTest, Conversion) {
+ sp<IMemory> reconstructed;
+ {
+ SharedFileRegion shmem;
+ sp<IMemory> imem = makeIMemory({6, 5, 3});
+ ASSERT_TRUE(convertIMemoryToSharedFileRegion(imem, &shmem));
+ ASSERT_EQ(3, shmem.size);
+ ASSERT_GE(shmem.fd.get(), 0);
+ ASSERT_TRUE(shmem.writeable);
+ ASSERT_TRUE(convertSharedFileRegionToIMemory(shmem, &reconstructed));
+ }
+ ASSERT_EQ(3, reconstructed->size());
+ ASSERT_EQ(reconstructed->getMemory()->getFlags() & IMemoryHeap::READ_ONLY, 0);
+ const uint8_t* p =
+ reinterpret_cast<const uint8_t*>(reconstructed->unsecurePointer());
+ EXPECT_EQ(6, p[0]);
+ EXPECT_EQ(5, p[1]);
+ EXPECT_EQ(3, p[2]);
+}
+
+TEST(ShmemTest, ConversionReadOnly) {
+ sp<IMemory> reconstructed;
+ {
+ SharedFileRegion shmem;
+ sp<IMemory> imem = makeIMemory({6, 5, 3}, false);
+ ASSERT_TRUE(convertIMemoryToSharedFileRegion(imem, &shmem));
+ ASSERT_EQ(3, shmem.size);
+ ASSERT_GE(shmem.fd.get(), 0);
+ ASSERT_FALSE(shmem.writeable);
+ ASSERT_TRUE(convertSharedFileRegionToIMemory(shmem, &reconstructed));
+ }
+ ASSERT_EQ(3, reconstructed->size());
+ ASSERT_NE(reconstructed->getMemory()->getFlags() & IMemoryHeap::READ_ONLY, 0);
+ const uint8_t* p =
+ reinterpret_cast<const uint8_t*>(reconstructed->unsecurePointer());
+ EXPECT_EQ(6, p[0]);
+ EXPECT_EQ(5, p[1]);
+ EXPECT_EQ(3, p[2]);
+}
+
+TEST(ShmemTest, NullConversion) {
+ sp<IMemory> reconstructed;
+ {
+ std::optional<SharedFileRegion> shmem;
+ sp<IMemory> imem;
+ ASSERT_TRUE(convertNullableIMemoryToSharedFileRegion(imem, &shmem));
+ ASSERT_FALSE(shmem.has_value());
+ ASSERT_TRUE(convertNullableSharedFileRegionToIMemory(shmem, &reconstructed));
+ }
+ ASSERT_EQ(nullptr, reconstructed);
+}
+
+} // namespace
+} // namespace media
+} // namespace android
diff --git a/media/libshmem/ShmemUtil.cpp b/media/libshmem/ShmemUtil.cpp
new file mode 100644
index 0000000..e075346
--- /dev/null
+++ b/media/libshmem/ShmemUtil.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "media/ShmemUtil.h"
+
+namespace android {
+namespace media {
+
+bool validateSharedFileRegion(const SharedFileRegion& shmem) {
+ // FD must be valid.
+ if (shmem.fd.get() < 0) {
+ return false;
+ }
+
+ // Size and offset must be non-negative.
+ if (shmem.size < 0 || shmem.offset < 0) {
+ return false;
+ }
+
+ uint64_t size = shmem.size;
+ uint64_t offset = shmem.offset;
+
+ // Must not wrap.
+ if (offset > offset + size) {
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace media
+} // namespace android
diff --git a/media/libshmem/aidl/android/media/SharedFileRegion.aidl b/media/libshmem/aidl/android/media/SharedFileRegion.aidl
new file mode 100644
index 0000000..199b647
--- /dev/null
+++ b/media/libshmem/aidl/android/media/SharedFileRegion.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * A shared file region.
+ *
+ * This type contains the required information to share a region of a file between processes over
+ * AIDL.
+ * An instance of this type represents a valid FD. For representing a null SharedFileRegion, use a
+ * @nullable SharedFileRegion.
+ * Primarily, this is intended for shared memory blocks.
+ *
+ * @hide
+ */
+parcelable SharedFileRegion {
+ /** File descriptor of the region. Must be valid. */
+ ParcelFileDescriptor fd;
+ /** Offset, in bytes within the file of the start of the region. Must be non-negative. */
+ long offset;
+ /** Size, in bytes of the memory region. Must be non-negative. */
+ long size;
+ /** Whether the region is writeable. */
+ boolean writeable;
+}
diff --git a/media/libshmem/include/media/ShmemCompat.h b/media/libshmem/include/media/ShmemCompat.h
new file mode 100644
index 0000000..ba59f25
--- /dev/null
+++ b/media/libshmem/include/media/ShmemCompat.h
@@ -0,0 +1,74 @@
+/*
+ * 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
+
+// This module contains utilities for interfacing between legacy code that is using IMemory and new
+// code that is using android.os.SharedFileRegion.
+
+#include <optional>
+
+#include "android/media/SharedFileRegion.h"
+#include "binder/IMemory.h"
+#include "utils/StrongPointer.h"
+
+namespace android {
+namespace media {
+
+/**
+ * Converts a SharedFileRegion parcelable to an IMemory instance.
+ * @param shmem The SharedFileRegion instance.
+ * @param result The resulting IMemory instance. May not be null.
+ * @return true if the conversion is successful (should always succeed under normal circumstances,
+ * failure usually means corrupt data).
+ */
+bool convertSharedFileRegionToIMemory(const SharedFileRegion& shmem,
+ sp<IMemory>* result);
+
+/**
+ * Converts a nullable SharedFileRegion parcelable to an IMemory instance.
+ * @param shmem The SharedFileRegion instance.
+ * @param result The resulting IMemory instance. May not be null. Pointee assigned to null,
+ * if the input is null.
+ * @return true if the conversion is successful (should always succeed under normal circumstances,
+ * failure usually means corrupt data).
+ */
+bool convertNullableSharedFileRegionToIMemory(const std::optional<SharedFileRegion>& shmem,
+ sp<IMemory>* result);
+
+/**
+ * Converts an IMemory instance to SharedFileRegion.
+ * @param mem The IMemory instance. May not be null.
+ * @param result The resulting SharedFileRegion instance.
+ * @return true if the conversion is successful (should always succeed under normal circumstances,
+ * failure usually means corrupt data).
+ */
+bool convertIMemoryToSharedFileRegion(const sp<IMemory>& mem,
+ SharedFileRegion* result);
+
+/**
+ * Converts a nullable IMemory instance to a nullable SharedFileRegion.
+ * @param mem The IMemory instance. May be null.
+ * @param result The resulting SharedFileRegion instance. May not be null. Assigned to empty,
+ * if the input is null.
+ * @return true if the conversion is successful (should always succeed under normal circumstances,
+ * failure usually means corrupt data).
+ */
+bool convertNullableIMemoryToSharedFileRegion(const sp<IMemory>& mem,
+ std::optional<SharedFileRegion>* result);
+
+} // namespace media
+} // namespace android
diff --git a/media/libshmem/include/media/ShmemUtil.h b/media/libshmem/include/media/ShmemUtil.h
new file mode 100644
index 0000000..3a7a5a5
--- /dev/null
+++ b/media/libshmem/include/media/ShmemUtil.h
@@ -0,0 +1,32 @@
+/*
+ * 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
+
+// This module contains utilities for working with android.os.SharedFileRegion.
+
+#include "android/media/SharedFileRegion.h"
+
+namespace android {
+namespace media {
+
+/**
+ * Checks whether a SharedFileRegion instance is valid (all the fields have sane values).
+ */
+bool validateSharedFileRegion(const SharedFileRegion& shmem);
+
+} // namespace media
+} // namespace android
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 224ec8b..1aa1848 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -74,6 +74,12 @@
kMaxIndicesToCheck = 32, // used when enumerating supported formats and profiles
};
+namespace {
+
+constexpr char TUNNEL_PEEK_KEY[] = "android._trigger-tunnel-peek";
+
+}
+
// OMX errors are directly mapped into status_t range if
// there is no corresponding MediaError status code.
// Use the statusFromOMXError(int32_t omxError) function.
@@ -1465,6 +1471,10 @@
mCallback->onOutputFramesRendered(done);
}
+void ACodec::onFirstTunnelFrameReady() {
+ mCallback->onFirstTunnelFrameReady();
+}
+
ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() {
ANativeWindowBuffer *buf;
CHECK(mNativeWindow.get() != NULL);
@@ -2460,6 +2470,30 @@
return err;
}
+status_t ACodec::setTunnelPeek(int32_t tunnelPeek) {
+ if (mIsEncoder) {
+ ALOGE("encoder does not support %s", TUNNEL_PEEK_KEY);
+ return BAD_VALUE;
+ }
+ if (!mTunneled) {
+ ALOGE("%s is only supported in tunnel mode", TUNNEL_PEEK_KEY);
+ return BAD_VALUE;
+ }
+
+ OMX_CONFIG_BOOLEANTYPE config;
+ InitOMXParams(&config);
+ config.bEnabled = (OMX_BOOL)(tunnelPeek != 0);
+ status_t err = mOMXNode->setConfig(
+ (OMX_INDEXTYPE)OMX_IndexConfigAndroidTunnelPeek,
+ &config, sizeof(config));
+ if (err != OK) {
+ ALOGE("decoder cannot set %s to %d (err %d)",
+ TUNNEL_PEEK_KEY, tunnelPeek, err);
+ }
+
+ return err;
+}
+
status_t ACodec::setAudioPresentation(int32_t presentationId, int32_t programId) {
OMX_AUDIO_CONFIG_ANDROID_AUDIOPRESENTATION config;
InitOMXParams(&config);
@@ -7893,6 +7927,15 @@
&presentation, sizeof(presentation));
}
}
+
+ int32_t tunnelPeek = 0;
+ if (params->findInt32(TUNNEL_PEEK_KEY, &tunnelPeek)) {
+ status_t err = setTunnelPeek(tunnelPeek);
+ if (err != OK) {
+ return err;
+ }
+ }
+
return setVendorParameters(params);
}
@@ -8358,6 +8401,12 @@
return true;
}
+ case OMX_EventOnFirstTunnelFrameReady:
+ {
+ mCodec->onFirstTunnelFrameReady();
+ return true;
+ }
+
default:
return BaseState::onOMXEvent(event, data1, data2);
}
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 5d959cb..d6e36b9 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -228,7 +228,7 @@
],
static_libs: [
- "librenderengine",
+ "librenderfright",
],
export_include_dirs: [
@@ -274,6 +274,7 @@
"MPEG2TSWriter.cpp",
"MPEG4Writer.cpp",
"MediaAdapter.cpp",
+ "MediaAppender.cpp",
"MediaClock.cpp",
"MediaCodec.cpp",
"MediaCodecList.cpp",
@@ -337,6 +338,8 @@
"android.hardware.cas.native@1.0",
"android.hardware.drm@1.0",
"android.hardware.media.omx@1.0",
+ "media_permission-aidl-cpp",
+ "libaudioclient_aidl_conversion",
],
static_libs: [
@@ -349,6 +352,9 @@
"libogg",
"libwebm",
"libstagefright_id3",
+ "media_permission-aidl-cpp",
+ "libmediandk_format",
+ "libmedia_ndkformatpriv",
],
header_libs:[
@@ -356,6 +362,7 @@
"libnativeloader-headers",
"libstagefright_xmlparser_headers",
"media_ndk_headers",
+ "libmediaformatshaper_headers",
],
export_shared_lib_headers: [
@@ -363,6 +370,7 @@
"libhidlmemory",
"libmedia",
"android.hidl.allocator@1.0",
+ "media_permission-aidl-cpp",
],
export_include_dirs: [
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 4bc861e..89fe56f 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -21,6 +21,8 @@
#define LOG_TAG "AudioSource"
#include <utils/Log.h>
+#include <binder/IPCThreadState.h>
+#include <media/AidlConversion.h>
#include <media/AudioRecord.h>
#include <media/stagefright/AudioSource.h>
#include <media/stagefright/MediaBuffer.h>
@@ -32,6 +34,8 @@
namespace android {
+using android::media::permission::Identity;
+
static void AudioRecordCallbackFunction(int event, void *user, void *info) {
AudioSource *source = (AudioSource *) user;
switch (event) {
@@ -50,74 +54,100 @@
}
AudioSource::AudioSource(
+ const audio_attributes_t *attr, const Identity& identity,
+ uint32_t sampleRate, uint32_t channelCount, uint32_t outSampleRate,
+ audio_port_handle_t selectedDeviceId,
+ audio_microphone_direction_t selectedMicDirection,
+ float selectedMicFieldDimension)
+{
+ set(attr, identity, sampleRate, channelCount, outSampleRate, selectedDeviceId,
+ selectedMicDirection, selectedMicFieldDimension);
+}
+
+AudioSource::AudioSource(
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,
float selectedMicFieldDimension)
- : mStarted(false),
- mSampleRate(sampleRate),
- mOutSampleRate(outSampleRate > 0 ? outSampleRate : sampleRate),
- mTrackMaxAmplitude(false),
- mStartTimeUs(0),
- mStopSystemTimeUs(-1),
- mLastFrameTimestampUs(0),
- mMaxAmplitude(0),
- mPrevSampleTimeUs(0),
- mInitialReadTimeUs(0),
- mNumFramesReceived(0),
- mNumFramesSkipped(0),
- mNumFramesLost(0),
- mNumClientOwnedBuffers(0),
- mNoMoreFramesToRead(false) {
- ALOGV("sampleRate: %u, outSampleRate: %u, channelCount: %u",
- sampleRate, outSampleRate, channelCount);
- CHECK(channelCount == 1 || channelCount == 2);
- CHECK(sampleRate > 0);
+{
+ // TODO b/182392769: use identity util
+ Identity identity;
+ identity.packageName = VALUE_OR_FATAL(legacy2aidl_String16_string(opPackageName));
+ identity.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(uid));
+ identity.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(pid));
+ set(attr, identity, sampleRate, channelCount, outSampleRate, selectedDeviceId,
+ selectedMicDirection, selectedMicFieldDimension);
+}
- size_t minFrameCount;
- status_t status = AudioRecord::getMinFrameCount(&minFrameCount,
- sampleRate,
- AUDIO_FORMAT_PCM_16_BIT,
- audio_channel_in_mask_from_count(channelCount));
- if (status == OK) {
- // make sure that the AudioRecord callback never returns more than the maximum
- // buffer size
- uint32_t frameCount = kMaxBufferSize / sizeof(int16_t) / channelCount;
+void AudioSource::set(
+ const audio_attributes_t *attr, const Identity& identity,
+ uint32_t sampleRate, uint32_t channelCount, uint32_t outSampleRate,
+ audio_port_handle_t selectedDeviceId,
+ audio_microphone_direction_t selectedMicDirection,
+ float selectedMicFieldDimension)
+{
+ mStarted = false;
+ mSampleRate = sampleRate;
+ mOutSampleRate = outSampleRate > 0 ? outSampleRate : sampleRate;
+ mTrackMaxAmplitude = false;
+ mStartTimeUs = 0;
+ mStopSystemTimeUs = -1;
+ mLastFrameTimestampUs = 0;
+ mMaxAmplitude = 0;
+ mPrevSampleTimeUs = 0;
+ mInitialReadTimeUs = 0;
+ mNumFramesReceived = 0;
+ mNumFramesSkipped = 0;
+ mNumFramesLost = 0;
+ mNumClientOwnedBuffers = 0;
+ mNoMoreFramesToRead = false;
+ ALOGV("sampleRate: %u, outSampleRate: %u, channelCount: %u",
+ sampleRate, outSampleRate, channelCount);
+ CHECK(channelCount == 1 || channelCount == 2);
+ CHECK(sampleRate > 0);
- // make sure that the AudioRecord total buffer size is large enough
- size_t bufCount = 2;
- while ((bufCount * frameCount) < minFrameCount) {
- bufCount++;
- }
+ size_t minFrameCount;
+ status_t status = AudioRecord::getMinFrameCount(&minFrameCount,
+ sampleRate,
+ AUDIO_FORMAT_PCM_16_BIT,
+ audio_channel_in_mask_from_count(channelCount));
+ if (status == OK) {
+ // make sure that the AudioRecord callback never returns more than the maximum
+ // buffer size
+ uint32_t frameCount = kMaxBufferSize / sizeof(int16_t) / channelCount;
- mRecord = new AudioRecord(
- AUDIO_SOURCE_DEFAULT, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
- audio_channel_in_mask_from_count(channelCount),
- opPackageName,
- (size_t) (bufCount * frameCount),
- AudioRecordCallbackFunction,
- this,
- frameCount /*notificationFrames*/,
- AUDIO_SESSION_ALLOCATE,
- AudioRecord::TRANSFER_DEFAULT,
- AUDIO_INPUT_FLAG_NONE,
- uid,
- pid,
- attr,
- selectedDeviceId,
- selectedMicDirection,
- selectedMicFieldDimension);
- // Set caller name so it can be logged in destructor.
- // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_MEDIA
- mRecord->setCallerName("media");
- mInitCheck = mRecord->initCheck();
- if (mInitCheck != OK) {
- mRecord.clear();
- }
- } else {
- mInitCheck = status;
+ // make sure that the AudioRecord total buffer size is large enough
+ size_t bufCount = 2;
+ while ((bufCount * frameCount) < minFrameCount) {
+ bufCount++;
}
+
+ mRecord = new AudioRecord(
+ AUDIO_SOURCE_DEFAULT, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
+ audio_channel_in_mask_from_count(channelCount),
+ identity,
+ (size_t) (bufCount * frameCount),
+ AudioRecordCallbackFunction,
+ this,
+ frameCount /*notificationFrames*/,
+ AUDIO_SESSION_ALLOCATE,
+ AudioRecord::TRANSFER_DEFAULT,
+ AUDIO_INPUT_FLAG_NONE,
+ attr,
+ selectedDeviceId,
+ selectedMicDirection,
+ selectedMicFieldDimension);
+ // Set caller name so it can be logged in destructor.
+ // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_MEDIA
+ mRecord->setCallerName("media");
+ mInitCheck = mRecord->initCheck();
+ if (mInitCheck != OK) {
+ mRecord.clear();
+ }
+ } else {
+ mInitCheck = status;
+ }
}
AudioSource::~AudioSource() {
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 4e34a26..b1aa7a9 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -46,88 +46,6 @@
static const int64_t CAMERA_SOURCE_TIMEOUT_NS = 3000000000LL;
-struct CameraSourceListener : public CameraListener {
- explicit CameraSourceListener(const sp<CameraSource> &source);
-
- virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2);
- virtual void postData(int32_t msgType, const sp<IMemory> &dataPtr,
- camera_frame_metadata_t *metadata);
-
- virtual void postDataTimestamp(
- nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr);
-
- virtual void postRecordingFrameHandleTimestamp(nsecs_t timestamp, native_handle_t* handle);
-
- virtual void postRecordingFrameHandleTimestampBatch(
- const std::vector<nsecs_t>& timestamps,
- const std::vector<native_handle_t*>& handles);
-
-protected:
- virtual ~CameraSourceListener();
-
-private:
- wp<CameraSource> mSource;
-
- CameraSourceListener(const CameraSourceListener &);
- CameraSourceListener &operator=(const CameraSourceListener &);
-};
-
-CameraSourceListener::CameraSourceListener(const sp<CameraSource> &source)
- : mSource(source) {
-}
-
-CameraSourceListener::~CameraSourceListener() {
-}
-
-void CameraSourceListener::notify(int32_t msgType, int32_t ext1, int32_t ext2) {
- UNUSED_UNLESS_VERBOSE(msgType);
- UNUSED_UNLESS_VERBOSE(ext1);
- UNUSED_UNLESS_VERBOSE(ext2);
- ALOGV("notify(%d, %d, %d)", msgType, ext1, ext2);
-}
-
-void CameraSourceListener::postData(int32_t msgType, const sp<IMemory> &dataPtr,
- camera_frame_metadata_t * /* metadata */) {
- ALOGV("postData(%d, ptr:%p, size:%zu)",
- msgType, dataPtr->unsecurePointer(), dataPtr->size());
-
- sp<CameraSource> source = mSource.promote();
- if (source.get() != NULL) {
- source->dataCallback(msgType, dataPtr);
- }
-}
-
-void CameraSourceListener::postDataTimestamp(
- nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr) {
-
- sp<CameraSource> source = mSource.promote();
- if (source.get() != NULL) {
- source->dataCallbackTimestamp(timestamp/1000, msgType, dataPtr);
- }
-}
-
-void CameraSourceListener::postRecordingFrameHandleTimestamp(nsecs_t timestamp,
- native_handle_t* handle) {
- sp<CameraSource> source = mSource.promote();
- if (source.get() != nullptr) {
- source->recordingFrameHandleCallbackTimestamp(timestamp/1000, handle);
- }
-}
-
-void CameraSourceListener::postRecordingFrameHandleTimestampBatch(
- const std::vector<nsecs_t>& timestamps,
- const std::vector<native_handle_t*>& handles) {
- sp<CameraSource> source = mSource.promote();
- if (source.get() != nullptr) {
- int n = timestamps.size();
- std::vector<nsecs_t> modifiedTimestamps(n);
- for (int i = 0; i < n; i++) {
- modifiedTimestamps[i] = timestamps[i] / 1000;
- }
- source->recordingFrameHandleCallbackTimestampBatch(modifiedTimestamps, handles);
- }
-}
-
static int32_t getColorFormat(const char* colorFormat) {
if (!colorFormat) {
ALOGE("Invalid color format");
@@ -169,16 +87,6 @@
return -1;
}
-CameraSource *CameraSource::Create(const String16 &clientName) {
- Size size;
- size.width = -1;
- size.height = -1;
-
- sp<hardware::ICamera> camera;
- return new CameraSource(camera, NULL, 0, clientName, Camera::USE_CALLING_UID,
- Camera::USE_CALLING_PID, size, -1, NULL, false);
-}
-
// static
CameraSource *CameraSource::CreateFromCamera(
const sp<hardware::ICamera>& camera,
@@ -189,12 +97,10 @@
pid_t clientPid,
Size videoSize,
int32_t frameRate,
- const sp<IGraphicBufferProducer>& surface,
- bool storeMetaDataInVideoBuffers) {
+ const sp<IGraphicBufferProducer>& surface) {
CameraSource *source = new CameraSource(camera, proxy, cameraId,
- clientName, clientUid, clientPid, videoSize, frameRate, surface,
- storeMetaDataInVideoBuffers);
+ clientName, clientUid, clientPid, videoSize, frameRate, surface);
return source;
}
@@ -207,8 +113,7 @@
pid_t clientPid,
Size videoSize,
int32_t frameRate,
- const sp<IGraphicBufferProducer>& surface,
- bool storeMetaDataInVideoBuffers)
+ const sp<IGraphicBufferProducer>& surface)
: mCameraFlags(0),
mNumInputBuffers(0),
mVideoFrameRate(-1),
@@ -231,8 +136,7 @@
mInitCheck = init(camera, proxy, cameraId,
clientName, clientUid, clientPid,
- videoSize, frameRate,
- storeMetaDataInVideoBuffers);
+ videoSize, frameRate);
if (mInitCheck != OK) releaseCamera();
}
@@ -531,15 +435,13 @@
uid_t clientUid,
pid_t clientPid,
Size videoSize,
- int32_t frameRate,
- bool storeMetaDataInVideoBuffers) {
+ int32_t frameRate) {
ALOGV("init");
status_t err = OK;
int64_t token = IPCThreadState::self()->clearCallingIdentity();
err = initWithCameraAccess(camera, proxy, cameraId, clientName, clientUid, clientPid,
- videoSize, frameRate,
- storeMetaDataInVideoBuffers);
+ videoSize, frameRate);
IPCThreadState::self()->restoreCallingIdentity(token);
return err;
}
@@ -626,8 +528,7 @@
uid_t clientUid,
pid_t clientPid,
Size videoSize,
- int32_t frameRate,
- bool storeMetaDataInVideoBuffers) {
+ int32_t frameRate) {
ALOGV("initWithCameraAccess");
status_t err = OK;
@@ -667,24 +568,12 @@
CHECK_EQ((status_t)OK, mCamera->setPreviewTarget(mSurface));
}
- // By default, store real data in video buffers.
- mVideoBufferMode = hardware::ICamera::VIDEO_BUFFER_MODE_DATA_CALLBACK_YUV;
- if (storeMetaDataInVideoBuffers) {
- if (OK == mCamera->setVideoBufferMode(hardware::ICamera::VIDEO_BUFFER_MODE_BUFFER_QUEUE)) {
- mVideoBufferMode = hardware::ICamera::VIDEO_BUFFER_MODE_BUFFER_QUEUE;
- } else if (OK == mCamera->setVideoBufferMode(
- hardware::ICamera::VIDEO_BUFFER_MODE_DATA_CALLBACK_METADATA)) {
- mVideoBufferMode = hardware::ICamera::VIDEO_BUFFER_MODE_DATA_CALLBACK_METADATA;
- }
- }
-
- if (mVideoBufferMode == hardware::ICamera::VIDEO_BUFFER_MODE_DATA_CALLBACK_YUV) {
- err = mCamera->setVideoBufferMode(hardware::ICamera::VIDEO_BUFFER_MODE_DATA_CALLBACK_YUV);
- if (err != OK) {
- ALOGE("%s: Setting video buffer mode to VIDEO_BUFFER_MODE_DATA_CALLBACK_YUV failed: "
- "%s (err=%d)", __FUNCTION__, strerror(-err), err);
- return err;
- }
+ // Use buffer queue to receive video buffers from camera
+ err = mCamera->setVideoBufferMode(hardware::ICamera::VIDEO_BUFFER_MODE_BUFFER_QUEUE);
+ if (err != OK) {
+ ALOGE("%s: Setting video buffer mode to VIDEO_BUFFER_MODE_BUFFER_QUEUE failed: "
+ "%s (err=%d)", __FUNCTION__, strerror(-err), err);
+ return err;
}
int64_t glitchDurationUs = (1000000LL / mVideoFrameRate);
@@ -724,54 +613,26 @@
int64_t token = IPCThreadState::self()->clearCallingIdentity();
status_t err;
- if (mVideoBufferMode == hardware::ICamera::VIDEO_BUFFER_MODE_BUFFER_QUEUE) {
- // Initialize buffer queue.
- err = initBufferQueue(mVideoSize.width, mVideoSize.height, mEncoderFormat,
- (android_dataspace_t)mEncoderDataSpace,
- mNumInputBuffers > 0 ? mNumInputBuffers : 1);
- if (err != OK) {
- ALOGE("%s: Failed to initialize buffer queue: %s (err=%d)", __FUNCTION__,
- strerror(-err), err);
- return err;
- }
- } else {
- if (mNumInputBuffers > 0) {
- err = mCamera->sendCommand(
- CAMERA_CMD_SET_VIDEO_BUFFER_COUNT, mNumInputBuffers, 0);
-
- // This could happen for CameraHAL1 clients; thus the failure is
- // not a fatal error
- if (err != OK) {
- ALOGW("Failed to set video buffer count to %d due to %d",
- mNumInputBuffers, err);
- }
- }
-
- err = mCamera->sendCommand(
- CAMERA_CMD_SET_VIDEO_FORMAT, mEncoderFormat, mEncoderDataSpace);
-
- // This could happen for CameraHAL1 clients; thus the failure is
- // not a fatal error
- if (err != OK) {
- ALOGW("Failed to set video encoder format/dataspace to %d, %d due to %d",
- mEncoderFormat, mEncoderDataSpace, err);
- }
-
- // Create memory heap to store buffers as VideoNativeMetadata.
- createVideoBufferMemoryHeap(sizeof(VideoNativeHandleMetadata), kDefaultVideoBufferCount);
+ // Initialize buffer queue.
+ err = initBufferQueue(mVideoSize.width, mVideoSize.height, mEncoderFormat,
+ (android_dataspace_t)mEncoderDataSpace,
+ mNumInputBuffers > 0 ? mNumInputBuffers : 1);
+ if (err != OK) {
+ ALOGE("%s: Failed to initialize buffer queue: %s (err=%d)", __FUNCTION__,
+ strerror(-err), err);
+ return err;
}
+ // Start data flow
err = OK;
if (mCameraFlags & FLAGS_HOT_CAMERA) {
mCamera->unlock();
mCamera.clear();
- if ((err = mCameraRecordingProxy->startRecording(
- new ProxyListener(this))) != OK) {
+ if ((err = mCameraRecordingProxy->startRecording()) != OK) {
ALOGE("Failed to start recording, received error: %s (%d)",
strerror(-err), err);
}
} else {
- mCamera->setListener(new CameraSourceListener(this));
mCamera->startRecording();
if (!mCamera->recordingEnabled()) {
err = -EINVAL;
@@ -837,7 +698,6 @@
}
} else {
if (mCamera != 0) {
- mCamera->setListener(NULL);
mCamera->stopRecording();
}
}
@@ -936,97 +796,31 @@
void CameraSource::releaseRecordingFrame(const sp<IMemory>& frame) {
ALOGV("releaseRecordingFrame");
- if (mVideoBufferMode == hardware::ICamera::VIDEO_BUFFER_MODE_BUFFER_QUEUE) {
- // Return the buffer to buffer queue in VIDEO_BUFFER_MODE_BUFFER_QUEUE mode.
- ssize_t offset;
- size_t size;
- sp<IMemoryHeap> heap = frame->getMemory(&offset, &size);
- if (heap->getHeapID() != mMemoryHeapBase->getHeapID()) {
- ALOGE("%s: Mismatched heap ID, ignoring release (got %x, expected %x)", __FUNCTION__,
- heap->getHeapID(), mMemoryHeapBase->getHeapID());
- return;
- }
-
- VideoNativeMetadata *payload = reinterpret_cast<VideoNativeMetadata*>(
- (uint8_t*)heap->getBase() + offset);
-
- // Find the corresponding buffer item for the native window buffer.
- ssize_t index = mReceivedBufferItemMap.indexOfKey(payload->pBuffer);
- if (index == NAME_NOT_FOUND) {
- ALOGE("%s: Couldn't find buffer item for %p", __FUNCTION__, payload->pBuffer);
- return;
- }
-
- BufferItem buffer = mReceivedBufferItemMap.valueAt(index);
- mReceivedBufferItemMap.removeItemsAt(index);
- mVideoBufferConsumer->releaseBuffer(buffer);
- mMemoryBases.push_back(frame);
- mMemoryBaseAvailableCond.signal();
- } else {
- native_handle_t* handle = nullptr;
-
- // Check if frame contains a VideoNativeHandleMetadata.
- if (frame->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*)(frame->unsecurePointer());
- if (metadata->eType == kMetadataBufferTypeNativeHandleSource) {
- handle = metadata->pHandle;
- }
- }
-
- if (handle != nullptr) {
- ssize_t offset;
- size_t size;
- sp<IMemoryHeap> heap = frame->getMemory(&offset, &size);
- if (heap->getHeapID() != mMemoryHeapBase->getHeapID()) {
- ALOGE("%s: Mismatched heap ID, ignoring release (got %x, expected %x)",
- __FUNCTION__, heap->getHeapID(), mMemoryHeapBase->getHeapID());
- return;
- }
- uint32_t batchSize = 0;
- {
- Mutex::Autolock autoLock(mBatchLock);
- if (mInflightBatchSizes.size() > 0) {
- batchSize = mInflightBatchSizes[0];
- }
- }
- if (batchSize == 0) { // return buffers one by one
- // Frame contains a VideoNativeHandleMetadata. Send the handle back to camera.
- releaseRecordingFrameHandle(handle);
- mMemoryBases.push_back(frame);
- mMemoryBaseAvailableCond.signal();
- } else { // Group buffers in batch then return
- Mutex::Autolock autoLock(mBatchLock);
- mInflightReturnedHandles.push_back(handle);
- mInflightReturnedMemorys.push_back(frame);
- if (mInflightReturnedHandles.size() == batchSize) {
- releaseRecordingFrameHandleBatch(mInflightReturnedHandles);
-
- mInflightBatchSizes.pop_front();
- mInflightReturnedHandles.clear();
- for (const auto& mem : mInflightReturnedMemorys) {
- mMemoryBases.push_back(mem);
- mMemoryBaseAvailableCond.signal();
- }
- mInflightReturnedMemorys.clear();
- }
- }
-
- } else if (mCameraRecordingProxy != nullptr) {
- // mCamera is created by application. Return the frame back to camera via camera
- // recording proxy.
- mCameraRecordingProxy->releaseRecordingFrame(frame);
- } else if (mCamera != nullptr) {
- // mCamera is created by CameraSource. Return the frame directly back to camera.
- int64_t token = IPCThreadState::self()->clearCallingIdentity();
- mCamera->releaseRecordingFrame(frame);
- IPCThreadState::self()->restoreCallingIdentity(token);
- }
+ // Return the buffer to buffer queue in VIDEO_BUFFER_MODE_BUFFER_QUEUE mode.
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap = frame->getMemory(&offset, &size);
+ if (heap->getHeapID() != mMemoryHeapBase->getHeapID()) {
+ ALOGE("%s: Mismatched heap ID, ignoring release (got %x, expected %x)", __FUNCTION__,
+ heap->getHeapID(), mMemoryHeapBase->getHeapID());
+ return;
}
+
+ VideoNativeMetadata *payload = reinterpret_cast<VideoNativeMetadata*>(
+ (uint8_t*)heap->getBase() + offset);
+
+ // Find the corresponding buffer item for the native window buffer.
+ ssize_t index = mReceivedBufferItemMap.indexOfKey(payload->pBuffer);
+ if (index == NAME_NOT_FOUND) {
+ ALOGE("%s: Couldn't find buffer item for %p", __FUNCTION__, payload->pBuffer);
+ return;
+ }
+
+ BufferItem buffer = mReceivedBufferItemMap.valueAt(index);
+ mReceivedBufferItemMap.removeItemsAt(index);
+ mVideoBufferConsumer->releaseBuffer(buffer);
+ mMemoryBases.push_back(frame);
+ mMemoryBaseAvailableCond.signal();
}
void CameraSource::releaseQueuedFrames() {
@@ -1187,152 +981,6 @@
return false;
}
-void CameraSource::dataCallbackTimestamp(int64_t timestampUs,
- int32_t msgType __unused, const sp<IMemory> &data) {
- ALOGV("dataCallbackTimestamp: timestamp %lld us", (long long)timestampUs);
- Mutex::Autolock autoLock(mLock);
-
- if (shouldSkipFrameLocked(timestampUs)) {
- releaseOneRecordingFrame(data);
- return;
- }
-
- ++mNumFramesReceived;
-
- CHECK(data != NULL && data->size() > 0);
- mFramesReceived.push_back(data);
- int64_t timeUs = mStartTimeUs + (timestampUs - mFirstFrameTimeUs);
- mFrameTimes.push_back(timeUs);
- ALOGV("initial delay: %" PRId64 ", current time stamp: %" PRId64,
- mStartTimeUs, timeUs);
- mFrameAvailableCondition.signal();
-}
-
-void CameraSource::releaseRecordingFrameHandle(native_handle_t* handle) {
- if (mCameraRecordingProxy != nullptr) {
- mCameraRecordingProxy->releaseRecordingFrameHandle(handle);
- } else if (mCamera != nullptr) {
- int64_t token = IPCThreadState::self()->clearCallingIdentity();
- mCamera->releaseRecordingFrameHandle(handle);
- IPCThreadState::self()->restoreCallingIdentity(token);
- } else {
- native_handle_close(handle);
- native_handle_delete(handle);
- }
-}
-
-void CameraSource::releaseRecordingFrameHandleBatch(const std::vector<native_handle_t*>& handles) {
- if (mCameraRecordingProxy != nullptr) {
- mCameraRecordingProxy->releaseRecordingFrameHandleBatch(handles);
- } else if (mCamera != nullptr) {
- int64_t token = IPCThreadState::self()->clearCallingIdentity();
- mCamera->releaseRecordingFrameHandleBatch(handles);
- IPCThreadState::self()->restoreCallingIdentity(token);
- } else {
- for (auto& handle : handles) {
- native_handle_close(handle);
- native_handle_delete(handle);
- }
- }
-}
-
-void CameraSource::recordingFrameHandleCallbackTimestamp(int64_t timestampUs,
- native_handle_t* handle) {
- ALOGV("%s: timestamp %lld us", __FUNCTION__, (long long)timestampUs);
- Mutex::Autolock autoLock(mLock);
- if (handle == nullptr) return;
-
- if (shouldSkipFrameLocked(timestampUs)) {
- releaseRecordingFrameHandle(handle);
- return;
- }
-
- while (mMemoryBases.empty()) {
- if (mMemoryBaseAvailableCond.waitRelative(mLock, kMemoryBaseAvailableTimeoutNs) ==
- TIMED_OUT) {
- ALOGW("Waiting on an available memory base timed out. Dropping a recording frame.");
- releaseRecordingFrameHandle(handle);
- return;
- }
- }
-
- ++mNumFramesReceived;
-
- sp<IMemory> data = *mMemoryBases.begin();
- mMemoryBases.erase(mMemoryBases.begin());
-
- // Wrap native handle in sp<IMemory> so it can be pushed to mFramesReceived.
- VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(data->unsecurePointer());
- metadata->eType = kMetadataBufferTypeNativeHandleSource;
- metadata->pHandle = handle;
-
- mFramesReceived.push_back(data);
- int64_t timeUs = mStartTimeUs + (timestampUs - mFirstFrameTimeUs);
- mFrameTimes.push_back(timeUs);
- ALOGV("initial delay: %" PRId64 ", current time stamp: %" PRId64, mStartTimeUs, timeUs);
- mFrameAvailableCondition.signal();
-}
-
-void CameraSource::recordingFrameHandleCallbackTimestampBatch(
- const std::vector<int64_t>& timestampsUs,
- const std::vector<native_handle_t*>& handles) {
- size_t n = timestampsUs.size();
- if (n != handles.size()) {
- ALOGE("%s: timestampsUs(%zu) and handles(%zu) size mismatch!",
- __FUNCTION__, timestampsUs.size(), handles.size());
- }
-
- Mutex::Autolock autoLock(mLock);
- int batchSize = 0;
- for (size_t i = 0; i < n; i++) {
- int64_t timestampUs = timestampsUs[i];
- native_handle_t* handle = handles[i];
-
- ALOGV("%s: timestamp %lld us", __FUNCTION__, (long long)timestampUs);
- if (handle == nullptr) continue;
-
- if (shouldSkipFrameLocked(timestampUs)) {
- releaseRecordingFrameHandle(handle);
- continue;
- }
-
- while (mMemoryBases.empty()) {
- if (mMemoryBaseAvailableCond.waitRelative(mLock, kMemoryBaseAvailableTimeoutNs) ==
- TIMED_OUT) {
- ALOGW("Waiting on an available memory base timed out. Dropping a recording frame.");
- releaseRecordingFrameHandle(handle);
- continue;
- }
- }
- ++batchSize;
- ++mNumFramesReceived;
- sp<IMemory> data = *mMemoryBases.begin();
- mMemoryBases.erase(mMemoryBases.begin());
-
- // Wrap native handle in sp<IMemory> so it can be pushed to mFramesReceived.
- // 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;
-
- mFramesReceived.push_back(data);
- int64_t timeUs = mStartTimeUs + (timestampUs - mFirstFrameTimeUs);
- mFrameTimes.push_back(timeUs);
- ALOGV("initial delay: %" PRId64 ", current time stamp: %" PRId64, mStartTimeUs, timeUs);
-
- }
- if (batchSize > 0) {
- Mutex::Autolock autoLock(mBatchLock);
- mInflightBatchSizes.push_back(batchSize);
- }
- for (int i = 0; i < batchSize; i++) {
- mFrameAvailableCondition.signal();
- }
-}
-
CameraSource::BufferQueueListener::BufferQueueListener(const sp<BufferItemConsumer>& consumer,
const sp<CameraSource>& cameraSource) {
mConsumer = consumer;
@@ -1424,41 +1072,7 @@
MetadataBufferType CameraSource::metaDataStoredInVideoBuffers() const {
ALOGV("metaDataStoredInVideoBuffers");
- // Output buffers will contain metadata if camera sends us buffer in metadata mode or via
- // buffer queue.
- switch (mVideoBufferMode) {
- case hardware::ICamera::VIDEO_BUFFER_MODE_DATA_CALLBACK_METADATA:
- return kMetadataBufferTypeNativeHandleSource;
- case hardware::ICamera::VIDEO_BUFFER_MODE_BUFFER_QUEUE:
- return kMetadataBufferTypeANWBuffer;
- default:
- return kMetadataBufferTypeInvalid;
- }
-}
-
-CameraSource::ProxyListener::ProxyListener(const sp<CameraSource>& source) {
- mSource = source;
-}
-
-void CameraSource::ProxyListener::dataCallbackTimestamp(
- nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr) {
- mSource->dataCallbackTimestamp(timestamp / 1000, msgType, dataPtr);
-}
-
-void CameraSource::ProxyListener::recordingFrameHandleCallbackTimestamp(nsecs_t timestamp,
- native_handle_t* handle) {
- mSource->recordingFrameHandleCallbackTimestamp(timestamp / 1000, handle);
-}
-
-void CameraSource::ProxyListener::recordingFrameHandleCallbackTimestampBatch(
- const std::vector<int64_t>& timestampsUs,
- const std::vector<native_handle_t*>& handles) {
- int n = timestampsUs.size();
- std::vector<nsecs_t> modifiedTimestamps(n);
- for (int i = 0; i < n; i++) {
- modifiedTimestamps[i] = timestampsUs[i] / 1000;
- }
- mSource->recordingFrameHandleCallbackTimestampBatch(modifiedTimestamps, handles);
+ return kMetadataBufferTypeANWBuffer;
}
void CameraSource::DeathNotifier::binderDied(const wp<IBinder>& who __unused) {
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index e0a6eb3..50a512f 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -45,15 +45,13 @@
Size videoSize,
int32_t videoFrameRate,
const sp<IGraphicBufferProducer>& surface,
- int64_t timeBetweenFrameCaptureUs,
- bool storeMetaDataInVideoBuffers) {
+ int64_t timeBetweenFrameCaptureUs) {
CameraSourceTimeLapse *source = new
CameraSourceTimeLapse(camera, proxy, cameraId,
clientName, clientUid, clientPid,
videoSize, videoFrameRate, surface,
- timeBetweenFrameCaptureUs,
- storeMetaDataInVideoBuffers);
+ timeBetweenFrameCaptureUs);
if (source != NULL) {
if (source->initCheck() != OK) {
@@ -74,11 +72,9 @@
Size videoSize,
int32_t videoFrameRate,
const sp<IGraphicBufferProducer>& surface,
- int64_t timeBetweenFrameCaptureUs,
- bool storeMetaDataInVideoBuffers)
+ int64_t timeBetweenFrameCaptureUs)
: CameraSource(camera, proxy, cameraId, clientName, clientUid, clientPid,
- videoSize, videoFrameRate, surface,
- storeMetaDataInVideoBuffers),
+ videoSize, videoFrameRate, surface),
mTimeBetweenTimeLapseVideoFramesUs(1E6/videoFrameRate),
mLastTimeLapseFrameRealTimestampUs(0),
mSkipCurrentFrame(false) {
@@ -173,12 +169,6 @@
ALOGV("signalBufferReturned");
Mutex::Autolock autoLock(mQuickStopLock);
if (mQuickStop && (buffer == mLastReadBufferCopy)) {
- if (metaDataStoredInVideoBuffers() == kMetadataBufferTypeNativeHandleSource) {
- native_handle_t* handle = (
- (VideoNativeHandleMetadata*)(mLastReadBufferCopy->data()))->pHandle;
- native_handle_close(handle);
- native_handle_delete(handle);
- }
buffer->setObserver(NULL);
buffer->release();
mLastReadBufferCopy = NULL;
@@ -191,8 +181,7 @@
void createMediaBufferCopy(
const MediaBufferBase& sourceBuffer,
int64_t frameTime,
- MediaBufferBase **newBuffer,
- int32_t videoBufferMode) {
+ MediaBufferBase **newBuffer) {
ALOGV("createMediaBufferCopy");
size_t sourceSize = sourceBuffer.size();
@@ -203,19 +192,13 @@
(*newBuffer)->meta_data().setInt64(kKeyTime, frameTime);
- if (videoBufferMode == kMetadataBufferTypeNativeHandleSource) {
- ((VideoNativeHandleMetadata*)((*newBuffer)->data()))->pHandle =
- native_handle_clone(
- ((VideoNativeHandleMetadata*)(sourceBuffer.data()))->pHandle);
- }
}
void CameraSourceTimeLapse::fillLastReadBufferCopy(MediaBufferBase& sourceBuffer) {
ALOGV("fillLastReadBufferCopy");
int64_t frameTime;
CHECK(sourceBuffer.meta_data().findInt64(kKeyTime, &frameTime));
- createMediaBufferCopy(sourceBuffer, frameTime, &mLastReadBufferCopy,
- metaDataStoredInVideoBuffers());
+ createMediaBufferCopy(sourceBuffer, frameTime, &mLastReadBufferCopy);
mLastReadBufferCopy->add_ref();
mLastReadBufferCopy->setObserver(this);
}
@@ -240,19 +223,6 @@
}
}
-sp<IMemory> CameraSourceTimeLapse::createIMemoryCopy(
- const sp<IMemory> &source_data) {
-
- ALOGV("createIMemoryCopy");
- size_t source_size = source_data->size();
- void* source_pointer = source_data->unsecurePointer();
-
- sp<MemoryHeapBase> newMemoryHeap = new MemoryHeapBase(source_size);
- sp<MemoryBase> newMemory = new MemoryBase(newMemoryHeap, 0, source_size);
- memcpy(newMemory->unsecurePointer(), source_pointer, source_size);
- return newMemory;
-}
-
bool CameraSourceTimeLapse::skipCurrentFrame(int64_t /* timestampUs */) {
ALOGV("skipCurrentFrame");
if (mSkipCurrentFrame) {
@@ -318,31 +288,6 @@
return false;
}
-void CameraSourceTimeLapse::dataCallbackTimestamp(int64_t timestampUs, int32_t msgType,
- const sp<IMemory> &data) {
- ALOGV("dataCallbackTimestamp");
- mSkipCurrentFrame = skipFrameAndModifyTimeStamp(×tampUs);
- CameraSource::dataCallbackTimestamp(timestampUs, msgType, data);
-}
-
-void CameraSourceTimeLapse::recordingFrameHandleCallbackTimestamp(int64_t timestampUs,
- native_handle_t* handle) {
- ALOGV("recordingFrameHandleCallbackTimestamp");
- mSkipCurrentFrame = skipFrameAndModifyTimeStamp(×tampUs);
- CameraSource::recordingFrameHandleCallbackTimestamp(timestampUs, handle);
-}
-
-void CameraSourceTimeLapse::recordingFrameHandleCallbackTimestampBatch(
- const std::vector<int64_t>& timestampsUs,
- const std::vector<native_handle_t*>& handles) {
- ALOGV("recordingFrameHandleCallbackTimestampBatch");
- int n = timestampsUs.size();
- for (int i = 0; i < n; i++) {
- // Don't do batching for CameraSourceTimeLapse for now
- recordingFrameHandleCallbackTimestamp(timestampsUs[i], handles[i]);
- }
-}
-
void CameraSourceTimeLapse::processBufferQueueFrame(BufferItem& buffer) {
ALOGV("processBufferQueueFrame");
int64_t timestampUs = buffer.mTimestamp / 1000;
diff --git a/media/libstagefright/FrameCaptureProcessor.cpp b/media/libstagefright/FrameCaptureProcessor.cpp
index 63238bc..8cd7f82 100644
--- a/media/libstagefright/FrameCaptureProcessor.cpp
+++ b/media/libstagefright/FrameCaptureProcessor.cpp
@@ -164,16 +164,15 @@
if (err != OK) {
ALOGE("drawLayers returned err %d", err);
- return err;
+ } else {
+ err = fence->wait(500);
+ if (err != OK) {
+ ALOGW("wait for fence returned err %d", err);
+ err = OK;
+ }
}
-
- err = fence->wait(500);
- if (err != OK) {
- ALOGW("wait for fence returned err %d", err);
- }
-
mRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL);
- return OK;
+ return err;
}
void FrameCaptureProcessor::onMessageReceived(const sp<AMessage> &msg) {
diff --git a/media/libstagefright/FrameDecoder.cpp b/media/libstagefright/FrameDecoder.cpp
index 4ba85ba..0fd4ef2 100644
--- a/media/libstagefright/FrameDecoder.cpp
+++ b/media/libstagefright/FrameDecoder.cpp
@@ -19,6 +19,7 @@
#include "include/FrameDecoder.h"
#include "include/FrameCaptureLayer.h"
+#include "include/HevcUtils.h"
#include <binder/MemoryBase.h>
#include <binder/MemoryHeapBase.h>
#include <gui/Surface.h>
@@ -66,6 +67,12 @@
if (trackMeta->findInt32(kKeySARWidth, &sarWidth)
&& trackMeta->findInt32(kKeySARHeight, &sarHeight)
&& sarHeight != 0) {
+ int32_t multVal;
+ if (width < 0 || sarWidth < 0 ||
+ __builtin_mul_overflow(width, sarWidth, &multVal)) {
+ ALOGE("displayWidth overflow %dx%d", width, sarWidth);
+ return NULL;
+ }
displayWidth = (width * sarWidth) / sarHeight;
displayHeight = height;
} else if (trackMeta->findInt32(kKeyDisplayWidth, &displayWidth)
@@ -86,6 +93,16 @@
rotationAngle = 0;
}
+ if (!metaOnly) {
+ int32_t multVal;
+ if (width < 0 || height < 0 || dstBpp < 0 ||
+ __builtin_mul_overflow(dstBpp, width, &multVal) ||
+ __builtin_mul_overflow(multVal, height, &multVal)) {
+ ALOGE("Frame size overflow %dx%d bpp %d", width, height, dstBpp);
+ return NULL;
+ }
+ }
+
VideoFrame frame(width, height, displayWidth, displayHeight,
tileWidth, tileHeight, rotationAngle, dstBpp, !metaOnly, iccSize);
@@ -96,7 +113,7 @@
return NULL;
}
sp<IMemory> frameMem = new MemoryBase(heap, 0, size);
- if (frameMem == NULL) {
+ if (frameMem == NULL || frameMem->unsecurePointer() == NULL) {
ALOGE("not enough memory for VideoFrame size=%zu", size);
return NULL;
}
@@ -120,15 +137,23 @@
false /*allocRotated*/, true /*metaOnly*/);
}
+bool isAvif(const sp<MetaData> &trackMeta) {
+ const char *mime;
+ return trackMeta->findCString(kKeyMIMEType, &mime)
+ && (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)
+ || !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_AVIF));
+}
+
bool findThumbnailInfo(
const sp<MetaData> &trackMeta, int32_t *width, int32_t *height,
uint32_t *type = NULL, const void **data = NULL, size_t *size = NULL) {
uint32_t dummyType;
const void *dummyData;
size_t dummySize;
+ int codecConfigKey = isAvif(trackMeta) ? kKeyThumbnailAV1C : kKeyThumbnailHVCC;
return trackMeta->findInt32(kKeyThumbnailWidth, width)
&& trackMeta->findInt32(kKeyThumbnailHeight, height)
- && trackMeta->findData(kKeyThumbnailHVCC,
+ && trackMeta->findData(codecConfigKey,
type ?: &dummyType, data ?: &dummyData, size ?: &dummySize);
}
@@ -456,7 +481,8 @@
const sp<IMediaSource> &source)
: FrameDecoder(componentName, trackMeta, source),
mFrame(NULL),
- mIsAvcOrHevc(false),
+ mIsAvc(false),
+ mIsHevc(false),
mSeekMode(MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC),
mTargetTimeUs(-1LL),
mDefaultSampleDurationUs(0) {
@@ -479,8 +505,8 @@
return NULL;
}
- mIsAvcOrHevc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)
- || !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC);
+ mIsAvc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
+ mIsHevc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC);
if (frameTimeUs < 0) {
int64_t thumbNailTime = -1ll;
@@ -543,8 +569,10 @@
ALOGV("Seeking closest: targetTimeUs=%lld", (long long)mTargetTimeUs);
}
- if (mIsAvcOrHevc && !isSeekingClosest
- && IsIDR(codecBuffer->data(), codecBuffer->size())) {
+ if (!isSeekingClosest
+ && ((mIsAvc && IsIDR(codecBuffer->data(), codecBuffer->size()))
+ || (mIsHevc && IsIDR(
+ codecBuffer->data(), codecBuffer->size())))) {
// Only need to decode one IDR frame, unless we're seeking with CLOSEST
// option, in which case we need to actually decode to targetTimeUs.
*flags |= MediaCodec::BUFFER_FLAG_EOS;
@@ -616,6 +644,10 @@
0,
dstBpp(),
mCaptureLayer != nullptr /*allocRotated*/);
+ if (frameMem == nullptr) {
+ return NO_MEMORY;
+ }
+
mFrame = static_cast<VideoFrame*>(frameMem->unsecurePointer());
setFrame(frameMem);
@@ -748,7 +780,10 @@
overrideMeta->remove(kKeyDisplayHeight);
overrideMeta->setInt32(kKeyWidth, mWidth);
overrideMeta->setInt32(kKeyHeight, mHeight);
- overrideMeta->setData(kKeyHVCC, type, data, size);
+ // The AV1 codec configuration data is passed via CSD0 to the AV1
+ // decoder.
+ const int codecConfigKey = isAvif(trackMeta()) ? kKeyOpaqueCSD0 : kKeyHVCC;
+ overrideMeta->setData(codecConfigKey, type, data, size);
options->setSeekTo(-1);
} else {
CHECK(trackMeta()->findInt32(kKeyWidth, &mWidth));
@@ -855,6 +890,11 @@
if (mFrame == NULL) {
sp<IMemory> frameMem = allocVideoFrame(
trackMeta(), mWidth, mHeight, mTileWidth, mTileHeight, dstBpp());
+
+ if (frameMem == nullptr) {
+ return NO_MEMORY;
+ }
+
mFrame = static_cast<VideoFrame*>(frameMem->unsecurePointer());
setFrame(frameMem);
diff --git a/media/libstagefright/HevcUtils.cpp b/media/libstagefright/HevcUtils.cpp
index e4794dd..5f9c20e 100644
--- a/media/libstagefright/HevcUtils.cpp
+++ b/media/libstagefright/HevcUtils.cpp
@@ -34,7 +34,10 @@
namespace android {
-static const uint8_t kHevcNalUnitTypes[5] = {
+static const uint8_t kHevcNalUnitTypes[8] = {
+ kHevcNalUnitTypeCodedSliceIdr,
+ kHevcNalUnitTypeCodedSliceIdrNoLP,
+ kHevcNalUnitTypeCodedSliceCra,
kHevcNalUnitTypeVps,
kHevcNalUnitTypeSps,
kHevcNalUnitTypePps,
@@ -539,4 +542,26 @@
return OK;
}
+bool HevcParameterSets::IsHevcIDR(const uint8_t *data, size_t size) {
+ bool foundIDR = false;
+ const uint8_t *nalStart;
+ size_t nalSize;
+ while (!foundIDR && getNextNALUnit(&data, &size, &nalStart, &nalSize, true) == OK) {
+ if (nalSize == 0) {
+ ALOGE("Encountered zero-length HEVC NAL");
+ return false;
+ }
+
+ uint8_t nalType = (nalStart[0] & 0x7E) >> 1;
+ switch(nalType) {
+ case kHevcNalUnitTypeCodedSliceIdr:
+ case kHevcNalUnitTypeCodedSliceIdrNoLP:
+ case kHevcNalUnitTypeCodedSliceCra:
+ foundIDR = true;
+ break;
+ }
+ }
+
+ return foundIDR;
+}
} // namespace android
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index b2fae96..7c7fcac 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -519,12 +519,13 @@
mSendNotify = false;
mWriteSeekErr = false;
mFallocateErr = false;
-
// Reset following variables for all the sessions and they will be
// initialized in start(MetaData *param).
mIsRealTimeRecording = true;
+ mIsBackgroundMode = false;
mUse4ByteNalLength = true;
mOffset = 0;
+ mMaxOffsetAppend = 0;
mPreAllocateFileEndOffset = 0;
mMdatOffset = 0;
mMdatEndOffset = 0;
@@ -542,6 +543,7 @@
mNumGrids = 0;
mNextItemId = kItemIdBase;
mHasRefs = false;
+ mResetStatus = OK;
mPreAllocFirstTime = true;
mPrevAllTracksTotalMetaDataSizeEstimate = 0;
@@ -661,6 +663,14 @@
sp<MetaData> meta = source->getFormat();
meta->findCString(kKeyMIMEType, &mime);
+
+ // Background mode for media transcoding. If either audio or video track signal this is in
+ // background mode, we will set all the threads to run in background priority.
+ int32_t isBackgroundMode;
+ if (meta && meta->findInt32(kKeyBackgroundMode, &isBackgroundMode)) {
+ mIsBackgroundMode |= isBackgroundMode;
+ }
+
if (Track::getFourCCForMime(mime) == NULL) {
ALOGE("Unsupported mime '%s'", mime);
return ERROR_UNSUPPORTED;
@@ -991,6 +1001,19 @@
seekOrPostError(mFd, mFreeBoxOffset, SEEK_SET);
writeInt32(mInMemoryCacheSize);
write("free", 4);
+ if (mInMemoryCacheSize >= 8) {
+ off64_t bufSize = mInMemoryCacheSize - 8;
+ char* zeroBuffer = new (std::nothrow) char[bufSize];
+ if (zeroBuffer) {
+ std::fill_n(zeroBuffer, bufSize, '0');
+ writeOrPostError(mFd, zeroBuffer, bufSize);
+ delete [] zeroBuffer;
+ } else {
+ ALOGW("freebox in file isn't initialized to 0");
+ }
+ } else {
+ ALOGW("freebox size is less than 8:%" PRId64, mInMemoryCacheSize);
+ }
mMdatOffset = mFreeBoxOffset + mInMemoryCacheSize;
} else {
mMdatOffset = mOffset;
@@ -1027,6 +1050,11 @@
return OK;
}
+status_t MPEG4Writer::stop() {
+ // If reset was in progress, wait for it to complete.
+ return reset(true, true);
+}
+
status_t MPEG4Writer::pause() {
ALOGW("MPEG4Writer: pause is not supported");
return ERROR_UNSUPPORTED;
@@ -1131,11 +1159,24 @@
if (!truncatePreAllocation()) {
if (err == OK) { err = ERROR_IO; }
}
+
+ // TODO(b/174770856) remove this measurement (and perhaps the fsync)
+ nsecs_t sync_started = systemTime(SYSTEM_TIME_REALTIME);
if (fsync(mFd) != 0) {
ALOGW("(ignored)fsync err:%s(%d)", std::strerror(errno), errno);
// Don't bubble up fsync error, b/157291505.
// if (err == OK) { err = ERROR_IO; }
}
+ nsecs_t sync_finished = systemTime(SYSTEM_TIME_REALTIME);
+ nsecs_t sync_elapsed_ns = sync_finished - sync_started;
+ int64_t filesize = -1;
+ struct stat statbuf;
+ if (fstat(mFd, &statbuf) == 0) {
+ filesize = statbuf.st_size;
+ }
+ ALOGD("final fsync() takes %" PRId64 " ms, file size %" PRId64,
+ sync_elapsed_ns / 1000000, (int64_t) filesize);
+
if (close(mFd) != 0) {
ALOGE("close err:%s(%d)", std::strerror(errno), errno);
if (err == OK) { err = ERROR_IO; }
@@ -1159,8 +1200,12 @@
return err;
}
-void MPEG4Writer::finishCurrentSession() {
- reset(false /* stopSource */);
+status_t MPEG4Writer::finishCurrentSession() {
+ ALOGV("finishCurrentSession");
+ /* Don't wait if reset is in progress already, that avoids deadlock
+ * as finishCurrentSession() is called from control looper thread.
+ */
+ return reset(false, false);
}
status_t MPEG4Writer::switchFd() {
@@ -1182,11 +1227,32 @@
return err;
}
-status_t MPEG4Writer::reset(bool stopSource) {
+status_t MPEG4Writer::reset(bool stopSource, bool waitForAnyPreviousCallToComplete) {
ALOGD("reset()");
- std::lock_guard<std::mutex> l(mResetMutex);
+ std::unique_lock<std::mutex> lk(mResetMutex, std::defer_lock);
+ if (waitForAnyPreviousCallToComplete) {
+ /* stop=>reset from client needs the return value of reset call, hence wait here
+ * if a reset was in process already.
+ */
+ lk.lock();
+ } else if (!lk.try_lock()) {
+ /* Internal reset from control looper thread shouldn't wait for any reset in
+ * process already.
+ */
+ return INVALID_OPERATION;
+ }
+
+ if (mResetStatus != OK) {
+ /* Don't have to proceed if reset has finished with an error before.
+ * If there was no error before, proceeding reset would be harmless, as the
+ * the call would return from the mInitCheck condition below.
+ */
+ return mResetStatus;
+ }
+
if (mInitCheck != OK) {
- return OK;
+ mResetStatus = OK;
+ return mResetStatus;
} else {
if (!mWriterThreadStarted ||
!mStarted) {
@@ -1198,7 +1264,8 @@
if (writerErr != OK) {
retErr = writerErr;
}
- return retErr;
+ mResetStatus = retErr;
+ return mResetStatus;
}
}
@@ -1245,7 +1312,8 @@
if (err != OK && err != ERROR_MALFORMED) {
// Ignoring release() return value as there was an "err" already.
release();
- return err;
+ mResetStatus = err;
+ return mResetStatus;
}
// Fix up the size of the 'mdat' chunk.
@@ -1303,7 +1371,8 @@
if (err == OK) {
err = errRelease;
}
- return err;
+ mResetStatus = err;
+ return mResetStatus;
}
/*
@@ -1494,6 +1563,26 @@
MediaBuffer *buffer, bool usePrefix,
uint32_t tiffHdrOffset, size_t *bytesWritten) {
off64_t old_offset = mOffset;
+ int64_t offset;
+ ALOGV("buffer->range_length:%lld", (long long)buffer->range_length());
+ if (buffer->meta_data().findInt64(kKeySampleFileOffset, &offset)) {
+ ALOGV("offset:%lld, old_offset:%lld", (long long)offset, (long long)old_offset);
+ if (old_offset == offset) {
+ mOffset += buffer->range_length();
+ } else {
+ ALOGV("offset and old_offset are not equal! diff:%lld", (long long)offset - old_offset);
+ mOffset = offset + buffer->range_length();
+ // mOffset += buffer->range_length() + offset - old_offset;
+ }
+ *bytesWritten = buffer->range_length();
+ ALOGV("mOffset:%lld, mMaxOffsetAppend:%lld, bytesWritten:%lld", (long long)mOffset,
+ (long long)mMaxOffsetAppend, (long long)*bytesWritten);
+ mMaxOffsetAppend = std::max(mOffset, mMaxOffsetAppend);
+ seekOrPostError(mFd, mMaxOffsetAppend, SEEK_SET);
+ return offset;
+ }
+
+ ALOGV("mOffset:%lld, mMaxOffsetAppend:%lld", (long long)mOffset, (long long)mMaxOffsetAppend);
if (usePrefix) {
addMultipleLengthPrefixedSamples_l(buffer);
@@ -1510,6 +1599,10 @@
mOffset += buffer->range_length();
}
*bytesWritten = mOffset - old_offset;
+
+ ALOGV("mOffset:%lld, old_offset:%lld, bytesWritten:%lld", (long long)mOffset,
+ (long long)old_offset, (long long)*bytesWritten);
+
return old_offset;
}
@@ -1522,6 +1615,7 @@
(const uint8_t *)buffer->data() + buffer->range_offset();
if (!memcmp(ptr, "\x00\x00\x00\x01", 4)) {
+ ALOGV("stripping start code");
buffer->set_range(
buffer->range_offset() + 4, buffer->range_length() - 4);
}
@@ -1552,8 +1646,10 @@
}
void MPEG4Writer::addLengthPrefixedSample_l(MediaBuffer *buffer) {
+ ALOGV("alp:buffer->range_length:%lld", (long long)buffer->range_length());
size_t length = buffer->range_length();
if (mUse4ByteNalLength) {
+ ALOGV("mUse4ByteNalLength");
uint8_t x[4];
x[0] = length >> 24;
x[1] = (length >> 16) & 0xff;
@@ -1563,6 +1659,7 @@
writeOrPostError(mFd, (const uint8_t*)buffer->data() + buffer->range_offset(), length);
mOffset += length + 4;
} else {
+ ALOGV("mUse2ByteNalLength");
CHECK_LT(length, 65536u);
uint8_t x[2];
@@ -2221,7 +2318,11 @@
if (mLooper == nullptr) {
mLooper = new ALooper;
mLooper->setName("MP4WtrCtrlHlpLooper");
- err = mLooper->start();
+ if (mIsBackgroundMode) {
+ err = mLooper->start(false, false, ANDROID_PRIORITY_BACKGROUND);
+ } else {
+ err = mLooper->start();
+ }
mReflector = new AHandlerReflector<MPEG4Writer>(this);
mLooper->registerHandler(mReflector);
}
@@ -2454,31 +2555,27 @@
int fd = mNextFd;
mNextFd = -1;
mLock.unlock();
- finishCurrentSession();
- initInternal(fd, false /*isFirstSession*/);
- start(mStartMeta.get());
- mSwitchPending = false;
- notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED, 0);
+ if (finishCurrentSession() == OK) {
+ initInternal(fd, false /*isFirstSession*/);
+ status_t status = start(mStartMeta.get());
+ mSwitchPending = false;
+ if (status == OK) {
+ 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
+ /* ::write() or lseek64() wasn't a success, file could be malformed.
+ * Or fallocate() failed. reset() and notify client on both the cases.
+ */
+ case kWhatFallocateError: // fallthrough
case kWhatIOError: {
- ALOGE("kWhatIOError");
int32_t err;
CHECK(msg->findInt32("err", &err));
- // Stop tracks' threads and main writer thread.
- stop();
- notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, err);
- break;
- }
- // fallocate() failed, hence stop() and notify app.
- case kWhatFallocateError: {
- ALOGE("kWhatFallocateError");
- int32_t err;
- CHECK(msg->findInt32("err", &err));
- // Stop tracks' threads and main writer thread.
- stop();
- //TODO: introduce a suitable MEDIA_RECORDER_ERROR_* instead MEDIA_RECORDER_ERROR_UNKNOWN?
+ // If reset already in process, don't wait for it complete to avoid deadlock.
+ reset(true, false);
+ //TODO: new MEDIA_RECORDER_ERROR_**** instead MEDIA_RECORDER_ERROR_UNKNOWN ?
notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, err);
break;
}
@@ -2486,7 +2583,7 @@
* Responding with other options could be added later if required.
*/
case kWhatNoIOErrorSoFar: {
- ALOGD("kWhatNoIOErrorSoFar");
+ ALOGV("kWhatNoIOErrorSoFar");
sp<AMessage> response = new AMessage;
response->setInt32("err", OK);
sp<AReplyToken> replyID;
@@ -2694,6 +2791,11 @@
prctl(PR_SET_NAME, (unsigned long)"MPEG4Writer", 0, 0, 0);
+ if (mIsBackgroundMode) {
+ // Background priority for media transcoding.
+ androidSetThreadPriority(0 /* tid (0 = current) */, ANDROID_PRIORITY_BACKGROUND);
+ }
+
Mutex::Autolock autoLock(mLock);
while (!mDone) {
Chunk chunk;
@@ -2719,6 +2821,9 @@
}
writeAllChunks();
+ ALOGV("threadFunc mOffset:%lld, mMaxOffsetAppend:%lld", (long long)mOffset,
+ (long long)mMaxOffsetAppend);
+ mOffset = std::max(mOffset, mMaxOffsetAppend);
}
status_t MPEG4Writer::startWriterThread() {
@@ -3280,6 +3385,7 @@
uint32_t lastSamplesPerChunk = 0;
int64_t lastSampleDurationUs = -1; // Duration calculated from EOS buffer and its timestamp
int64_t lastSampleDurationTicks = -1; // Timescale based ticks
+ int64_t sampleFileOffset = -1;
if (mIsAudio) {
prctl(PR_SET_NAME, (unsigned long)"MP4WtrAudTrkThread", 0, 0, 0);
@@ -3291,6 +3397,9 @@
if (mOwner->isRealTimeRecording()) {
androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
+ } else if (mOwner->isBackgroundMode()) {
+ // Background priority for media transcoding.
+ androidSetThreadPriority(0 /* tid (0 = current) */, ANDROID_PRIORITY_BACKGROUND);
}
sp<MetaData> meta_data;
@@ -3299,6 +3408,7 @@
MediaBufferBase *buffer;
const char *trackName = getTrackType();
while (!mDone && (err = mSource->read(&buffer)) == OK) {
+ ALOGV("read:buffer->range_length:%lld", (long long)buffer->range_length());
int32_t isEOS = false;
if (buffer->range_length() == 0) {
if (buffer->meta_data().findInt32(kKeyIsEndOfStream, &isEOS) && isEOS) {
@@ -3405,6 +3515,14 @@
continue;
}
}
+ if (!buffer->meta_data().findInt64(kKeySampleFileOffset, &sampleFileOffset)) {
+ sampleFileOffset = -1;
+ }
+ int64_t lastSample = -1;
+ if (!buffer->meta_data().findInt64(kKeyLastSampleIndexInChunk, &lastSample)) {
+ lastSample = -1;
+ }
+ ALOGV("sampleFileOffset:%lld", (long long)sampleFileOffset);
/*
* Reserve space in the file for the current sample + to be written MOOV box. If reservation
@@ -3412,7 +3530,7 @@
* write MOOV box successfully as space for the same was reserved in the prior call.
* Release the current buffer/sample here.
*/
- if (!mOwner->preAllocate(buffer->range_length())) {
+ if (sampleFileOffset == -1 && !mOwner->preAllocate(buffer->range_length())) {
buffer->release();
buffer = nullptr;
break;
@@ -3423,9 +3541,14 @@
// Make a deep copy of the MediaBuffer and Metadata and release
// the original as soon as we can
MediaBuffer *copy = new MediaBuffer(buffer->range_length());
- memcpy(copy->data(), (uint8_t *)buffer->data() + buffer->range_offset(),
- buffer->range_length());
+ if (sampleFileOffset != -1) {
+ copy->meta_data().setInt64(kKeySampleFileOffset, sampleFileOffset);
+ } else {
+ memcpy(copy->data(), (uint8_t*)buffer->data() + buffer->range_offset(),
+ buffer->range_length());
+ }
copy->set_range(0, buffer->range_length());
+
meta_data = new MetaData(buffer->meta_data());
buffer->release();
buffer = NULL;
@@ -3433,14 +3556,16 @@
copy->meta_data().setInt32(kKeyExifTiffOffset, tiffHdrOffset);
}
bool usePrefix = this->usePrefix() && !isExif;
-
- if (usePrefix) StripStartcode(copy);
-
+ if (sampleFileOffset == -1 && usePrefix) {
+ StripStartcode(copy);
+ }
size_t sampleSize = copy->range_length();
- if (usePrefix) {
+ if (sampleFileOffset == -1 && usePrefix) {
if (mOwner->useNalLengthFour()) {
+ ALOGV("nallength4");
sampleSize += 4;
} else {
+ ALOGV("nallength2");
sampleSize += 2;
}
}
@@ -3735,7 +3860,8 @@
chunkTimestampUs = timestampUs;
} else {
int64_t chunkDurationUs = timestampUs - chunkTimestampUs;
- if (chunkDurationUs > interleaveDurationUs) {
+ if (chunkDurationUs > interleaveDurationUs || lastSample > 1) {
+ ALOGV("lastSample:%lld", (long long)lastSample);
if (chunkDurationUs > mMaxChunkDurationUs) {
mMaxChunkDurationUs = chunkDurationUs;
}
@@ -3971,6 +4097,10 @@
return mIsRealTimeRecording;
}
+bool MPEG4Writer::isBackgroundMode() const {
+ return mIsBackgroundMode;
+}
+
bool MPEG4Writer::useNalLengthFour() {
return mUse4ByteNalLength;
}
@@ -4724,10 +4854,18 @@
// This is useful if the pixel is not square
void MPEG4Writer::Track::writePaspBox() {
- mOwner->beginBox("pasp");
- mOwner->writeInt32(1 << 16); // hspacing
- mOwner->writeInt32(1 << 16); // vspacing
- mOwner->endBox(); // pasp
+ // Do not write 'pasp' box unless the track format specifies it.
+ // According to ISO/IEC 14496-12 (ISO base media file format), 'pasp' box
+ // is optional. If present, it overrides the SAR from the video CSD. Only
+ // set it if the track format specifically requests that.
+ int32_t hSpacing, vSpacing;
+ if (mMeta->findInt32(kKeySARWidth, &hSpacing) && (hSpacing > 0)
+ && mMeta->findInt32(kKeySARHeight, &vSpacing) && (vSpacing > 0)) {
+ mOwner->beginBox("pasp");
+ mOwner->writeInt32(hSpacing); // hspacing
+ mOwner->writeInt32(vSpacing); // vspacing
+ mOwner->endBox(); // pasp
+ }
}
int64_t MPEG4Writer::Track::getStartTimeOffsetTimeUs() const {
diff --git a/media/libstagefright/MediaAdapter.cpp b/media/libstagefright/MediaAdapter.cpp
index f1b6e8c..5a2a910 100644
--- a/media/libstagefright/MediaAdapter.cpp
+++ b/media/libstagefright/MediaAdapter.cpp
@@ -114,6 +114,13 @@
return -EINVAL;
}
+ /* As mAdapterLock is unlocked while waiting for signalBufferReturned,
+ * a new buffer for the same track could be pushed from another thread
+ * in the client process, mBufferGatingMutex will help to hold that
+ * until the previous buffer is processed.
+ */
+ std::unique_lock<std::mutex> lk(mBufferGatingMutex);
+
Mutex::Autolock autoLock(mAdapterLock);
if (!mStarted) {
ALOGE("pushBuffer called before start");
diff --git a/media/libstagefright/MediaAppender.cpp b/media/libstagefright/MediaAppender.cpp
new file mode 100644
index 0000000..5d80b30
--- /dev/null
+++ b/media/libstagefright/MediaAppender.cpp
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaAppender"
+
+#include <media/stagefright/MediaAppender.h>
+#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <utils/Log.h>
+// TODO : check if this works for NDK apps without JVM
+// #include <media/ndk/NdkJavaVMHelperPriv.h>
+
+namespace android {
+
+struct MediaAppender::sampleDataInfo {
+ size_t size;
+ int64_t time;
+ size_t exTrackIndex;
+ sp<MetaData> meta;
+};
+
+sp<MediaAppender> MediaAppender::create(int fd, AppendMode mode) {
+ if (fd < 0) {
+ ALOGE("invalid file descriptor");
+ return nullptr;
+ }
+ if (!(mode >= APPEND_MODE_FIRST && mode <= APPEND_MODE_LAST)) {
+ ALOGE("invalid mode %d", mode);
+ return nullptr;
+ }
+ sp<MediaAppender> ma = new (std::nothrow) MediaAppender(fd, mode);
+ if (ma->init() != OK) {
+ return nullptr;
+ }
+ return ma;
+}
+
+// TODO: inject mediamuxer and mediaextractor objects.
+// TODO: @format is not required as an input if we can sniff the file and find the format of
+// the existing content.
+// TODO: Code it to the interface(MediaAppender), and have a separate MediaAppender NDK
+MediaAppender::MediaAppender(int fd, AppendMode mode)
+ : mFd(fd),
+ mMode(mode),
+ // TODO : check if this works for NDK apps without JVM
+ // mExtractor(new NuMediaExtractor(NdkJavaVMHelper::getJNIEnv() != nullptr
+ // ? NuMediaExtractor::EntryPoint::NDK_WITH_JVM
+ // : NuMediaExtractor::EntryPoint::NDK_NO_JVM)),
+ mExtractor(new (std::nothrow) NuMediaExtractor(NuMediaExtractor::EntryPoint::NDK_WITH_JVM)),
+ mTrackCount(0),
+ mState(UNINITIALIZED) {
+ ALOGV("MediaAppender::MediaAppender mode:%d", mode);
+ }
+
+status_t MediaAppender::init() {
+ std::scoped_lock lock(mMutex);
+ ALOGV("MediaAppender::init");
+ status_t status = mExtractor->setDataSource(mFd, 0, lseek(mFd, 0, SEEK_END));
+ if (status != OK) {
+ ALOGE("extractor_setDataSource failed, status :%d", status);
+ return status;
+ }
+
+ if (strcmp("MPEG4Extractor", mExtractor->getName()) == 0) {
+ mFormat = MediaMuxer::OUTPUT_FORMAT_MPEG_4;
+ } else {
+ ALOGE("Unsupported format, extractor name:%s", mExtractor->getName());
+ return ERROR_UNSUPPORTED;
+ }
+
+ mTrackCount = mExtractor->countTracks();
+ ALOGV("mTrackCount:%zu", mTrackCount);
+ if (mTrackCount == 0) {
+ ALOGE("no tracks are present");
+ return ERROR_MALFORMED;
+ }
+ size_t exTrackIndex = 0;
+ ssize_t audioTrackIndex = -1, videoTrackIndex = -1;
+ bool audioSyncSampleTimeSet = false;
+
+ while (exTrackIndex < mTrackCount) {
+ sp<AMessage> fmt;
+ status = mExtractor->getTrackFormat(exTrackIndex, &fmt, 0);
+ if (status != OK) {
+ ALOGE("getTrackFormat failed for trackIndex:%zu, status:%d", exTrackIndex, status);
+ return status;
+ }
+ AString mime;
+ if (fmt->findString("mime", &mime)) {
+ if (!strncasecmp(mime.c_str(), "video/", 6)) {
+ ALOGV("VideoTrack");
+ if (videoTrackIndex != -1) {
+ ALOGE("Not more than one video track is supported");
+ return ERROR_UNSUPPORTED;
+ }
+ videoTrackIndex = exTrackIndex;
+ } else if (!strncasecmp(mime.c_str(), "audio/", 6)) {
+ ALOGV("AudioTrack");
+ if (audioTrackIndex != -1) {
+ ALOGE("Not more than one audio track is supported");
+ }
+ audioTrackIndex = exTrackIndex;
+ } else {
+ ALOGV("Neither Video nor Audio track");
+ }
+ }
+ mFmtIndexMap.emplace(exTrackIndex, fmt);
+ mSampleCountVect.emplace_back(0);
+ mMaxTimestampVect.emplace_back(0);
+ mLastSyncSampleTimeVect.emplace_back(0);
+ status = mExtractor->selectTrack(exTrackIndex);
+ if (status != OK) {
+ ALOGE("selectTrack failed for trackIndex:%zu, status:%d", exTrackIndex, status);
+ return status;
+ }
+ ++exTrackIndex;
+ }
+
+ ALOGV("AudioTrackIndex:%zu, VideoTrackIndex:%zu", audioTrackIndex, videoTrackIndex);
+
+ do {
+ sampleDataInfo tmpSDI;
+ // TODO: read info into members of the struct sampleDataInfo directly
+ size_t sampleSize;
+ status = mExtractor->getSampleSize(&sampleSize);
+ if (status != OK) {
+ ALOGE("getSampleSize failed, status:%d", status);
+ return status;
+ }
+ mSampleSizeVect.emplace_back(sampleSize);
+ tmpSDI.size = sampleSize;
+ int64_t sampleTime = 0;
+ status = mExtractor->getSampleTime(&sampleTime);
+ if (status != OK) {
+ ALOGE("getSampleTime failed, status:%d", status);
+ return status;
+ }
+ mSampleTimeVect.emplace_back(sampleTime);
+ tmpSDI.time = sampleTime;
+ status = mExtractor->getSampleTrackIndex(&exTrackIndex);
+ if (status != OK) {
+ ALOGE("getSampleTrackIndex failed, status:%d", status);
+ return status;
+ }
+ mSampleIndexVect.emplace_back(exTrackIndex);
+ tmpSDI.exTrackIndex = exTrackIndex;
+ ++mSampleCountVect[exTrackIndex];
+ mMaxTimestampVect[exTrackIndex] = std::max(mMaxTimestampVect[exTrackIndex], sampleTime);
+ sp<MetaData> sampleMeta;
+ status = mExtractor->getSampleMeta(&sampleMeta);
+ if (status != OK) {
+ ALOGE("getSampleMeta failed, status:%d", status);
+ return status;
+ }
+ mSampleMetaVect.emplace_back(sampleMeta);
+ int32_t val = 0;
+ if (sampleMeta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
+ mLastSyncSampleTimeVect[exTrackIndex] = sampleTime;
+ }
+ tmpSDI.meta = sampleMeta;
+ mSDI.emplace_back(tmpSDI);
+ } while (mExtractor->advance() == OK);
+
+ mExtractor.clear();
+
+ std::sort(mSDI.begin(), mSDI.end(), [](sampleDataInfo& a, sampleDataInfo& b) {
+ int64_t aOffset, bOffset;
+ a.meta->findInt64(kKeySampleFileOffset, &aOffset);
+ b.meta->findInt64(kKeySampleFileOffset, &bOffset);
+ return aOffset < bOffset;
+ });
+ for (int64_t syncSampleTime : mLastSyncSampleTimeVect) {
+ ALOGV("before ignoring frames, mLastSyncSampleTimeVect:%lld", (long long)syncSampleTime);
+ }
+ ALOGV("mMode:%u", mMode);
+ if (mMode == APPEND_MODE_IGNORE_LAST_VIDEO_GOP && videoTrackIndex != -1 ) {
+ ALOGV("Video track is present");
+ bool lastVideoIframe = false;
+ size_t lastVideoIframeOffset = 0;
+ int64_t lastVideoSampleTime = -1;
+ for (auto rItr = mSDI.rbegin(); rItr != mSDI.rend(); ++rItr) {
+ if (rItr->exTrackIndex != videoTrackIndex) {
+ continue;
+ }
+ if (lastVideoSampleTime == -1) {
+ lastVideoSampleTime = rItr->time;
+ }
+ int64_t offset = 0;
+ if (!rItr->meta->findInt64(kKeySampleFileOffset, &offset) || offset == 0) {
+ ALOGE("Missing offset");
+ return ERROR_MALFORMED;
+ }
+ ALOGV("offset:%lld", (long long)offset);
+ int32_t val = 0;
+ if (rItr->meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
+ ALOGV("sampleTime:%lld", (long long)rItr->time);
+ ALOGV("lastVideoSampleTime:%lld", (long long)lastVideoSampleTime);
+ if (lastVideoIframe == false && (lastVideoSampleTime - rItr->time) >
+ 1000000/* Track interleaving duration in MPEG4Writer*/) {
+ ALOGV("lastVideoIframe got chosen");
+ lastVideoIframe = true;
+ mLastSyncSampleTimeVect[videoTrackIndex] = rItr->time;
+ lastVideoIframeOffset = offset;
+ ALOGV("lastVideoIframeOffset:%lld", (long long)offset);
+ break;
+ }
+ }
+ }
+ if (lastVideoIframe == false) {
+ ALOGV("Need to rewrite all samples");
+ mLastSyncSampleTimeVect[videoTrackIndex] = 0;
+ lastVideoIframeOffset = 0;
+ }
+ unsigned int framesIgnoredCount = 0;
+ for (auto itr = mSDI.begin(); itr != mSDI.end();) {
+ int64_t offset = 0;
+ ALOGV("trackIndex:%zu, %" PRId64 "", itr->exTrackIndex, itr->time);
+ if (itr->meta->findInt64(kKeySampleFileOffset, &offset) &&
+ offset >= lastVideoIframeOffset) {
+ ALOGV("offset:%lld", (long long)offset);
+ if (!audioSyncSampleTimeSet && audioTrackIndex != -1 &&
+ audioTrackIndex == itr->exTrackIndex) {
+ mLastSyncSampleTimeVect[audioTrackIndex] = itr->time;
+ audioSyncSampleTimeSet = true;
+ }
+ itr = mSDI.erase(itr);
+ ++framesIgnoredCount;
+ } else {
+ ++itr;
+ }
+ }
+ ALOGV("framesIgnoredCount:%u", framesIgnoredCount);
+ }
+
+ if (mMode == APPEND_MODE_IGNORE_LAST_VIDEO_GOP && videoTrackIndex == -1 &&
+ audioTrackIndex != -1) {
+ ALOGV("Only AudioTrack is present");
+ for (auto rItr = mSDI.rbegin(); rItr != mSDI.rend(); ++rItr) {
+ int32_t val = 0;
+ if (rItr->meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
+ mLastSyncSampleTimeVect[audioTrackIndex] = rItr->time;
+ break;
+ }
+ }
+ unsigned int framesIgnoredCount = 0;
+ for (auto itr = mSDI.begin(); itr != mSDI.end();) {
+ if (itr->time >= mLastSyncSampleTimeVect[audioTrackIndex]) {
+ itr = mSDI.erase(itr);
+ ++framesIgnoredCount;
+ } else {
+ ++itr;
+ }
+ }
+ ALOGV("framesIgnoredCount :%u", framesIgnoredCount);
+ }
+
+ for (size_t i = 0; i < mLastSyncSampleTimeVect.size(); ++i) {
+ ALOGV("mLastSyncSampleTimeVect[%zu]:%lld", i, (long long)mLastSyncSampleTimeVect[i]);
+ mFmtIndexMap[i]->setInt64(
+ "sample-time-before-append" /*AMEDIAFORMAT_KEY_SAMPLE_TIME_BEFORE_APPEND*/,
+ mLastSyncSampleTimeVect[i]);
+ }
+ for (size_t i = 0; i < mMaxTimestampVect.size(); ++i) {
+ ALOGV("mMaxTimestamp[%zu]:%lld", i, (long long)mMaxTimestampVect[i]);
+ }
+ for (size_t i = 0; i < mSampleCountVect.size(); ++i) {
+ ALOGV("SampleCountVect[%zu]:%zu", i, mSampleCountVect[i]);
+ }
+ mState = INITIALIZED;
+ return OK;
+}
+
+MediaAppender::~MediaAppender() {
+ ALOGV("MediaAppender::~MediaAppender");
+ mMuxer.clear();
+ mExtractor.clear();
+}
+
+status_t MediaAppender::start() {
+ std::scoped_lock lock(mMutex);
+ ALOGV("MediaAppender::start");
+ if (mState != INITIALIZED) {
+ ALOGE("MediaAppender::start() is called in invalid state %d", mState);
+ return INVALID_OPERATION;
+ }
+ mMuxer = new (std::nothrow) MediaMuxer(mFd, mFormat);
+ for (const auto& n : mFmtIndexMap) {
+ ssize_t muxIndex = mMuxer->addTrack(n.second);
+ if (muxIndex < 0) {
+ ALOGE("addTrack failed");
+ return UNKNOWN_ERROR;
+ }
+ mTrackIndexMap.emplace(n.first, muxIndex);
+ }
+ ALOGV("trackIndexmap size:%zu", mTrackIndexMap.size());
+
+ status_t status = mMuxer->start();
+ if (status != OK) {
+ ALOGE("muxer start failed:%d", status);
+ return status;
+ }
+
+ ALOGV("Sorting samples based on their offsets");
+ for (int i = 0; i < mSDI.size(); ++i) {
+ ALOGV("i:%d", i + 1);
+ /* TODO : Allocate a single allocation of the max size, and reuse it across ABuffers if
+ * using new ABuffer(void *, size_t).
+ */
+ sp<ABuffer> data = new (std::nothrow) ABuffer(mSDI[i].size);
+ if (data == nullptr) {
+ ALOGE("memory allocation failed");
+ return NO_MEMORY;
+ }
+ data->setRange(0, mSDI[i].size);
+ int32_t val = 0;
+ int sampleFlags = 0;
+ if (mSDI[i].meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
+ sampleFlags |= MediaCodec::BUFFER_FLAG_SYNCFRAME;
+ }
+
+ int64_t val64;
+ if (mSDI[i].meta->findInt64(kKeySampleFileOffset, &val64)) {
+ ALOGV("SampleFileOffset Found :%zu:%lld:%lld", mSDI[i].exTrackIndex,
+ (long long)mSampleCountVect[mSDI[i].exTrackIndex], (long long)val64);
+ sp<AMessage> bufMeta = data->meta();
+ bufMeta->setInt64("sample-file-offset" /*AMEDIAFORMAT_KEY_SAMPLE_TIME_BEFORE_APPEND*/,
+ val64);
+ }
+ if (mSDI[i].meta->findInt64(kKeyLastSampleIndexInChunk, &val64)) {
+ ALOGV("kKeyLastSampleIndexInChunk Found %lld:%lld",
+ (long long)mSampleCountVect[mSDI[i].exTrackIndex], (long long)val64);
+ sp<AMessage> bufMeta = data->meta();
+ bufMeta->setInt64(
+ "last-sample-index-in-chunk" /*AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK*/,
+ val64);
+ }
+ status = mMuxer->writeSampleData(data, mTrackIndexMap[mSDI[i].exTrackIndex], mSDI[i].time,
+ sampleFlags);
+ if (status != OK) {
+ ALOGE("muxer writeSampleData failed:%d", status);
+ return status;
+ }
+ }
+ mState = STARTED;
+ return OK;
+}
+
+status_t MediaAppender::stop() {
+ std::scoped_lock lock(mMutex);
+ ALOGV("MediaAppender::stop");
+ if (mState == STARTED) {
+ status_t status = mMuxer->stop();
+ if (status != OK) {
+ mState = ERROR;
+ } else {
+ mState = STOPPED;
+ }
+ return status;
+ } else {
+ ALOGE("stop() is called in invalid state %d", mState);
+ return INVALID_OPERATION;
+ }
+}
+
+ssize_t MediaAppender::getTrackCount() {
+ std::scoped_lock lock(mMutex);
+ ALOGV("MediaAppender::getTrackCount");
+ if (mState != INITIALIZED && mState != STARTED) {
+ ALOGE("getTrackCount() is called in invalid state %d", mState);
+ return -1;
+ }
+ return mTrackCount;
+}
+
+sp<AMessage> MediaAppender::getTrackFormat(size_t idx) {
+ std::scoped_lock lock(mMutex);
+ ALOGV("MediaAppender::getTrackFormat");
+ if (mState != INITIALIZED && mState != STARTED) {
+ ALOGE("getTrackFormat() is called in invalid state %d", mState);
+ return nullptr;
+ }
+ if (idx < 0 || idx >= mTrackCount) {
+ ALOGE("getTrackFormat() idx is out of range");
+ return nullptr;
+ }
+ return mFmtIndexMap[idx];
+}
+
+status_t MediaAppender::writeSampleData(const sp<ABuffer>& buffer, size_t trackIndex,
+ int64_t timeUs, uint32_t flags) {
+ std::scoped_lock lock(mMutex);
+ ALOGV("writeSampleData:trackIndex:%zu, time:%" PRId64 "", trackIndex, timeUs);
+ return mMuxer->writeSampleData(buffer, trackIndex, timeUs, flags);
+}
+
+status_t MediaAppender::setOrientationHint([[maybe_unused]] int degrees) {
+ ALOGE("setOrientationHint not supported. Has to be called prior to start on initial muxer");
+ return ERROR_UNSUPPORTED;
+};
+
+status_t MediaAppender::setLocation([[maybe_unused]] int latit, [[maybe_unused]] int longit) {
+ ALOGE("setLocation not supported. Has to be called prior to start on initial muxer");
+ return ERROR_UNSUPPORTED;
+}
+
+ssize_t MediaAppender::addTrack([[maybe_unused]] const sp<AMessage> &format) {
+ ALOGE("addTrack not supported");
+ return ERROR_UNSUPPORTED;
+}
+
+} // namespace android
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 6bf7612..57bdba0 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -20,9 +20,11 @@
#include <utils/Log.h>
#include <set>
+#include <stdlib.h>
#include <inttypes.h>
#include <stdlib.h>
+#include <dlfcn.h>
#include <C2Buffer.h>
@@ -35,6 +37,7 @@
#include <aidl/android/media/IResourceManagerService.h>
#include <android/binder_ibinder.h>
#include <android/binder_manager.h>
+#include <android/dlext.h>
#include <binder/IMemory.h>
#include <binder/MemoryDealer.h>
#include <cutils/properties.h>
@@ -47,6 +50,10 @@
#include <media/MediaCodecInfo.h>
#include <media/MediaMetricsItem.h>
#include <media/MediaResource.h>
+#include <media/NdkMediaErrorPriv.h>
+#include <media/NdkMediaFormat.h>
+#include <media/NdkMediaFormatPriv.h>
+#include <media/formatshaper/FormatShaper.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -61,12 +68,14 @@
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/MediaCodecList.h>
+#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaFilter.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/PersistentSurface.h>
#include <media/stagefright/SurfaceUtils.h>
+#include <nativeloader/dlext_namespaces.h>
#include <private/android_filesystem_config.h>
#include <utils/Singleton.h>
@@ -93,6 +102,17 @@
static const char *kCodecWidth = "android.media.mediacodec.width"; /* 0..n */
static const char *kCodecHeight = "android.media.mediacodec.height"; /* 0..n */
static const char *kCodecRotation = "android.media.mediacodec.rotation-degrees"; /* 0/90/180/270 */
+static const char *kCodecColorFormat = "android.media.mediacodec.color-format";
+static const char *kCodecFrameRate = "android.media.mediacodec.frame-rate";
+static const char *kCodecCaptureRate = "android.media.mediacodec.capture-rate";
+static const char *kCodecOperatingRate = "android.media.mediacodec.operating-rate";
+static const char *kCodecPriority = "android.media.mediacodec.priority";
+static const char *kCodecRequestedVideoQPIMin = "android.media.mediacodec.video-qp-i-min";
+static const char *kCodecRequestedVideoQPIMax = "android.media.mediacodec.video-qp-i-max";
+static const char *kCodecRequestedVideoQPPMin = "android.media.mediacodec.video-qp-p-min";
+static const char *kCodecRequestedVideoQPPMax = "android.media.mediacodec.video-qp-p-max";
+static const char *kCodecRequestedVideoQPBMin = "android.media.mediacodec.video-qp-b-min";
+static const char *kCodecRequestedVideoQPBMax = "android.media.mediacodec.video-qp-b-max";
// NB: These are not yet exposed as public Java API constants.
static const char *kCodecCrypto = "android.media.mediacodec.crypto"; /* 0,1 */
@@ -100,6 +120,7 @@
static const char *kCodecLevel = "android.media.mediacodec.level"; /* 0..n */
static const char *kCodecBitrateMode = "android.media.mediacodec.bitrate_mode"; /* CQ/VBR/CBR */
static const char *kCodecBitrate = "android.media.mediacodec.bitrate"; /* 0..n */
+static const char *kCodecOriginalBitrate = "android.media.mediacodec.original.bitrate"; /* 0..n */
static const char *kCodecMaxWidth = "android.media.mediacodec.maxwidth"; /* 0..n */
static const char *kCodecMaxHeight = "android.media.mediacodec.maxheight"; /* 0..n */
static const char *kCodecError = "android.media.mediacodec.errcode";
@@ -117,6 +138,13 @@
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 */
+static const char *kCodecChannelCount = "android.media.mediacodec.channelCount";
+static const char *kCodecSampleRate = "android.media.mediacodec.sampleRate";
+static const char *kCodecVideoEncodedBytes = "android.media.mediacodec.vencode.bytes";
+static const char *kCodecVideoEncodedFrames = "android.media.mediacodec.vencode.frames";
+static const char *kCodecVideoInputBytes = "android.media.mediacodec.video.input.bytes";
+static const char *kCodecVideoInputFrames = "android.media.mediacodec.video.input.frames";
+static const char *kCodecVideoEncodedDurationUs = "android.media.mediacodec.vencode.durationUs";
// the kCodecRecent* fields appear only in getMetrics() results
static const char *kCodecRecentLatencyMax = "android.media.mediacodec.recent.max"; /* in us */
@@ -125,6 +153,8 @@
static const char *kCodecRecentLatencyCount = "android.media.mediacodec.recent.n";
static const char *kCodecRecentLatencyHist = "android.media.mediacodec.recent.hist"; /* in us */
+static const char *kCodecShapingEnhanced = "android.media.mediacodec.shaped"; /* 0/1 */
+
// XXX suppress until we get our representation right
static bool kEmitHistogram = false;
@@ -253,14 +283,14 @@
return;
}
+ // Kill clients pending removal.
+ mService->reclaimResourcesFromClientsPendingRemoval(mPid);
+
// so our handler will process the death notifications
addCookie(this);
// after this, require mLock whenever using mService
AIBinder_linkToDeath(mService->asBinder().get(), mDeathRecipient.get(), this);
-
- // Kill clients pending removal.
- mService->reclaimResourcesFromClientsPendingRemoval(mPid);
}
//static
@@ -410,6 +440,7 @@
kWhatSignaledInputEOS = 'seos',
kWhatOutputFramesRendered = 'outR',
kWhatOutputBuffersChanged = 'outC',
+ kWhatFirstTunnelFrameReady = 'ftfR',
};
class BufferCallback : public CodecBase::BufferCallback {
@@ -472,6 +503,7 @@
virtual void onSignaledInputEOS(status_t err) override;
virtual void onOutputFramesRendered(const std::list<FrameRenderTracker::Info> &done) override;
virtual void onOutputBuffersChanged() override;
+ virtual void onFirstTunnelFrameReady() override;
private:
const sp<AMessage> mNotify;
};
@@ -592,6 +624,12 @@
notify->post();
}
+void CodecCallback::onFirstTunnelFrameReady() {
+ sp<AMessage> notify(mNotify->dup());
+ notify->setInt32("what", kWhatFirstTunnelFrameReady);
+ notify->post();
+}
+
} // namespace
////////////////////////////////////////////////////////////////////////////////
@@ -600,12 +638,20 @@
sp<MediaCodec> MediaCodec::CreateByType(
const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err, pid_t pid,
uid_t uid) {
+ sp<AMessage> format;
+ return CreateByType(looper, mime, encoder, err, pid, uid, format);
+}
+
+sp<MediaCodec> MediaCodec::CreateByType(
+ const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err, pid_t pid,
+ uid_t uid, sp<AMessage> format) {
Vector<AString> matchingCodecs;
MediaCodecList::findMatchingCodecs(
mime.c_str(),
encoder,
0,
+ format,
&matchingCodecs);
if (err != NULL) {
@@ -690,10 +736,15 @@
mTunneledInputWidth(0),
mTunneledInputHeight(0),
mTunneled(false),
+ mTunnelPeekState(TunnelPeekState::kEnabledNoBuffer),
mHaveInputSurface(false),
mHavePendingInputBuffers(false),
mCpuBoostRequested(false),
mLatencyUnknown(0),
+ mBytesEncoded(0),
+ mEarliestEncodedPtsUs(INT64_MAX),
+ mLatestEncodedPtsUs(INT64_MIN),
+ mFramesEncoded(0),
mNumLowLatencyEnables(0),
mNumLowLatencyDisables(0),
mIsLowLatencyModeOn(false),
@@ -801,6 +852,20 @@
mediametrics_setInt64(mMetricsHandle, kCodecLifetimeMs, lifetime);
}
+ if (mBytesEncoded) {
+ Mutex::Autolock al(mOutputStatsLock);
+
+ mediametrics_setInt64(mMetricsHandle, kCodecVideoEncodedBytes, mBytesEncoded);
+ int64_t duration = 0;
+ if (mLatestEncodedPtsUs > mEarliestEncodedPtsUs) {
+ duration = mLatestEncodedPtsUs - mEarliestEncodedPtsUs;
+ }
+ mediametrics_setInt64(mMetricsHandle, kCodecVideoEncodedDurationUs, duration);
+ mediametrics_setInt64(mMetricsHandle, kCodecVideoEncodedFrames, mFramesEncoded);
+ mediametrics_setInt64(mMetricsHandle, kCodecVideoInputFrames, mFramesInput);
+ mediametrics_setInt64(mMetricsHandle, kCodecVideoInputBytes, mBytesInput);
+ }
+
{
Mutex::Autolock al(mLatencyLock);
mediametrics_setInt64(mMetricsHandle, kCodecNumLowLatencyModeOn, mNumLowLatencyEnables);
@@ -879,6 +944,47 @@
}
}
+constexpr const char *MediaCodec::asString(TunnelPeekState state, const char *default_string){
+ switch(state) {
+ case TunnelPeekState::kEnabledNoBuffer:
+ return "EnabledNoBuffer";
+ case TunnelPeekState::kDisabledNoBuffer:
+ return "DisabledNoBuffer";
+ case TunnelPeekState::kBufferDecoded:
+ return "BufferDecoded";
+ case TunnelPeekState::kBufferRendered:
+ return "BufferRendered";
+ default:
+ return default_string;
+ }
+}
+
+void MediaCodec::updateTunnelPeek(const sp<AMessage> &msg) {
+ int32_t tunnelPeek = 0;
+ if (!msg->findInt32("tunnel-peek", &tunnelPeek)){
+ return;
+ }
+ if(tunnelPeek == 0){
+ if (mTunnelPeekState == TunnelPeekState::kEnabledNoBuffer) {
+ mTunnelPeekState = TunnelPeekState::kDisabledNoBuffer;
+ ALOGV("TunnelPeekState: %s -> %s",
+ asString(TunnelPeekState::kEnabledNoBuffer),
+ asString(TunnelPeekState::kDisabledNoBuffer));
+ return;
+ }
+ } else {
+ if (mTunnelPeekState == TunnelPeekState::kDisabledNoBuffer) {
+ mTunnelPeekState = TunnelPeekState::kEnabledNoBuffer;
+ ALOGV("TunnelPeekState: %s -> %s",
+ asString(TunnelPeekState::kDisabledNoBuffer),
+ asString(TunnelPeekState::kEnabledNoBuffer));
+ return;
+ }
+ }
+
+ ALOGV("Ignoring tunnel-peek=%d for %s", tunnelPeek, asString(mTunnelPeekState));
+}
+
bool MediaCodec::Histogram::setup(int nbuckets, int64_t width, int64_t floor)
{
if (nbuckets <= 0 || width <= 0) {
@@ -970,7 +1076,7 @@
}
// when we send a buffer to the codec;
-void MediaCodec::statsBufferSent(int64_t presentationUs) {
+void MediaCodec::statsBufferSent(int64_t presentationUs, const sp<MediaCodecBuffer> &buffer) {
// only enqueue if we have a legitimate time
if (presentationUs <= 0) {
@@ -984,6 +1090,11 @@
});
}
+ if (mIsVideo && (mFlags & kFlagIsEncoder)) {
+ mBytesInput += buffer->size();
+ mFramesInput++;
+ }
+
const int64_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
BufferFlightTiming_t startdata = { presentationUs, nowNs };
@@ -1004,10 +1115,34 @@
}
// when we get a buffer back from the codec
-void MediaCodec::statsBufferReceived(int64_t presentationUs) {
+void MediaCodec::statsBufferReceived(int64_t presentationUs, const sp<MediaCodecBuffer> &buffer) {
CHECK_NE(mState, UNINITIALIZED);
+ if (mIsVideo && (mFlags & kFlagIsEncoder)) {
+ int32_t flags = 0;
+ (void) buffer->meta()->findInt32("flags", &flags);
+
+ // some of these frames, we don't want to count
+ // standalone EOS.... has an invalid timestamp
+ if ((flags & (BUFFER_FLAG_CODECCONFIG|BUFFER_FLAG_EOS)) == 0) {
+ mBytesEncoded += buffer->size();
+ mFramesEncoded++;
+
+ Mutex::Autolock al(mOutputStatsLock);
+ int64_t timeUs = 0;
+ if (buffer->meta()->findInt64("timeUs", &timeUs)) {
+ if (timeUs > mLatestEncodedPtsUs) {
+ mLatestEncodedPtsUs = timeUs;
+ }
+ // can't chain as an else-if or this never triggers
+ if (timeUs < mEarliestEncodedPtsUs) {
+ mEarliestEncodedPtsUs = timeUs;
+ }
+ }
+ }
+ }
+
// mutex access to mBuffersInFlight and other stats
Mutex::Autolock al(mLatencyLock);
@@ -1063,7 +1198,7 @@
return;
}
- // nowNs start our calculations
+ // now start our calculations
const int64_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
int64_t latencyUs = (nowNs - startdata.startedNs + 500) / 1000;
@@ -1280,6 +1415,21 @@
return msg->post();
}
+status_t MediaCodec::setOnFirstTunnelFrameReadyNotification(const sp<AMessage> ¬ify) {
+ sp<AMessage> msg = new AMessage(kWhatSetNotification, this);
+ msg->setMessage("first-tunnel-frame-ready", notify);
+ return msg->post();
+}
+
+/*
+ * MediaFormat Shaping forward declarations
+ * including the property name we use for control.
+ */
+static int enableMediaFormatShapingDefault = 1;
+static const char enableMediaFormatShapingProperty[] = "debug.stagefright.enableshaping";
+static void mapFormat(AString componentName, const sp<AMessage> &format, const char *kind,
+ bool reverse);
+
status_t MediaCodec::configure(
const sp<AMessage> &format,
const sp<Surface> &nativeWindow,
@@ -1328,6 +1478,50 @@
if (format->findInt32("max-height", &maxHeight)) {
mediametrics_setInt32(mMetricsHandle, kCodecMaxHeight, maxHeight);
}
+ int32_t colorFormat = -1;
+ if (format->findInt32("color-format", &colorFormat)) {
+ mediametrics_setInt32(mMetricsHandle, kCodecColorFormat, colorFormat);
+ }
+ float frameRate = -1.0;
+ if (format->findFloat("frame-rate", &frameRate)) {
+ mediametrics_setDouble(mMetricsHandle, kCodecFrameRate, frameRate);
+ }
+ float captureRate = -1.0;
+ if (format->findFloat("capture-rate", &captureRate)) {
+ mediametrics_setDouble(mMetricsHandle, kCodecCaptureRate, captureRate);
+ }
+ float operatingRate = -1.0;
+ if (format->findFloat("operating-rate", &operatingRate)) {
+ mediametrics_setDouble(mMetricsHandle, kCodecOperatingRate, operatingRate);
+ }
+ int32_t priority = -1;
+ if (format->findInt32("priority", &priority)) {
+ mediametrics_setInt32(mMetricsHandle, kCodecPriority, priority);
+ }
+ int32_t qpIMin = -1;
+ if (format->findInt32("video-qp-i-min", &qpIMin)) {
+ mediametrics_setInt32(mMetricsHandle, kCodecRequestedVideoQPIMin, qpIMin);
+ }
+ int32_t qpIMax = -1;
+ if (format->findInt32("video-qp-i-max", &qpIMax)) {
+ mediametrics_setInt32(mMetricsHandle, kCodecRequestedVideoQPIMax, qpIMax);
+ }
+ int32_t qpPMin = -1;
+ if (format->findInt32("video-qp-p-min", &qpPMin)) {
+ mediametrics_setInt32(mMetricsHandle, kCodecRequestedVideoQPPMin, qpPMin);
+ }
+ int32_t qpPMax = -1;
+ if (format->findInt32("video-qp-p-max", &qpPMax)) {
+ mediametrics_setInt32(mMetricsHandle, kCodecRequestedVideoQPPMax, qpPMax);
+ }
+ int32_t qpBMin = -1;
+ if (format->findInt32("video-qp-b-min", &qpBMin)) {
+ mediametrics_setInt32(mMetricsHandle, kCodecRequestedVideoQPBMin, qpBMin);
+ }
+ int32_t qpBMax = -1;
+ if (format->findInt32("video-qp-b-max", &qpBMax)) {
+ mediametrics_setInt32(mMetricsHandle, kCodecRequestedVideoQPBMax, qpBMax);
+ }
}
// Prevent possible integer overflow in downstream code.
@@ -1336,6 +1530,30 @@
ALOGE("Invalid size(s), width=%d, height=%d", mVideoWidth, mVideoHeight);
return BAD_VALUE;
}
+
+ } else {
+ if (mMetricsHandle != 0) {
+ int32_t channelCount;
+ if (format->findInt32(KEY_CHANNEL_COUNT, &channelCount)) {
+ mediametrics_setInt32(mMetricsHandle, kCodecChannelCount, channelCount);
+ }
+ int32_t sampleRate;
+ if (format->findInt32(KEY_SAMPLE_RATE, &sampleRate)) {
+ mediametrics_setInt32(mMetricsHandle, kCodecSampleRate, sampleRate);
+ }
+ }
+ }
+
+ if (flags & CONFIGURE_FLAG_ENCODE) {
+ int8_t enableShaping = property_get_bool(enableMediaFormatShapingProperty,
+ enableMediaFormatShapingDefault);
+ if (!enableShaping) {
+ ALOGI("format shaping disabled, property '%s'", enableMediaFormatShapingProperty);
+ } else {
+ (void) shapeMediaFormat(format, flags);
+ // XXX: do we want to do this regardless of shaping enablement?
+ mapFormat(mComponentName, format, nullptr, false);
+ }
}
updateLowLatency(format);
@@ -1403,6 +1621,358 @@
return err;
}
+// Media Format Shaping support
+//
+
+static android::mediaformatshaper::FormatShaperOps_t *sShaperOps = NULL;
+
+static bool connectFormatShaper() {
+ static std::once_flag sCheckOnce;
+
+ ALOGV("connectFormatShaper...");
+
+ std::call_once(sCheckOnce, [&](){
+
+ void *libHandle = NULL;
+ nsecs_t loading_started = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ // prefer any copy in the mainline module
+ //
+ android_namespace_t *mediaNs = android_get_exported_namespace("com_android_media");
+ AString libraryName = "libmediaformatshaper.so";
+
+ if (mediaNs != NULL) {
+ static const android_dlextinfo dlextinfo = {
+ .flags = ANDROID_DLEXT_USE_NAMESPACE,
+ .library_namespace = mediaNs,
+ };
+
+ AString libraryMainline = "/apex/com.android.media/";
+#if __LP64__
+ libraryMainline.append("lib64/");
+#else
+ libraryMainline.append("lib/");
+#endif
+ libraryMainline.append(libraryName);
+
+ libHandle = android_dlopen_ext(libraryMainline.c_str(), RTLD_NOW|RTLD_NODELETE,
+ &dlextinfo);
+
+ if (libHandle != NULL) {
+ sShaperOps = (android::mediaformatshaper::FormatShaperOps_t*)
+ dlsym(libHandle, "shaper_ops");
+ } else {
+ ALOGW("connectFormatShaper: unable to load mainline formatshaper %s",
+ libraryMainline.c_str());
+ }
+ } else {
+ ALOGV("connectFormatShaper: couldn't find media namespace.");
+ }
+
+ // fall back to the system partition, if present.
+ //
+ if (sShaperOps == NULL) {
+
+ libHandle = dlopen(libraryName.c_str(), RTLD_NOW|RTLD_NODELETE);
+
+ if (libHandle != NULL) {
+ sShaperOps = (android::mediaformatshaper::FormatShaperOps_t*)
+ dlsym(libHandle, "shaper_ops");
+ } else {
+ ALOGW("connectFormatShaper: unable to load formatshaper %s", libraryName.c_str());
+ }
+ }
+
+ if (sShaperOps != nullptr
+ && sShaperOps->version != android::mediaformatshaper::SHAPER_VERSION_V1) {
+ ALOGW("connectFormatShaper: unhandled version ShaperOps: %d, DISABLED",
+ sShaperOps->version);
+ sShaperOps = nullptr;
+ }
+
+ if (sShaperOps != nullptr) {
+ ALOGV("connectFormatShaper: connected to library %s", libraryName.c_str());
+ }
+
+ nsecs_t loading_finished = systemTime(SYSTEM_TIME_MONOTONIC);
+ ALOGV("connectFormatShaper: loaded libraries: %" PRId64 " us",
+ (loading_finished - loading_started)/1000);
+
+ });
+
+ return true;
+}
+
+
+#if 0
+// a construct to force the above dlopen() to run very early.
+// goal: so the dlopen() doesn't happen on critical path of latency sensitive apps
+// failure of this means that cold start of those apps is slower by the time to dlopen()
+// TODO(b/183454066): tradeoffs between memory of early loading vs latency of late loading
+//
+static bool forceEarlyLoadingShaper = connectFormatShaper();
+#endif
+
+// parse the codec's properties: mapping, whether it meets min quality, etc
+// and pass them into the video quality code
+//
+static void loadCodecProperties(mediaformatshaper::shaperHandle_t shaperHandle,
+ sp<MediaCodecInfo> codecInfo, AString mediaType) {
+
+ sp<MediaCodecInfo::Capabilities> capabilities =
+ codecInfo->getCapabilitiesFor(mediaType.c_str());
+ if (capabilities == nullptr) {
+ ALOGI("no capabilities as part of the codec?");
+ } else {
+ const sp<AMessage> &details = capabilities->getDetails();
+ AString mapTarget;
+ int count = details->countEntries();
+ for(int ix = 0; ix < count; ix++) {
+ AMessage::Type entryType;
+ const char *mapSrc = details->getEntryNameAt(ix, &entryType);
+ // XXX: re-use ix from getEntryAt() to avoid additional findXXX() invocation
+ //
+ static const char *featurePrefix = "feature-";
+ static const int featurePrefixLen = strlen(featurePrefix);
+ static const char *tuningPrefix = "tuning-";
+ static const int tuningPrefixLen = strlen(tuningPrefix);
+ static const char *mappingPrefix = "mapping-";
+ static const int mappingPrefixLen = strlen(mappingPrefix);
+
+ if (mapSrc == NULL) {
+ continue;
+ } else if (!strncmp(mapSrc, featurePrefix, featurePrefixLen)) {
+ int32_t intValue;
+ if (details->findInt32(mapSrc, &intValue)) {
+ ALOGV("-- feature '%s' -> %d", mapSrc, intValue);
+ (void)(sShaperOps->setFeature)(shaperHandle, &mapSrc[featurePrefixLen],
+ intValue);
+ }
+ continue;
+ } else if (!strncmp(mapSrc, tuningPrefix, tuningPrefixLen)) {
+ AString value;
+ if (details->findString(mapSrc, &value)) {
+ ALOGV("-- tuning '%s' -> '%s'", mapSrc, value.c_str());
+ (void)(sShaperOps->setTuning)(shaperHandle, &mapSrc[tuningPrefixLen],
+ value.c_str());
+ }
+ continue;
+ } else if (!strncmp(mapSrc, mappingPrefix, mappingPrefixLen)) {
+ AString target;
+ if (details->findString(mapSrc, &target)) {
+ ALOGV("-- mapping %s: map %s to %s", mapSrc, &mapSrc[mappingPrefixLen],
+ target.c_str());
+ // key is really "kind-key"
+ // separate that, so setMap() sees the triple kind, key, value
+ const char *kind = &mapSrc[mappingPrefixLen];
+ const char *sep = strchr(kind, '-');
+ const char *key = sep+1;
+ if (sep != NULL) {
+ std::string xkind = std::string(kind, sep-kind);
+ (void)(sShaperOps->setMap)(shaperHandle, xkind.c_str(),
+ key, target.c_str());
+ }
+ }
+ }
+ }
+ }
+}
+
+status_t MediaCodec::setupFormatShaper(AString mediaType) {
+ ALOGV("setupFormatShaper: initializing shaper data for codec %s mediaType %s",
+ mComponentName.c_str(), mediaType.c_str());
+
+ nsecs_t mapping_started = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ // someone might have beaten us to it.
+ mediaformatshaper::shaperHandle_t shaperHandle;
+ shaperHandle = sShaperOps->findShaper(mComponentName.c_str(), mediaType.c_str());
+ if (shaperHandle != nullptr) {
+ ALOGV("shaperhandle %p -- no initialization needed", shaperHandle);
+ return OK;
+ }
+
+ // we get to build & register one
+ shaperHandle = sShaperOps->createShaper(mComponentName.c_str(), mediaType.c_str());
+ if (shaperHandle == nullptr) {
+ ALOGW("unable to create a shaper for cocodec %s mediaType %s",
+ mComponentName.c_str(), mediaType.c_str());
+ return OK;
+ }
+
+ (void) loadCodecProperties(shaperHandle, mCodecInfo, mediaType);
+
+ shaperHandle = sShaperOps->registerShaper(shaperHandle,
+ mComponentName.c_str(), mediaType.c_str());
+
+ nsecs_t mapping_finished = systemTime(SYSTEM_TIME_MONOTONIC);
+ ALOGV("setupFormatShaper: populated shaper node for codec %s: %" PRId64 " us",
+ mComponentName.c_str(), (mapping_finished - mapping_started)/1000);
+
+ return OK;
+}
+
+
+// Format Shaping
+// Mapping and Manipulation of encoding parameters
+//
+
+status_t MediaCodec::shapeMediaFormat(
+ const sp<AMessage> &format,
+ uint32_t flags) {
+ ALOGV("shapeMediaFormat entry");
+
+ if (!(flags & CONFIGURE_FLAG_ENCODE)) {
+ ALOGW("shapeMediaFormat: not encoder");
+ return OK;
+ }
+ if (mCodecInfo == NULL) {
+ ALOGW("shapeMediaFormat: no codecinfo");
+ return OK;
+ }
+
+ AString mediaType;
+ if (!format->findString("mime", &mediaType)) {
+ ALOGW("shapeMediaFormat: no mediaType information");
+ return OK;
+ }
+
+ // make sure we have the function entry points for the shaper library
+ //
+
+ connectFormatShaper();
+ if (sShaperOps == nullptr) {
+ ALOGW("shapeMediaFormat: no MediaFormatShaper hooks available");
+ return OK;
+ }
+
+ // find the shaper information for this codec+mediaType pair
+ //
+ mediaformatshaper::shaperHandle_t shaperHandle;
+ shaperHandle = sShaperOps->findShaper(mComponentName.c_str(), mediaType.c_str());
+ if (shaperHandle == nullptr) {
+ setupFormatShaper(mediaType);
+ shaperHandle = sShaperOps->findShaper(mComponentName.c_str(), mediaType.c_str());
+ }
+ if (shaperHandle == nullptr) {
+ ALOGW("shapeMediaFormat: no handler for codec %s mediatype %s",
+ mComponentName.c_str(), mediaType.c_str());
+ return OK;
+ }
+
+ // run the shaper
+ //
+
+ ALOGV("Shaping input: %s", format->debugString(0).c_str());
+
+ sp<AMessage> updatedFormat = format->dup();
+ AMediaFormat *updatedNdkFormat = AMediaFormat_fromMsg(&updatedFormat);
+
+ int result = (*sShaperOps->shapeFormat)(shaperHandle, updatedNdkFormat, flags);
+ if (result == 0) {
+ AMediaFormat_getFormat(updatedNdkFormat, &updatedFormat);
+
+ sp<AMessage> deltas = updatedFormat->changesFrom(format, false /* deep */);
+ size_t changeCount = deltas->countEntries();
+ ALOGD("shapeMediaFormat: deltas(%zu): %s", changeCount, deltas->debugString(2).c_str());
+ if (changeCount > 0) {
+ if (mMetricsHandle != 0) {
+ mediametrics_setInt32(mMetricsHandle, kCodecShapingEnhanced, changeCount);
+ // save some old properties before we fold in the new ones
+ int32_t bitrate;
+ if (format->findInt32(KEY_BIT_RATE, &bitrate)) {
+ mediametrics_setInt32(mMetricsHandle, kCodecOriginalBitrate, bitrate);
+ }
+ }
+ // NB: for any field in both format and deltas, the deltas copy wins
+ format->extend(deltas);
+ }
+ }
+
+ AMediaFormat_delete(updatedNdkFormat);
+ return OK;
+}
+
+static void mapFormat(AString componentName, const sp<AMessage> &format, const char *kind,
+ bool reverse) {
+ AString mediaType;
+ if (!format->findString("mime", &mediaType)) {
+ ALOGW("mapFormat: no mediaType information");
+ return;
+ }
+ ALOGV("mapFormat: codec %s mediatype %s kind %s reverse %d", componentName.c_str(),
+ mediaType.c_str(), kind ? kind : "<all>", reverse);
+
+ // make sure we have the function entry points for the shaper library
+ //
+
+#if 0
+ // let's play the faster "only do mapping if we've already loaded the library
+ connectFormatShaper();
+#endif
+ if (sShaperOps == nullptr) {
+ ALOGV("mapFormat: no MediaFormatShaper hooks available");
+ return;
+ }
+
+ // find the shaper information for this codec+mediaType pair
+ //
+ mediaformatshaper::shaperHandle_t shaperHandle;
+ shaperHandle = sShaperOps->findShaper(componentName.c_str(), mediaType.c_str());
+ if (shaperHandle == nullptr) {
+ ALOGV("mapFormat: no shaper handle");
+ return;
+ }
+
+ const char **mappings;
+ if (reverse)
+ mappings = sShaperOps->getReverseMappings(shaperHandle, kind);
+ else
+ mappings = sShaperOps->getMappings(shaperHandle, kind);
+
+ if (mappings == nullptr) {
+ ALOGV("no mappings returned");
+ return;
+ }
+
+ ALOGV("Pre-mapping: %s", format->debugString(2).c_str());
+ // do the mapping
+ //
+ int entries = format->countEntries();
+ for (int i = 0; ; i += 2) {
+ if (mappings[i] == nullptr) {
+ break;
+ }
+
+ size_t ix = format->findEntryByName(mappings[i]);
+ if (ix < entries) {
+ ALOGV("map '%s' to '%s'", mappings[i], mappings[i+1]);
+ status_t status = format->setEntryNameAt(ix, mappings[i+1]);
+ if (status != OK) {
+ ALOGW("Unable to map from '%s' to '%s': status %d",
+ mappings[i], mappings[i+1], status);
+ }
+ }
+ }
+ ALOGV("Post-mapping: %s", format->debugString(2).c_str());
+
+
+ // reclaim the mapping memory
+ for (int i = 0; ; i += 2) {
+ if (mappings[i] == nullptr) {
+ break;
+ }
+ free((void*)mappings[i]);
+ free((void*)mappings[i + 1]);
+ }
+ free(mappings);
+ mappings = nullptr;
+}
+
+//
+// end of Format Shaping hooks within MediaCodec
+//
+
status_t MediaCodec::releaseCrypto()
{
ALOGV("releaseCrypto");
@@ -2194,14 +2764,15 @@
int64_t timeUs;
CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
- statsBufferReceived(timeUs);
-
response->setInt64("timeUs", timeUs);
int32_t flags;
CHECK(buffer->meta()->findInt32("flags", &flags));
response->setInt32("flags", flags);
+
+ statsBufferReceived(timeUs, buffer);
+
response->postReply(replyID);
}
@@ -2222,8 +2793,8 @@
CHECK(msg->findInt32("err", &err));
CHECK(msg->findInt32("actionCode", &actionCode));
- ALOGE("Codec reported err %#x, actionCode %d, while in state %d",
- err, actionCode, mState);
+ ALOGE("Codec reported err %#x, actionCode %d, while in state %d/%s",
+ err, actionCode, mState, stateString(mState).c_str());
if (err == DEAD_OBJECT) {
mFlags |= kFlagSawMediaServerDie;
mFlags &= ~kFlagIsComponentAllocated;
@@ -2400,8 +2971,8 @@
if (mState == RELEASING || mState == UNINITIALIZED) {
// In case a kWhatError or kWhatRelease message came in and replied,
// we log a warning and ignore.
- ALOGW("allocate interrupted by error or release, current state %d",
- mState);
+ ALOGW("allocate interrupted by error or release, current state %d/%s",
+ mState, stateString(mState).c_str());
break;
}
CHECK_EQ(mState, INITIALIZING);
@@ -2447,8 +3018,8 @@
if (mState == RELEASING || mState == UNINITIALIZED || mState == INITIALIZED) {
// In case a kWhatError or kWhatRelease message came in and replied,
// we log a warning and ignore.
- ALOGW("configure interrupted by error or release, current state %d",
- mState);
+ ALOGW("configure interrupted by error or release, current state %d/%s",
+ mState, stateString(mState).c_str());
break;
}
CHECK_EQ(mState, CONFIGURING);
@@ -2463,7 +3034,7 @@
if (mSurface != nullptr && !mAllowFrameDroppingBySurface) {
// signal frame dropping mode in the input format as this may also be
// meaningful and confusing for an encoder in a transcoder scenario
- mInputFormat->setInt32("allow-frame-drop", mAllowFrameDroppingBySurface);
+ mInputFormat->setInt32(KEY_ALLOW_FRAME_DROP, mAllowFrameDroppingBySurface);
}
sp<AMessage> interestingFormat =
(mFlags & kFlagIsEncoder) ? mOutputFormat : mInputFormat;
@@ -2595,7 +3166,8 @@
if (mState == RELEASING || mState == UNINITIALIZED) {
// In case a kWhatRelease message came in and replied,
// we log a warning and ignore.
- ALOGW("start interrupted by release, current state %d", mState);
+ ALOGW("start interrupted by release, current state %d/%s",
+ mState, stateString(mState).c_str());
break;
}
@@ -2618,9 +3190,17 @@
case kWhatOutputFramesRendered:
{
- // ignore these in all states except running, and check that we have a
- // notification set
- if (mState == STARTED && mOnFrameRenderedNotification != NULL) {
+ // ignore these in all states except running
+ if (mState != STARTED) {
+ break;
+ }
+ TunnelPeekState previousState = mTunnelPeekState;
+ mTunnelPeekState = TunnelPeekState::kBufferRendered;
+ ALOGV("TunnelPeekState: %s -> %s",
+ asString(previousState),
+ asString(TunnelPeekState::kBufferRendered));
+ // check that we have a notification set
+ if (mOnFrameRenderedNotification != NULL) {
sp<AMessage> notify = mOnFrameRenderedNotification->dup();
notify->setMessage("data", msg);
notify->post();
@@ -2628,6 +3208,41 @@
break;
}
+ case kWhatFirstTunnelFrameReady:
+ {
+ if (mState != STARTED) {
+ break;
+ }
+ switch(mTunnelPeekState) {
+ case TunnelPeekState::kDisabledNoBuffer:
+ mTunnelPeekState = TunnelPeekState::kBufferDecoded;
+ ALOGV("TunnelPeekState: %s -> %s",
+ asString(TunnelPeekState::kDisabledNoBuffer),
+ asString(TunnelPeekState::kBufferDecoded));
+ break;
+ case TunnelPeekState::kEnabledNoBuffer:
+ mTunnelPeekState = TunnelPeekState::kBufferDecoded;
+ ALOGV("TunnelPeekState: %s -> %s",
+ asString(TunnelPeekState::kEnabledNoBuffer),
+ asString(TunnelPeekState::kBufferDecoded));
+ {
+ sp<AMessage> parameters = new AMessage();
+ parameters->setInt32("android._trigger-tunnel-peek", 1);
+ mCodec->signalSetParameters(parameters);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (mOnFirstTunnelFrameReadyNotification != nullptr) {
+ sp<AMessage> notify = mOnFirstTunnelFrameReadyNotification->dup();
+ notify->setMessage("data", msg);
+ notify->post();
+ }
+ break;
+ }
+
case kWhatFillThisBuffer:
{
/* size_t index = */updateBuffers(kPortIndexInput, msg);
@@ -2737,7 +3352,8 @@
case kWhatStopCompleted:
{
if (mState != STOPPING) {
- ALOGW("Received kWhatStopCompleted in state %d", mState);
+ ALOGW("Received kWhatStopCompleted in state %d/%s",
+ mState, stateString(mState).c_str());
break;
}
setState(INITIALIZED);
@@ -2754,7 +3370,8 @@
case kWhatReleaseCompleted:
{
if (mState != RELEASING) {
- ALOGW("Received kWhatReleaseCompleted in state %d", mState);
+ ALOGW("Received kWhatReleaseCompleted in state %d/%s",
+ mState, stateString(mState).c_str());
break;
}
setState(UNINITIALIZED);
@@ -2784,8 +3401,8 @@
case kWhatFlushCompleted:
{
if (mState != FLUSHING) {
- ALOGW("received FlushCompleted message in state %d",
- mState);
+ ALOGW("received FlushCompleted message in state %d/%s",
+ mState, stateString(mState).c_str());
break;
}
@@ -2844,6 +3461,9 @@
if (msg->findMessage("on-frame-rendered", ¬ify)) {
mOnFrameRenderedNotification = notify;
}
+ if (msg->findMessage("first-tunnel-frame-ready", ¬ify)) {
+ mOnFirstTunnelFrameReadyNotification = notify;
+ }
break;
}
@@ -2905,7 +3525,7 @@
}
if (obj != NULL) {
- if (!format->findInt32("allow-frame-drop", &mAllowFrameDroppingBySurface)) {
+ if (!format->findInt32(KEY_ALLOW_FRAME_DROP, &mAllowFrameDroppingBySurface)) {
// allow frame dropping by surface by default
mAllowFrameDroppingBySurface = true;
}
@@ -2973,6 +3593,11 @@
mTunneled = false;
}
+ int32_t background = 0;
+ if (format->findInt32("android._background-mode", &background) && background) {
+ androidSetThreadPriority(gettid(), ANDROID_PRIORITY_BACKGROUND);
+ }
+
mCodec->initiateConfigureComponent(format);
break;
}
@@ -3085,6 +3710,11 @@
}
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
+ TunnelPeekState previousState = mTunnelPeekState;
+ mTunnelPeekState = TunnelPeekState::kEnabledNoBuffer;
+ ALOGV("TunnelPeekState: %s -> %s",
+ asString(previousState),
+ asString(TunnelPeekState::kEnabledNoBuffer));
mReplyID = replyID;
setState(STARTING);
@@ -3140,7 +3770,13 @@
int32_t reclaimed = 0;
msg->findInt32("reclaimed", &reclaimed);
if (reclaimed) {
- mReleasedByResourceManager = true;
+ if (!mReleasedByResourceManager) {
+ // notify the async client
+ if (mFlags & kFlagIsAsync) {
+ onError(DEAD_OBJECT, ACTION_CODE_FATAL);
+ }
+ mReleasedByResourceManager = true;
+ }
int32_t force = 0;
msg->findInt32("force", &force);
@@ -3152,10 +3788,6 @@
response->setInt32("err", WOULD_BLOCK);
response->postReply(replyID);
- // notify the async client
- if (mFlags & kFlagIsAsync) {
- onError(DEAD_OBJECT, ACTION_CODE_FATAL);
- }
break;
}
}
@@ -3517,6 +4149,11 @@
mCodec->signalFlush();
returnBuffersToCodec();
+ TunnelPeekState previousState = mTunnelPeekState;
+ mTunnelPeekState = TunnelPeekState::kEnabledNoBuffer;
+ ALOGV("TunnelPeekState: %s -> %s",
+ asString(previousState),
+ asString(TunnelPeekState::kEnabledNoBuffer));
break;
}
@@ -3647,6 +4284,7 @@
buffer->meta()->setObject("changedKeys", changedKeys);
}
mOutputFormat = format;
+ mapFormat(mComponentName, format, nullptr, true);
ALOGV("[%s] output format changed to: %s",
mComponentName.c_str(), mOutputFormat->debugString(4).c_str());
@@ -4102,7 +4740,7 @@
info->mOwnedByClient = false;
info->mData.clear();
- statsBufferSent(timeUs);
+ statsBufferSent(timeUs, buffer);
}
return err;
@@ -4353,13 +4991,13 @@
msg->setInt64("timeUs", timeUs);
- statsBufferReceived(timeUs);
-
int32_t flags;
CHECK(buffer->meta()->findInt32("flags", &flags));
msg->setInt32("flags", flags);
+ statsBufferReceived(timeUs, buffer);
+
msg->post();
}
}
@@ -4426,6 +5064,8 @@
status_t MediaCodec::onSetParameters(const sp<AMessage> ¶ms) {
updateLowLatency(params);
+ mapFormat(mComponentName, params, nullptr, false);
+ updateTunnelPeek(params);
mCodec->signalSetParameters(params);
return OK;
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index 799ca0d..6243828 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -44,6 +44,7 @@
#include <cutils/properties.h>
#include <algorithm>
+#include <regex>
namespace android {
@@ -348,6 +349,14 @@
void MediaCodecList::findMatchingCodecs(
const char *mime, bool encoder, uint32_t flags,
Vector<AString> *matches) {
+ sp<AMessage> format; // initializes as clear/null
+ findMatchingCodecs(mime, encoder, flags, format, matches);
+}
+
+//static
+void MediaCodecList::findMatchingCodecs(
+ const char *mime, bool encoder, uint32_t flags, sp<AMessage> format,
+ Vector<AString> *matches) {
matches->clear();
const sp<IMediaCodecList> list = getInstance();
@@ -368,14 +377,22 @@
const sp<MediaCodecInfo> info = list->getCodecInfo(matchIndex);
CHECK(info != nullptr);
+
AString componentName = info->getCodecName();
+ if (!codecHandlesFormat(mime, info, format)) {
+ ALOGV("skipping codec '%s' which doesn't satisfy format %s",
+ componentName.c_str(), format->debugString(2).c_str());
+ continue;
+ }
+
if ((flags & kHardwareCodecsOnly) && isSoftwareCodec(componentName)) {
ALOGV("skipping SW codec '%s'", componentName.c_str());
- } else {
- matches->push(componentName);
- ALOGV("matching '%s'", componentName.c_str());
+ continue;
}
+
+ matches->push(componentName);
+ ALOGV("matching '%s'", componentName.c_str());
}
if (flags & kPreferSoftwareCodecs ||
@@ -384,4 +401,118 @@
}
}
+/*static*/
+bool MediaCodecList::codecHandlesFormat(const char *mime, sp<MediaCodecInfo> info,
+ sp<AMessage> format) {
+
+ if (format == nullptr) {
+ ALOGD("codecHandlesFormat: no format, so no extra checks");
+ return true;
+ }
+
+ sp<MediaCodecInfo::Capabilities> capabilities = info->getCapabilitiesFor(mime);
+
+ // ... no capabilities listed means 'handle it all'
+ if (capabilities == nullptr) {
+ ALOGD("codecHandlesFormat: no capabilities for refinement");
+ return true;
+ }
+
+ const sp<AMessage> &details = capabilities->getDetails();
+
+ // if parsing the capabilities fails, ignore this particular codec
+ // currently video-centric evaluation
+ //
+ // TODO: like to make it handle the same set of properties from
+ // MediaCodecInfo::isFormatSupported()
+ // not yet done here are:
+ // profile, level, bitrate, features,
+
+ bool isVideo = false;
+ if (strncmp(mime, "video/", 6) == 0) {
+ isVideo = true;
+ }
+
+ if (isVideo) {
+ int width = -1;
+ int height = -1;
+
+ if (format->findInt32("height", &height) && format->findInt32("width", &width)) {
+
+ // is it within the supported size range of the codec?
+ AString sizeRange;
+ AString minSize,maxSize;
+ AString minWidth, minHeight;
+ AString maxWidth, maxHeight;
+ if (!details->findString("size-range", &sizeRange)
+ || !splitString(sizeRange, "-", &minSize, &maxSize)) {
+ ALOGW("Unable to parse size-range from codec info");
+ return false;
+ }
+ if (!splitString(minSize, "x", &minWidth, &minHeight)) {
+ if (!splitString(minSize, "*", &minWidth, &minHeight)) {
+ ALOGW("Unable to parse size-range/min-size from codec info");
+ return false;
+ }
+ }
+ if (!splitString(maxSize, "x", &maxWidth, &maxHeight)) {
+ if (!splitString(maxSize, "*", &maxWidth, &maxHeight)) {
+ ALOGW("Unable to fully parse size-range/max-size from codec info");
+ return false;
+ }
+ }
+
+ // strtol() returns 0 if unable to parse a number, which works for our later tests
+ int minW = strtol(minWidth.c_str(), NULL, 10);
+ int minH = strtol(minHeight.c_str(), NULL, 10);
+ int maxW = strtol(maxWidth.c_str(), NULL, 10);
+ int maxH = strtol(maxHeight.c_str(), NULL, 10);
+
+ if (minW == 0 || minH == 0 || maxW == 0 || maxH == 0) {
+ ALOGW("Unable to parse values from size-range from codec info");
+ return false;
+ }
+
+ // finally, comparison time
+ if (width < minW || width > maxW || height < minH || height > maxH) {
+ ALOGV("format %dx%d outside of allowed %dx%d-%dx%d",
+ width, height, minW, minH, maxW, maxH);
+ // at this point, it's a rejection, UNLESS
+ // the codec allows swapping width and height
+ int32_t swappable;
+ if (!details->findInt32("feature-can-swap-width-height", &swappable)
+ || swappable == 0) {
+ return false;
+ }
+ // NB: deliberate comparison of height vs width limits (and width vs height)
+ if (height < minW || height > maxW || width < minH || width > maxH) {
+ return false;
+ }
+ }
+
+ // @ 'alignment' [e.g. "2x2" which tells us that both dimensions must be even]
+ // no alignment == we're ok with anything
+ AString alignment, alignWidth, alignHeight;
+ if (details->findString("alignment", &alignment)) {
+ if (splitString(alignment, "x", &alignWidth, &alignHeight) ||
+ splitString(alignment, "*", &alignWidth, &alignHeight)) {
+ int wAlign = strtol(alignWidth.c_str(), NULL, 10);
+ int hAlign = strtol(alignHeight.c_str(), NULL, 10);
+ // strtol() returns 0 if failing to parse, treat as "no restriction"
+ if (wAlign > 0 && hAlign > 0) {
+ if ((width % wAlign) != 0 || (height % hAlign) != 0) {
+ ALOGV("format dimensions %dx%d not aligned to %dx%d",
+ width, height, wAlign, hAlign);
+ return false;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // haven't found a reason to discard this one
+ return true;
+}
+
} // namespace android
diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp
index 7c981b3..2520e2a 100644
--- a/media/libstagefright/MediaExtractorFactory.cpp
+++ b/media/libstagefright/MediaExtractorFactory.cpp
@@ -188,11 +188,11 @@
// sanity check check struct version, uuid, name
if (plugin->def.def_version != EXTRACTORDEF_VERSION_NDK_V1 &&
plugin->def.def_version != EXTRACTORDEF_VERSION_NDK_V2) {
- ALOGE("don't understand extractor format %u, ignoring.", plugin->def.def_version);
+ ALOGW("don't understand extractor format %u, ignoring.", plugin->def.def_version);
return;
}
if (memcmp(&plugin->def.extractor_uuid, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) == 0) {
- ALOGE("invalid UUID, ignoring");
+ ALOGW("invalid UUID, ignoring");
return;
}
if (plugin->def.extractor_name == NULL || strlen(plugin->def.extractor_name) == 0) {
@@ -244,13 +244,18 @@
void *libHandle = android_dlopen_ext(
libPath.string(),
RTLD_NOW | RTLD_LOCAL, dlextinfo);
- CHECK(libHandle != nullptr)
- << "couldn't dlopen(" << libPath.string() << ") " << strerror(errno);
+ if (libHandle == nullptr) {
+ ALOGI("dlopen(%s) reported error %s", libPath.string(), strerror(errno));
+ continue;
+ }
GetExtractorDef getDef =
(GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF");
- CHECK(getDef != nullptr)
- << libPath.string() << " does not contain sniffer";
+ if (getDef == nullptr) {
+ ALOGI("no sniffer found in %s", libPath.string());
+ dlclose(libHandle);
+ continue;
+ }
ALOGV("registering sniffer for %s", libPath.string());
RegisterExtractor(
@@ -258,7 +263,7 @@
}
closedir(libDir);
} else {
- ALOGE("couldn't opendir(%s)", libDirPath);
+ ALOGI("plugin directory not present (%s)", libDirPath);
}
}
diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp
index 8d9bc06..a946f71 100644
--- a/media/libstagefright/MediaMuxer.cpp
+++ b/media/libstagefright/MediaMuxer.cpp
@@ -76,6 +76,7 @@
mFileMeta.clear();
mWriter.clear();
mTrackList.clear();
+ mFormatList.clear();
}
ssize_t MediaMuxer::addTrack(const sp<AMessage> &format) {
@@ -109,6 +110,8 @@
ALOGW("addTrack() setCaptureRate failed :%d", result);
}
}
+
+ mFormatList.add(format);
return mTrackList.add(newTrack);
}
@@ -177,16 +180,23 @@
status_t MediaMuxer::writeSampleData(const sp<ABuffer> &buffer, size_t trackIndex,
int64_t timeUs, uint32_t flags) {
- Mutex::Autolock autoLock(mMuxerLock);
-
if (buffer.get() == NULL) {
ALOGE("WriteSampleData() get an NULL buffer.");
return -EINVAL;
}
-
- if (mState != STARTED) {
- ALOGE("WriteSampleData() is called in invalid state %d", mState);
- return INVALID_OPERATION;
+ {
+ /* As MediaMuxer's writeSampleData handles inputs from multiple tracks,
+ * limited the scope of mMuxerLock to this inner block so that the
+ * current track's buffer does not wait until the completion
+ * of processing of previous buffer of the same or another track.
+ * It's the responsibility of individual track - MediaAdapter object
+ * to gate its buffers.
+ */
+ Mutex::Autolock autoLock(mMuxerLock);
+ if (mState != STARTED) {
+ ALOGE("WriteSampleData() is called in invalid state %d", mState);
+ return INVALID_OPERATION;
+ }
}
if (trackIndex >= mTrackList.size()) {
@@ -217,9 +227,42 @@
ALOGV("BUFFER_FLAG_EOS");
}
+ sp<AMessage> bufMeta = buffer->meta();
+ int64_t val64;
+ if (bufMeta->findInt64("sample-file-offset", &val64)) {
+ sampleMetaData.setInt64(kKeySampleFileOffset, val64);
+ }
+ if (bufMeta->findInt64(
+ "last-sample-index-in-chunk" /*AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK*/,
+ &val64)) {
+ sampleMetaData.setInt64(kKeyLastSampleIndexInChunk, val64);
+ }
+
sp<MediaAdapter> currentTrack = mTrackList[trackIndex];
// This pushBuffer will wait until the mediaBuffer is consumed.
return currentTrack->pushBuffer(mediaBuffer);
}
+ssize_t MediaMuxer::getTrackCount() {
+ Mutex::Autolock autoLock(mMuxerLock);
+ if (mState != INITIALIZED && mState != STARTED) {
+ ALOGE("getTrackCount() must be called either in INITIALIZED or STARTED state");
+ return -1;
+ }
+ return mTrackList.size();
+}
+
+sp<AMessage> MediaMuxer::getTrackFormat([[maybe_unused]] size_t idx) {
+ Mutex::Autolock autoLock(mMuxerLock);
+ if (mState != INITIALIZED && mState != STARTED) {
+ ALOGE("getTrackFormat() must be called either in INITIALIZED or STARTED state");
+ return nullptr;
+ }
+ if (idx < 0 || idx >= mFormatList.size()) {
+ ALOGE("getTrackFormat() idx is out of range");
+ return nullptr;
+ }
+ return mFormatList[idx];
+}
+
} // namespace android
diff --git a/media/libstagefright/MediaTrack.cpp b/media/libstagefright/MediaTrack.cpp
index 24ba38a..2447f5e 100644
--- a/media/libstagefright/MediaTrack.cpp
+++ b/media/libstagefright/MediaTrack.cpp
@@ -133,6 +133,14 @@
if (format->mFormat->findInt64("target-time", &val64)) {
meta.setInt64(kKeyTargetTime, val64);
}
+ if (format->mFormat->findInt64("sample-file-offset", &val64)) {
+ meta.setInt64(kKeySampleFileOffset, val64);
+ }
+ if (format->mFormat->findInt64(
+ "last-sample-index-in-chunk" /*AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK*/,
+ &val64)) {
+ meta.setInt64(kKeyLastSampleIndexInChunk, val64);
+ }
int32_t val32;
if (format->mFormat->findInt32("is-sync-frame", &val32)) {
meta.setInt32(kKeyIsSyncFrame, val32);
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index c638507..a5c3ba6 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -189,6 +189,11 @@
return err;
}
+const char* NuMediaExtractor::getName() const {
+ Mutex::Autolock autoLock(mLock);
+ return mImpl == nullptr ? nullptr : mImpl->name().string();
+}
+
static String8 arrayToString(const std::vector<uint8_t> &array) {
String8 result;
for (size_t i = 0; i < array.size(); i++) {
@@ -316,6 +321,27 @@
(*format)->setBuffer("pssh", buf);
}
+ // Copy over the slow-motion related metadata
+ const void *slomoMarkers;
+ size_t slomoMarkersSize;
+ if (meta->findData(kKeySlowMotionMarkers, &type, &slomoMarkers, &slomoMarkersSize)
+ && slomoMarkersSize > 0) {
+ sp<ABuffer> buf = new ABuffer(slomoMarkersSize);
+ memcpy(buf->data(), slomoMarkers, slomoMarkersSize);
+ (*format)->setBuffer("slow-motion-markers", buf);
+ }
+
+ int32_t temporalLayerCount;
+ if (meta->findInt32(kKeyTemporalLayerCount, &temporalLayerCount)
+ && temporalLayerCount > 0) {
+ (*format)->setInt32("temporal-layer-count", temporalLayerCount);
+ }
+
+ float captureFps;
+ if (meta->findFloat(kKeyCaptureFramerate, &captureFps) && captureFps > 0.0f) {
+ (*format)->setFloat("capture-rate", captureFps);
+ }
+
return OK;
}
@@ -859,4 +885,15 @@
return ERROR_UNSUPPORTED;
}
+status_t NuMediaExtractor::setLogSessionId(const String8& logSessionId) {
+ if (mImpl == nullptr) {
+ return ERROR_UNSUPPORTED;
+ }
+ status_t status = mImpl->setLogSessionId(logSessionId);
+ if (status != OK) {
+ ALOGW("Failed to set log session id: %d.", status);
+ }
+ return status;
+}
+
} // namespace android
diff --git a/media/libstagefright/RemoteMediaExtractor.cpp b/media/libstagefright/RemoteMediaExtractor.cpp
index 381eb1a..baa2ca1 100644
--- a/media/libstagefright/RemoteMediaExtractor.cpp
+++ b/media/libstagefright/RemoteMediaExtractor.cpp
@@ -39,7 +39,11 @@
static const char *kExtractorFormat = "android.media.mediaextractor.fmt";
static const char *kExtractorMime = "android.media.mediaextractor.mime";
static const char *kExtractorTracks = "android.media.mediaextractor.ntrk";
+
+// The following are not available in frameworks/base/media/java/android/media/MediaExtractor.java
+// because they are not applicable or useful to that API.
static const char *kExtractorEntryPoint = "android.media.mediaextractor.entry";
+static const char *kExtractorLogSessionId = "android.media.mediaextractor.logSessionId";
static const char *kEntryPointSdk = "sdk";
static const char *kEntryPointWithJvm = "ndk-with-jvm";
@@ -174,6 +178,11 @@
return OK;
}
+status_t RemoteMediaExtractor::setLogSessionId(const String8& logSessionId) {
+ mMetricsItem->setCString(kExtractorLogSessionId, logSessionId.c_str());
+ return OK;
+}
+
////////////////////////////////////////////////////////////////////////////////
// static
diff --git a/media/libstagefright/TEST_MAPPING b/media/libstagefright/TEST_MAPPING
index 5e537dd..7ce2968 100644
--- a/media/libstagefright/TEST_MAPPING
+++ b/media/libstagefright/TEST_MAPPING
@@ -1,5 +1,17 @@
{
- "presubmit": [
+ // tests which require dynamic content
+ // invoke with: atest -- --enable-module-dynamic-download=true
+ // TODO(b/148094059): unit tests not allowed to download content
+ "dynamic-presubmit": [
+ // writerTest fails about 5 out of 66
+ // { "name": "writerTest" },
+
+ { "name": "HEVCUtilsUnitTest" },
+ { "name": "ExtractorFactoryTest" }
+
+ ],
+
+ "presubmit-large": [
{
"name": "CtsMediaTestCases",
"options": [
@@ -17,9 +29,14 @@
"exclude-filter": "android.media.cts.AudioRecordTest"
}
]
- },
+ }
+ ],
+ "presubmit": [
{
"name": "mediacodecTest"
+ },
+ {
+ "name": "CtsMediaTranscodingTestCases"
}
],
"postsubmit": [
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index bd9a694..4c18f87 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -735,14 +735,19 @@
}
};
-static std::vector<std::pair<const char *, uint32_t>> int64Mappings {
+static std::vector<std::pair<const char*, uint32_t>> int64Mappings {
{
- { "exif-offset", kKeyExifOffset },
- { "exif-size", kKeyExifSize },
- { "target-time", kKeyTargetTime },
- { "thumbnail-time", kKeyThumbnailTime },
- { "timeUs", kKeyTime },
- { "durationUs", kKeyDuration },
+ { "exif-offset", kKeyExifOffset},
+ { "exif-size", kKeyExifSize},
+ { "xmp-offset", kKeyXmpOffset},
+ { "xmp-size", kKeyXmpSize},
+ { "target-time", kKeyTargetTime},
+ { "thumbnail-time", kKeyThumbnailTime},
+ { "timeUs", kKeyTime},
+ { "durationUs", kKeyDuration},
+ { "sample-file-offset", kKeySampleFileOffset},
+ { "last-sample-index-in-chunk", kKeyLastSampleIndexInChunk},
+ { "sample-time-before-append", kKeySampleTimeBeforeAppend},
}
};
@@ -779,6 +784,8 @@
{ "sei", kKeySEI },
{ "text-format-data", kKeyTextFormatData },
{ "thumbnail-csd-hevc", kKeyThumbnailHVCC },
+ { "slow-motion-markers", kKeySlowMotionMarkers },
+ { "thumbnail-csd-av1c", kKeyThumbnailAV1C },
}
};
@@ -1700,7 +1707,7 @@
if (msg->findString("mime", &mime)) {
meta->setCString(kKeyMIMEType, mime.c_str());
} else {
- ALOGE("did not find mime type");
+ ALOGV("did not find mime type");
return BAD_VALUE;
}
@@ -1729,6 +1736,12 @@
meta->setInt32(kKeyIsSyncFrame, 1);
}
+ // Mode for media transcoding.
+ int32_t isBackgroundMode;
+ if (msg->findInt32("android._background-mode", &isBackgroundMode) && isBackgroundMode != 0) {
+ meta->setInt32(isBackgroundMode, 1);
+ }
+
int32_t avgBitrate = 0;
int32_t maxBitrate;
if (msg->findInt32("bitrate", &avgBitrate) && avgBitrate > 0) {
@@ -1750,7 +1763,7 @@
meta->setInt32(kKeyWidth, width);
meta->setInt32(kKeyHeight, height);
} else {
- ALOGE("did not find width and/or height");
+ ALOGV("did not find width and/or height");
return BAD_VALUE;
}
@@ -1839,7 +1852,7 @@
int32_t numChannels, sampleRate;
if (!msg->findInt32("channel-count", &numChannels) ||
!msg->findInt32("sample-rate", &sampleRate)) {
- ALOGE("did not find channel-count and/or sample-rate");
+ ALOGV("did not find channel-count and/or sample-rate");
return BAD_VALUE;
}
meta->setInt32(kKeyChannelCount, numChannels);
@@ -1950,7 +1963,8 @@
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 (mime == MEDIA_MIMETYPE_VIDEO_AV1) {
+ } else if (mime == MEDIA_MIMETYPE_VIDEO_AV1 ||
+ mime == MEDIA_MIMETYPE_IMAGE_AVIF) {
meta->setData(kKeyAV1C, 0, csd0->data(), csd0->size());
} else if (mime == MEDIA_MIMETYPE_VIDEO_DOLBY_VISION) {
if (msg->findBuffer("csd-2", &csd2)) {
@@ -2210,7 +2224,7 @@
}
info->duration_us = duration;
- int32_t brate = -1;
+ int32_t brate = 0;
if (!meta->findInt32(kKeyBitRate, &brate)) {
ALOGV("track of type '%s' does not publish bitrate", mime);
}
@@ -2235,7 +2249,7 @@
#ifdef DISABLE_AUDIO_SYSTEM_OFFLOAD
return false;
#else
- return AudioSystem::isOffloadSupported(info);
+ return AudioSystem::getOffloadSupport(info) != AUDIO_OFFLOAD_NOT_SUPPORTED;
#endif
}
diff --git a/media/libstagefright/bqhelper/Android.bp b/media/libstagefright/bqhelper/Android.bp
index e4fc128..0e2b472 100644
--- a/media/libstagefright/bqhelper/Android.bp
+++ b/media/libstagefright/bqhelper/Android.bp
@@ -110,6 +110,7 @@
"//apex_available:platform",
],
vendor_available: false,
+ min_sdk_version: "29",
static_libs: [
"libgui_bufferqueue_static",
],
diff --git a/media/libstagefright/bqhelper/TEST_MAPPING b/media/libstagefright/bqhelper/TEST_MAPPING
new file mode 100644
index 0000000..c7f2fd8
--- /dev/null
+++ b/media/libstagefright/bqhelper/TEST_MAPPING
@@ -0,0 +1,6 @@
+// mappings for frameworks/av/media/libstagefright/bqhelper
+{
+ "presubmit": [
+ { "name": "FrameDropper_test"}
+ ]
+}
diff --git a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/FrameDropper.h b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/FrameDropper.h
index 4e83059..7e2909a 100644
--- a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/FrameDropper.h
+++ b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/FrameDropper.h
@@ -30,6 +30,8 @@
FrameDropper();
// maxFrameRate required to be positive.
+ // maxFrameRate negative causes shouldDrop() to always return false
+ // maxFrameRate == 0 is illegal
status_t setMaxFrameRate(float maxFrameRate);
// Returns false if max frame rate has not been set via setMaxFrameRate.
diff --git a/media/libstagefright/bqhelper/tests/Android.bp b/media/libstagefright/bqhelper/tests/Android.bp
index 86ab62a..95953ee 100644
--- a/media/libstagefright/bqhelper/tests/Android.bp
+++ b/media/libstagefright/bqhelper/tests/Android.bp
@@ -9,16 +9,19 @@
cc_test {
name: "FrameDropper_test",
-
+ test_suites: [
+ "device-tests",
+ "mts",
+ ],
srcs: ["FrameDropper_test.cpp"],
-
shared_libs: [
"libstagefright_bufferqueue_helper",
"libutils",
],
-
cflags: [
"-Werror",
"-Wall",
],
+ compile_multilib: "first",
+
}
diff --git a/media/libstagefright/bqhelper/tests/FrameDropper_test.cpp b/media/libstagefright/bqhelper/tests/FrameDropper_test.cpp
index 55ae77c..b18067f 100644
--- a/media/libstagefright/bqhelper/tests/FrameDropper_test.cpp
+++ b/media/libstagefright/bqhelper/tests/FrameDropper_test.cpp
@@ -110,7 +110,7 @@
};
TEST_F(FrameDropperTest, TestInvalidMaxFrameRate) {
- EXPECT_NE(OK, mFrameDropper->setMaxFrameRate(-1.0));
+ EXPECT_EQ(OK, mFrameDropper->setMaxFrameRate(-1.0));
EXPECT_NE(OK, mFrameDropper->setMaxFrameRate(0));
}
diff --git a/media/libstagefright/data/media_codecs_sw.xml b/media/libstagefright/data/media_codecs_sw.xml
index dd2eed3..a15a988 100644
--- a/media/libstagefright/data/media_codecs_sw.xml
+++ b/media/libstagefright/data/media_codecs_sw.xml
@@ -274,6 +274,9 @@
<Limit name="bitrate" range="1-2000000" />
</Variant>
<Feature name="intra-refresh" />
+ <!-- Video Quality control -->
+ <!-- supports QP bounding with standard keys -->
+ <Feature name="qp-bounds" />
</MediaCodec>
<MediaCodec name="c2.android.vp8.encoder" type="video/x-vnd.on2.vp8" variant="slow-cpu,!slow-cpu">
<Alias name="OMX.google.vp8.encoder" />
diff --git a/media/libstagefright/flac/dec/Android.bp b/media/libstagefright/flac/dec/Android.bp
index 225c930..665aae1 100644
--- a/media/libstagefright/flac/dec/Android.bp
+++ b/media/libstagefright/flac/dec/Android.bp
@@ -40,12 +40,12 @@
},
shared_libs: [
- "libaudioutils",
"liblog",
],
static_libs: [
"libFLAC",
+ "libaudioutils", // needed for 'float_from_i32'
],
export_static_lib_headers: [
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index 7752bda..6bb7b37 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -33,7 +33,7 @@
#include <media/stagefright/foundation/hexdump.h>
-#ifndef __ANDROID_VNDK__
+#if !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
#include <binder/Parcel.h>
#endif
@@ -646,7 +646,7 @@
return s;
}
-#ifndef __ANDROID_VNDK__
+#if !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
// static
sp<AMessage> AMessage::FromParcel(const Parcel &parcel, size_t maxNestingLevel) {
int32_t what = parcel.readInt32();
@@ -813,7 +813,7 @@
}
}
}
-#endif // __ANDROID_VNDK__
+#endif // !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
sp<AMessage> AMessage::changesFrom(const sp<const AMessage> &other, bool deep) const {
if (other == NULL) {
@@ -1078,6 +1078,17 @@
return OK;
}
+status_t AMessage::removeEntryByName(const char *name) {
+ if (name == nullptr) {
+ return BAD_VALUE;
+ }
+ size_t index = findEntryByName(name);
+ if (index >= mNumItems) {
+ return BAD_INDEX;
+ }
+ return removeEntryAt(index);
+}
+
void AMessage::setItem(const char *name, const ItemData &item) {
if (item.used()) {
Item *it = allocateItem(name);
diff --git a/media/libstagefright/foundation/AString.cpp b/media/libstagefright/foundation/AString.cpp
index 8722e14..b1ed077 100644
--- a/media/libstagefright/foundation/AString.cpp
+++ b/media/libstagefright/foundation/AString.cpp
@@ -27,7 +27,7 @@
#include "ADebug.h"
#include "AString.h"
-#ifndef __ANDROID_VNDK__
+#if !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
#include <binder/Parcel.h>
#endif
@@ -365,7 +365,7 @@
return !strcasecmp(mData + mSize - suffixLen, suffix);
}
-#ifndef __ANDROID_VNDK__
+#if !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
// static
AString AString::FromParcel(const Parcel &parcel) {
size_t size = static_cast<size_t>(parcel.readInt32());
@@ -380,7 +380,7 @@
}
return err;
}
-#endif
+#endif // !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
AString AStringPrintf(const char *format, ...) {
va_list ap;
diff --git a/media/libstagefright/foundation/Android.bp b/media/libstagefright/foundation/Android.bp
index db87a33..4bfc673 100644
--- a/media/libstagefright/foundation/Android.bp
+++ b/media/libstagefright/foundation/Android.bp
@@ -109,6 +109,11 @@
"-DNO_IMEMORY",
],
},
+ apex: {
+ exclude_shared_libs: [
+ "libbinder",
+ ],
+ },
darwin: {
enabled: false,
},
diff --git a/media/libstagefright/foundation/MediaBuffer.cpp b/media/libstagefright/foundation/MediaBuffer.cpp
index 8e245dc..68df21f 100644
--- a/media/libstagefright/foundation/MediaBuffer.cpp
+++ b/media/libstagefright/foundation/MediaBuffer.cpp
@@ -51,12 +51,12 @@
mRangeLength(size),
mOwnsData(true),
mMetaData(new MetaDataBase) {
-#ifndef NO_IMEMORY
+#if !defined(NO_IMEMORY) && !defined(__ANDROID_APEX__)
if (size < kSharedMemThreshold
|| std::atomic_load_explicit(&mUseSharedMemory, std::memory_order_seq_cst) == 0) {
#endif
mData = malloc(size);
-#ifndef NO_IMEMORY
+#if !defined(NO_IMEMORY) && !defined(__ANDROID_APEX__)
} else {
ALOGV("creating memoryDealer");
size_t newSize = 0;
diff --git a/media/libstagefright/foundation/MediaBufferGroup.cpp b/media/libstagefright/foundation/MediaBufferGroup.cpp
index 3c25047..fc98f28 100644
--- a/media/libstagefright/foundation/MediaBufferGroup.cpp
+++ b/media/libstagefright/foundation/MediaBufferGroup.cpp
@@ -62,7 +62,7 @@
mInternal->mGrowthLimit = buffers;
}
-#ifndef NO_IMEMORY
+#if !defined(NO_IMEMORY) && !defined(__ANDROID_APEX__)
if (buffer_size >= kSharedMemoryThreshold) {
ALOGD("creating MemoryDealer");
// Using a single MemoryDealer is efficient for a group of shared memory objects.
diff --git a/media/libstagefright/foundation/MediaDefs.cpp b/media/libstagefright/foundation/MediaDefs.cpp
index 1493406..ada5d81 100644
--- a/media/libstagefright/foundation/MediaDefs.cpp
+++ b/media/libstagefright/foundation/MediaDefs.cpp
@@ -20,6 +20,7 @@
const char *MEDIA_MIMETYPE_IMAGE_JPEG = "image/jpeg";
const char *MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC = "image/vnd.android.heic";
+const char *MEDIA_MIMETYPE_IMAGE_AVIF = "image/avif";
const char *MEDIA_MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
const char *MEDIA_MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
diff --git a/media/libstagefright/foundation/MetaData.cpp b/media/libstagefright/foundation/MetaData.cpp
index 8174597..7f48cfd 100644
--- a/media/libstagefright/foundation/MetaData.cpp
+++ b/media/libstagefright/foundation/MetaData.cpp
@@ -28,7 +28,7 @@
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/MetaData.h>
-#ifndef __ANDROID_VNDK__
+#if !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
#include <binder/Parcel.h>
#endif
@@ -48,7 +48,7 @@
MetaData::~MetaData() {
}
-#ifndef __ANDROID_VNDK__
+#if !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
/* static */
sp<MetaData> MetaData::createFromParcel(const Parcel &parcel) {
diff --git a/media/libstagefright/foundation/MetaDataBase.cpp b/media/libstagefright/foundation/MetaDataBase.cpp
index 4b439c6..3f050ea 100644
--- a/media/libstagefright/foundation/MetaDataBase.cpp
+++ b/media/libstagefright/foundation/MetaDataBase.cpp
@@ -28,7 +28,7 @@
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/MetaDataBase.h>
-#ifndef __ANDROID_VNDK__
+#if !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
#include <binder/Parcel.h>
#endif
@@ -452,7 +452,7 @@
}
}
-#ifndef __ANDROID_VNDK__
+#if !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
status_t MetaDataBase::writeToParcel(Parcel &parcel) {
status_t ret;
size_t numItems = mInternalData->mItems.size();
@@ -532,7 +532,7 @@
ALOGW("no metadata in parcel");
return UNKNOWN_ERROR;
}
-#endif
+#endif // !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
} // namespace android
diff --git a/media/libstagefright/foundation/OpusHeader.cpp b/media/libstagefright/foundation/OpusHeader.cpp
index 784e802..30d0ae6 100644
--- a/media/libstagefright/foundation/OpusHeader.cpp
+++ b/media/libstagefright/foundation/OpusHeader.cpp
@@ -146,6 +146,10 @@
int WriteOpusHeader(const OpusHeader &header, int input_sample_rate,
uint8_t* output, size_t output_size) {
// See https://wiki.xiph.org/OggOpus#ID_Header.
+ if (header.channels < 1 || header.channels > kMaxChannels) {
+ ALOGE("Invalid channel count: %d", header.channels);
+ return -1;
+ }
const size_t total_size = kOpusHeaderStreamMapOffset + header.channels;
if (output_size < total_size) {
ALOGE("Output buffer too small for header.");
diff --git a/media/libstagefright/foundation/TEST_MAPPING b/media/libstagefright/foundation/TEST_MAPPING
index 3301c4b..a70c352 100644
--- a/media/libstagefright/foundation/TEST_MAPPING
+++ b/media/libstagefright/foundation/TEST_MAPPING
@@ -1,5 +1,14 @@
+// mappings for frameworks/av/media/libstagefright/foundation
{
+ // tests which require dynamic content
+ // invoke with: atest -- --enable-module-dynamic-download=true
+ // TODO(b/148094059): unit tests not allowed to download content
+ "dynamic-presubmit": [
+ { "name": "OpusHeaderTest" }
+ ],
+
"presubmit": [
- { "name": "sf_foundation_test" }
+ { "name": "sf_foundation_test" },
+ { "name": "MetaDataBaseUnitTest"}
]
}
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/AMessage.h b/media/libstagefright/foundation/include/media/stagefright/foundation/AMessage.h
index b5d6666..98d6147 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/AMessage.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/AMessage.h
@@ -63,7 +63,7 @@
AMessage();
AMessage(uint32_t what, const sp<const AHandler> &handler);
-#ifndef __ANDROID_VNDK__
+#if !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
// 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.
@@ -88,7 +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
+#endif // !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
void setWhat(uint32_t what);
uint32_t what() const;
@@ -261,6 +261,17 @@
*/
status_t removeEntryAt(size_t index);
+ /**
+ * Removes an entry based on name.
+ *
+ * \param name name of the entry
+ *
+ * \retval OK the entry was removed successfully
+ * \retval BAD_VALUE name is invalid (null)
+ * \retval BAD_INDEX name not found
+ */
+ status_t removeEntryByName(const char *name);
+
protected:
virtual ~AMessage();
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/AString.h b/media/libstagefright/foundation/include/media/stagefright/foundation/AString.h
index deef0d4..517774b 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/AString.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/AString.h
@@ -89,7 +89,7 @@
void tolower();
-#ifndef __ANDROID_VNDK__
+#if !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
static AString FromParcel(const Parcel &parcel);
status_t writeToParcel(Parcel *parcel) const;
#endif
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h
index 97458c3..f5cecef 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h
@@ -22,6 +22,7 @@
extern const char *MEDIA_MIMETYPE_IMAGE_JPEG;
extern const char *MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC;
+extern const char *MEDIA_MIMETYPE_IMAGE_AVIF;
extern const char *MEDIA_MIMETYPE_VIDEO_VP8;
extern const char *MEDIA_MIMETYPE_VIDEO_VP9;
diff --git a/media/libstagefright/foundation/tests/AMessage_test.cpp b/media/libstagefright/foundation/tests/AMessage_test.cpp
new file mode 100644
index 0000000..2b11326
--- /dev/null
+++ b/media/libstagefright/foundation/tests/AMessage_test.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AData_test"
+
+#include <gtest/gtest.h>
+#include <utils/RefBase.h>
+
+#include <media/stagefright/foundation/AMessage.h>
+
+using namespace android;
+
+class AMessageTest : public ::testing::Test {
+};
+
+
+TEST(AMessage_tests, item_manipulation) {
+ sp<AMessage> m1 = new AMessage();
+
+ m1->setInt32("value", 2);
+ m1->setInt32("bar", 3);
+
+ int32_t i32;
+ EXPECT_TRUE(m1->findInt32("value", &i32));
+ EXPECT_EQ(2, i32);
+
+ EXPECT_TRUE(m1->findInt32("bar", &i32));
+ EXPECT_EQ(3, i32);
+
+
+ m1->setInt64("big", INT64_MAX);
+ m1->setInt64("smaller", INT64_MAX - 2);
+ m1->setInt64("smallest", 257);
+
+ int64_t i64;
+ EXPECT_TRUE(m1->findInt64("big", &i64));
+ EXPECT_EQ(INT64_MAX, i64);
+
+ EXPECT_TRUE(m1->findInt64("smaller", &i64));
+ EXPECT_EQ(INT64_MAX - 2, i64);
+
+ m1->setSize("size1", 257);
+ m1->setSize("size2", 1023);
+
+ size_t sizing;
+ EXPECT_TRUE(m1->findSize("size2", &sizing));
+ EXPECT_EQ(1023, sizing);
+ EXPECT_TRUE(m1->findSize("size1", &sizing));
+ EXPECT_EQ(257, sizing);
+
+ m1->setDouble("precise", 10.5);
+ m1->setDouble("small", 0.125);
+
+ double d;
+ EXPECT_TRUE(m1->findDouble("precise", &d));
+ EXPECT_EQ(10.5, d);
+
+ EXPECT_TRUE(m1->findDouble("small", &d));
+ EXPECT_EQ(0.125, d);
+
+ // should be unchanged from the top of the test
+ EXPECT_TRUE(m1->findInt32("bar", &i32));
+ EXPECT_EQ(3, i32);
+
+ EXPECT_FALSE(m1->findInt32("nonesuch", &i32));
+ EXPECT_FALSE(m1->findInt64("nonesuch2", &i64));
+ // types disagree, not found
+ EXPECT_FALSE(m1->findInt32("big", &i32));
+ EXPECT_FALSE(m1->findInt32("precise", &i32));
+
+ // integral types should come back true
+ EXPECT_TRUE(m1->findAsInt64("big", &i64));
+ EXPECT_EQ(INT64_MAX, i64);
+ EXPECT_TRUE(m1->findAsInt64("bar", &i64));
+ EXPECT_EQ(3, i64);
+ EXPECT_FALSE(m1->findAsInt64("precise", &i64));
+
+ // recovers ints, size, and floating point values
+ float value;
+ EXPECT_TRUE(m1->findAsFloat("value", &value));
+ EXPECT_EQ(2, value);
+ EXPECT_TRUE(m1->findAsFloat("smallest", &value));
+ EXPECT_EQ(257, value);
+ EXPECT_TRUE(m1->findAsFloat("size2", &value));
+ EXPECT_EQ(1023, value);
+ EXPECT_TRUE(m1->findAsFloat("precise", &value));
+ EXPECT_EQ(10.5, value);
+ EXPECT_TRUE(m1->findAsFloat("small", &value));
+ EXPECT_EQ(0.125, value);
+
+
+ // need to handle still:
+ // strings
+ // Object
+ // Buffer
+ // Message (nested)
+ //
+
+ // removal
+ m1->setInt32("shortlived", 2);
+ m1->setInt32("alittlelonger", 2);
+ EXPECT_EQ(OK, m1->removeEntryByName("shortlived"));
+ EXPECT_EQ(BAD_VALUE, m1->removeEntryByName(nullptr));
+ EXPECT_EQ(BAD_INDEX, m1->removeEntryByName("themythicalnonesuch"));
+ EXPECT_FALSE(m1->findInt32("shortlived", &i32));
+ EXPECT_TRUE(m1->findInt32("alittlelonger", &i32));
+
+ EXPECT_NE(OK, m1->removeEntryByName("notpresent"));
+
+}
+
diff --git a/media/libstagefright/foundation/tests/Android.bp b/media/libstagefright/foundation/tests/Android.bp
index 801e216..e50742e 100644
--- a/media/libstagefright/foundation/tests/Android.bp
+++ b/media/libstagefright/foundation/tests/Android.bp
@@ -30,6 +30,7 @@
srcs: [
"AData_test.cpp",
+ "AMessage_test.cpp",
"Base64_test.cpp",
"Flagged_test.cpp",
"TypeTraits_test.cpp",
@@ -39,6 +40,7 @@
cc_test {
name: "MetaDataBaseUnitTest",
+ test_suites: ["device-tests"],
gtest: true,
srcs: [
diff --git a/media/libstagefright/foundation/tests/OpusHeader/Android.bp b/media/libstagefright/foundation/tests/OpusHeader/Android.bp
index 6c64ef3..fa2b40e 100644
--- a/media/libstagefright/foundation/tests/OpusHeader/Android.bp
+++ b/media/libstagefright/foundation/tests/OpusHeader/Android.bp
@@ -27,6 +27,7 @@
cc_test {
name: "OpusHeaderTest",
+ test_suites: ["device-tests"],
gtest: true,
srcs: [
diff --git a/media/libstagefright/id3/TEST_MAPPING b/media/libstagefright/id3/TEST_MAPPING
new file mode 100644
index 0000000..d82d26e
--- /dev/null
+++ b/media/libstagefright/id3/TEST_MAPPING
@@ -0,0 +1,24 @@
+// frameworks/av/media/libstagefright/id3
+{
+ // tests which require dynamic content
+ // invoke with: atest -- --enable-module-dynamic-download=true
+ // TODO(b/148094059): unit tests not allowed to download content
+ "dynamic-presubmit": [
+ { "name": "ID3Test" }
+ ],
+
+ "presubmit-large": [
+ // this doesn't seem to run any tests.
+ // but: cts-tradefed run -m CtsMediaTestCases -t android.media.cts.MediaMetadataRetrieverTest
+ // does run he 32 and 64 bit tests, but not the instant tests
+ // but all I know is that with 'atest', it's not running
+ {
+ "name": "CtsMediaTestCases",
+ "options": [
+ {
+ "include-filter": "android.media.cts.MediaMetadataRetrieverTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/media/libstagefright/id3/test/Android.bp b/media/libstagefright/id3/test/Android.bp
index 3cd8565..52cdfa5 100644
--- a/media/libstagefright/id3/test/Android.bp
+++ b/media/libstagefright/id3/test/Android.bp
@@ -27,6 +27,7 @@
cc_test {
name: "ID3Test",
+ test_suites: ["device-tests"],
gtest: true,
srcs: ["ID3Test.cpp"],
diff --git a/media/libstagefright/include/FrameDecoder.h b/media/libstagefright/include/FrameDecoder.h
index 19ae0e3..bca7f01 100644
--- a/media/libstagefright/include/FrameDecoder.h
+++ b/media/libstagefright/include/FrameDecoder.h
@@ -135,7 +135,8 @@
private:
sp<FrameCaptureLayer> mCaptureLayer;
VideoFrame *mFrame;
- bool mIsAvcOrHevc;
+ bool mIsAvc;
+ bool mIsHevc;
MediaSource::ReadOptions::SeekMode mSeekMode;
int64_t mTargetTimeUs;
List<int64_t> mSampleDurations;
diff --git a/media/libstagefright/include/HevcUtils.h b/media/libstagefright/include/HevcUtils.h
index 335de2f..6a4a168 100644
--- a/media/libstagefright/include/HevcUtils.h
+++ b/media/libstagefright/include/HevcUtils.h
@@ -30,6 +30,10 @@
namespace android {
enum {
+ kHevcNalUnitTypeCodedSliceIdr = 19,
+ kHevcNalUnitTypeCodedSliceIdrNoLP = 20,
+ kHevcNalUnitTypeCodedSliceCra = 21,
+
kHevcNalUnitTypeVps = 32,
kHevcNalUnitTypeSps = 33,
kHevcNalUnitTypePps = 34,
@@ -94,6 +98,7 @@
const sp<ABuffer> &SpsBuffer, int32_t *width, int32_t *height);
Info getInfo() const { return mInfo; }
+ static bool IsHevcIDR(const uint8_t *data, size_t size);
private:
status_t parseVps(const uint8_t* data, size_t size);
diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h
index 4c97b4d..c84cc10 100644
--- a/media/libstagefright/include/media/stagefright/ACodec.h
+++ b/media/libstagefright/include/media/stagefright/ACodec.h
@@ -31,6 +31,7 @@
#include <OMX_Audio.h>
#include <hardware/gralloc.h>
#include <nativebase/nativebase.h>
+#include <android/hardware/graphics/common/1.2/types.h>
#include <android/hidl/allocator/1.0/IAllocator.h>
#include <android/hidl/memory/1.0/IMemory.h>
@@ -54,6 +55,7 @@
struct DescribeColorFormat2Params;
struct DataConverter;
+using android::hardware::graphics::common::V1_2::BufferUsage;
typedef hidl::allocator::V1_0::IAllocator TAllocator;
typedef hidl::memory::V1_0::IMemory TMemory;
@@ -165,7 +167,8 @@
enum {
kVideoGrallocUsage = (GRALLOC_USAGE_HW_TEXTURE
| GRALLOC_USAGE_HW_COMPOSER
- | GRALLOC_USAGE_EXTERNAL_DISP),
+ | GRALLOC_USAGE_EXTERNAL_DISP)
+ | static_cast<uint64_t>(BufferUsage::VIDEO_DECODER),
};
struct BufferInfo {
@@ -515,6 +518,7 @@
status_t setLowLatency(int32_t lowLatency);
status_t setLatency(uint32_t latency);
status_t getLatency(uint32_t *latency);
+ status_t setTunnelPeek(int32_t tunnelPeek);
status_t setAudioPresentation(int32_t presentationId, int32_t programId);
status_t setOperatingRate(float rateFloat, bool isVideo);
status_t getIntraRefreshPeriod(uint32_t *intraRefreshPeriod);
@@ -575,6 +579,8 @@
void notifyOfRenderedFrames(
bool dropIncomplete = false, FrameRenderTracker::Info *until = NULL);
+ void onFirstTunnelFrameReady();
+
// Pass |expectedFormat| to print a warning if the format differs from it.
// Using sp<> instead of const sp<>& because expectedFormat is likely the current mOutputFormat
// which will get updated inside.
diff --git a/media/libstagefright/include/media/stagefright/AudioSource.h b/media/libstagefright/include/media/stagefright/AudioSource.h
index 451aa57..d1dcdb5 100644
--- a/media/libstagefright/include/media/stagefright/AudioSource.h
+++ b/media/libstagefright/include/media/stagefright/AudioSource.h
@@ -37,16 +37,27 @@
// Note that the "channels" parameter _is_ the number of channels,
// _not_ a bitmask of audio_channels_t constants.
AudioSource(
- const audio_attributes_t *attr,
- const String16 &opPackageName,
- uint32_t sampleRate,
- uint32_t channels,
- uint32_t outSampleRate = 0,
- uid_t uid = -1,
- pid_t pid = -1,
- audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
- audio_microphone_direction_t selectedMicDirection = MIC_DIRECTION_UNSPECIFIED,
- float selectedMicFieldDimension = MIC_FIELD_DIMENSION_NORMAL);
+ const audio_attributes_t *attr,
+ const media::permission::Identity& identity,
+ uint32_t sampleRate,
+ uint32_t channels,
+ uint32_t outSampleRate = 0,
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
+ audio_microphone_direction_t selectedMicDirection = MIC_DIRECTION_UNSPECIFIED,
+ float selectedMicFieldDimension = MIC_FIELD_DIMENSION_NORMAL);
+
+ // Legacy constructor kept for vendor dependencies
+ AudioSource(
+ const audio_attributes_t *attr,
+ const String16 &opPackageName,
+ uint32_t sampleRate,
+ uint32_t channels,
+ uint32_t outSampleRate = 0,
+ uid_t uid = -1,
+ pid_t pid = -1,
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
+ audio_microphone_direction_t selectedMicDirection = MIC_DIRECTION_UNSPECIFIED,
+ float selectedMicFieldDimension = MIC_FIELD_DIMENSION_NORMAL);
status_t initCheck() const;
@@ -131,6 +142,16 @@
AudioSource(const AudioSource &);
AudioSource &operator=(const AudioSource &);
+
+ void set(
+ const audio_attributes_t *attr,
+ const media::permission::Identity& identity,
+ uint32_t sampleRate,
+ uint32_t channels,
+ uint32_t outSampleRate = 0,
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
+ audio_microphone_direction_t selectedMicDirection = MIC_DIRECTION_UNSPECIFIED,
+ float selectedMicFieldDimension = MIC_FIELD_DIMENSION_NORMAL);
};
} // namespace android
diff --git a/media/libstagefright/include/media/stagefright/CameraSource.h b/media/libstagefright/include/media/stagefright/CameraSource.h
index efdfa02..e8770ed 100644
--- a/media/libstagefright/include/media/stagefright/CameraSource.h
+++ b/media/libstagefright/include/media/stagefright/CameraSource.h
@@ -23,7 +23,6 @@
#include <media/stagefright/MediaBuffer.h>
#include <camera/android/hardware/ICamera.h>
#include <camera/ICameraRecordingProxy.h>
-#include <camera/ICameraRecordingProxyListener.h>
#include <camera/CameraParameters.h>
#include <gui/BufferItemConsumer.h>
#include <utils/List.h>
@@ -40,17 +39,6 @@
class CameraSource : public MediaSource, public MediaBufferObserver {
public:
/**
- * Factory method to create a new CameraSource using the current
- * settings (such as video size, frame rate, color format, etc)
- * from the default camera.
- *
- * @param clientName The package/process name of the client application.
- * This is used for permissions checking.
- * @return NULL on error.
- */
- static CameraSource *Create(const String16 &clientName);
-
- /**
* Factory method to create a new CameraSource.
*
* @param camera the video input frame data source. If it is NULL,
@@ -89,8 +77,7 @@
pid_t clientPid,
Size videoSize,
int32_t frameRate,
- const sp<IGraphicBufferProducer>& surface,
- bool storeMetaDataInVideoBuffers = true);
+ const sp<IGraphicBufferProducer>& surface);
virtual ~CameraSource();
@@ -132,26 +119,6 @@
protected:
/**
- * The class for listening to BnCameraRecordingProxyListener. This is used to receive video
- * buffers in VIDEO_BUFFER_MODE_DATA_CALLBACK_YUV and VIDEO_BUFFER_MODE_DATA_CALLBACK_METADATA
- * mode. When a frame is available, CameraSource::dataCallbackTimestamp() will be called.
- */
- class ProxyListener: public BnCameraRecordingProxyListener {
- public:
- ProxyListener(const sp<CameraSource>& source);
- virtual void dataCallbackTimestamp(int64_t timestampUs, int32_t msgType,
- const sp<IMemory> &data);
- virtual void recordingFrameHandleCallbackTimestamp(int64_t timestampUs,
- native_handle_t* handle);
- virtual void recordingFrameHandleCallbackTimestampBatch(
- const std::vector<int64_t>& timestampsUs,
- const std::vector<native_handle_t*>& handles);
-
- private:
- sp<CameraSource> mSource;
- };
-
- /**
* The class for listening to BufferQueue's onFrameAvailable. This is used to receive video
* buffers in VIDEO_BUFFER_MODE_BUFFER_QUEUE mode. When a frame is available,
* CameraSource::processBufferQueueFrame() will be called.
@@ -214,32 +181,15 @@
CameraSource(const sp<hardware::ICamera>& camera, const sp<ICameraRecordingProxy>& proxy,
int32_t cameraId, const String16& clientName, uid_t clientUid, pid_t clientPid,
Size videoSize, int32_t frameRate,
- const sp<IGraphicBufferProducer>& surface,
- bool storeMetaDataInVideoBuffers);
+ const sp<IGraphicBufferProducer>& surface);
virtual status_t startCameraRecording();
virtual void releaseRecordingFrame(const sp<IMemory>& frame);
- virtual void releaseRecordingFrameHandle(native_handle_t* handle);
- // stagefright recorder not using this for now
- virtual void releaseRecordingFrameHandleBatch(const std::vector<native_handle_t*>& handles);
// Returns true if need to skip the current frame.
// Called from dataCallbackTimestamp.
virtual bool skipCurrentFrame(int64_t /*timestampUs*/) {return false;}
- // Callback called when still camera raw data is available.
- virtual void dataCallback(int32_t /*msgType*/, const sp<IMemory>& /*data*/) {}
-
- virtual void dataCallbackTimestamp(int64_t timestampUs, int32_t msgType,
- const sp<IMemory> &data);
-
- virtual void recordingFrameHandleCallbackTimestamp(int64_t timestampUs,
- native_handle_t* handle);
-
- virtual void recordingFrameHandleCallbackTimestampBatch(
- const std::vector<int64_t>& timestampsUs,
- const std::vector<native_handle_t*>& handles);
-
// Process a buffer item received in BufferQueueListener.
virtual void processBufferQueueFrame(BufferItem& buffer);
@@ -262,9 +212,6 @@
int64_t mGlitchDurationThresholdUs;
bool mCollectStats;
- // The mode video buffers are received from camera. One of VIDEO_BUFFER_MODE_*.
- int32_t mVideoBufferMode;
-
static const uint32_t kDefaultVideoBufferCount = 32;
/**
@@ -298,12 +245,12 @@
status_t init(const sp<hardware::ICamera>& camera, const sp<ICameraRecordingProxy>& proxy,
int32_t cameraId, const String16& clientName, uid_t clientUid, pid_t clientPid,
- Size videoSize, int32_t frameRate, bool storeMetaDataInVideoBuffers);
+ Size videoSize, int32_t frameRate);
status_t initWithCameraAccess(
const sp<hardware::ICamera>& camera, const sp<ICameraRecordingProxy>& proxy,
int32_t cameraId, const String16& clientName, uid_t clientUid, pid_t clientPid,
- Size videoSize, int32_t frameRate, bool storeMetaDataInVideoBuffers);
+ Size videoSize, int32_t frameRate);
// Initialize the buffer queue used in VIDEO_BUFFER_MODE_BUFFER_QUEUE mode.
status_t initBufferQueue(uint32_t width, uint32_t height, uint32_t format,
diff --git a/media/libstagefright/include/media/stagefright/CameraSourceTimeLapse.h b/media/libstagefright/include/media/stagefright/CameraSourceTimeLapse.h
index 533e33b..3c311cf 100644
--- a/media/libstagefright/include/media/stagefright/CameraSourceTimeLapse.h
+++ b/media/libstagefright/include/media/stagefright/CameraSourceTimeLapse.h
@@ -45,8 +45,7 @@
Size videoSize,
int32_t videoFrameRate,
const sp<IGraphicBufferProducer>& surface,
- int64_t timeBetweenTimeLapseFrameCaptureUs,
- bool storeMetaDataInVideoBuffers = true);
+ int64_t timeBetweenTimeLapseFrameCaptureUs);
virtual ~CameraSourceTimeLapse();
@@ -122,8 +121,7 @@
Size videoSize,
int32_t videoFrameRate,
const sp<IGraphicBufferProducer>& surface,
- int64_t timeBetweenTimeLapseFrameCaptureUs,
- bool storeMetaDataInVideoBuffers = true);
+ int64_t timeBetweenTimeLapseFrameCaptureUs);
// Wrapper over CameraSource::signalBufferReturned() to implement quick stop.
// It only handles the case when mLastReadBufferCopy is signalled. Otherwise
@@ -137,33 +135,6 @@
// frame needs to be skipped and this function just returns the value of mSkipCurrentFrame.
virtual bool skipCurrentFrame(int64_t timestampUs);
- // In the video camera case calls skipFrameAndModifyTimeStamp() to modify
- // timestamp and set mSkipCurrentFrame.
- // Then it calls the base CameraSource::dataCallbackTimestamp()
- // This will be called in VIDEO_BUFFER_MODE_DATA_CALLBACK_YUV and
- // VIDEO_BUFFER_MODE_DATA_CALLBACK_METADATA mode.
- virtual void dataCallbackTimestamp(int64_t timestampUs, int32_t msgType,
- const sp<IMemory> &data);
-
- // In the video camera case calls skipFrameAndModifyTimeStamp() to modify
- // timestamp and set mSkipCurrentFrame.
- // Then it calls the base CameraSource::recordingFrameHandleCallbackTimestamp() or
- // CameraSource::recordingFrameHandleCallbackTimestampBatch()
- // This will be called in VIDEO_BUFFER_MODE_DATA_CALLBACK_METADATA mode when
- // the metadata is VideoNativeHandleMetadata.
- virtual void recordingFrameHandleCallbackTimestamp(int64_t timestampUs,
- native_handle_t* handle);
-
- // In the video camera case calls skipFrameAndModifyTimeStamp() to modify
- // timestamp and set mSkipCurrentFrame.
- // Then it calls the base CameraSource::recordingFrameHandleCallbackTimestamp() or
- // CameraSource::recordingFrameHandleCallbackTimestampBatch()
- // This will be called in VIDEO_BUFFER_MODE_DATA_CALLBACK_METADATA mode when
- // the metadata is VideoNativeHandleMetadata.
- virtual void recordingFrameHandleCallbackTimestampBatch(
- const std::vector<int64_t>& timestampsUs,
- const std::vector<native_handle_t*>& handles);
-
// Process a buffer item received in CameraSource::BufferQueueListener.
// This will be called in VIDEO_BUFFER_MODE_BUFFER_QUEUE mode.
virtual void processBufferQueueFrame(BufferItem& buffer);
@@ -187,9 +158,6 @@
// Wrapper to enter threadTimeLapseEntry()
static void *ThreadTimeLapseWrapper(void *me);
- // Creates a copy of source_data into a new memory of final type MemoryBase.
- sp<IMemory> createIMemoryCopy(const sp<IMemory> &source_data);
-
CameraSourceTimeLapse(const CameraSourceTimeLapse &);
CameraSourceTimeLapse &operator=(const CameraSourceTimeLapse &);
};
diff --git a/media/libstagefright/include/media/stagefright/CodecBase.h b/media/libstagefright/include/media/stagefright/CodecBase.h
index 2e98fec..efb2f86 100644
--- a/media/libstagefright/include/media/stagefright/CodecBase.h
+++ b/media/libstagefright/include/media/stagefright/CodecBase.h
@@ -178,6 +178,10 @@
* Notify MediaCodec that output buffers are changed.
*/
virtual void onOutputBuffersChanged() = 0;
+ /**
+ * Notify MediaCodec that the first tunnel frame is ready.
+ */
+ virtual void onFirstTunnelFrameReady() = 0;
};
/**
diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
index 501cf2c..7c3eca6 100644
--- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h
+++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
@@ -46,7 +46,7 @@
// Returns INVALID_OPERATION if there is no source or track.
virtual status_t start(MetaData *param = NULL);
- virtual status_t stop() { return reset(); }
+ virtual status_t stop();
virtual status_t pause();
virtual bool reachedEOS();
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -97,6 +97,7 @@
sp<MetaData> mStartMeta;
status_t mInitCheck;
bool mIsRealTimeRecording;
+ bool mIsBackgroundMode;
bool mUse4ByteNalLength;
bool mIsFileSizeLimitExplicitlyRequested;
bool mPaused;
@@ -106,6 +107,7 @@
off64_t mOffset;
off64_t mPreAllocateFileEndOffset; //End of file offset during preallocation.
off64_t mMdatOffset;
+ off64_t mMaxOffsetAppend; // File offset written upto while appending.
off64_t mMdatEndOffset; // End offset of mdat atom.
uint8_t *mInMemoryCache;
off64_t mInMemoryCacheOffset;
@@ -126,6 +128,7 @@
bool mWriteSeekErr;
bool mFallocateErr;
bool mPreAllocationEnabled;
+ status_t mResetStatus;
// Queue to hold top long write durations
std::priority_queue<std::chrono::microseconds, std::vector<std::chrono::microseconds>,
std::greater<std::chrono::microseconds>> mWriteDurationPQ;
@@ -135,7 +138,9 @@
sp<AHandlerReflector<MPEG4Writer> > mReflector;
Mutex mLock;
+ // Serialize reset calls from client of MPEG4Writer and MP4WtrCtrlHlpLooper.
std::mutex mResetMutex;
+ // Serialize preallocation calls from different track threads.
std::mutex mFallocMutex;
bool mPreAllocFirstTime; // Pre-allocate space for file and track headers only once per file.
uint64_t mPrevAllTracksTotalMetaDataSizeEstimate;
@@ -271,6 +276,10 @@
// By default, real time recording is on.
bool isRealTimeRecording() const;
+ // Return whether the writer is used in background mode for media
+ // transcoding.
+ bool isBackgroundMode() const;
+
void lock();
void unlock();
@@ -304,7 +313,7 @@
void writeGeoDataBox();
void writeLatitude(int degreex10000);
void writeLongitude(int degreex10000);
- void finishCurrentSession();
+ status_t finishCurrentSession();
void addDeviceMeta();
void writeHdlr(const char *handlerType);
@@ -337,7 +346,7 @@
void sendSessionSummary();
status_t release();
status_t switchFd();
- status_t reset(bool stopSource = true);
+ status_t reset(bool stopSource = true, bool waitForAnyPreviousCallToComplete = true);
static uint32_t getMpeg4Time();
diff --git a/media/libstagefright/include/media/stagefright/MediaAdapter.h b/media/libstagefright/include/media/stagefright/MediaAdapter.h
index 177a9e9..c7d7765 100644
--- a/media/libstagefright/include/media/stagefright/MediaAdapter.h
+++ b/media/libstagefright/include/media/stagefright/MediaAdapter.h
@@ -58,6 +58,7 @@
private:
Mutex mAdapterLock;
+ std::mutex mBufferGatingMutex;
// Make sure the read() wait for the incoming buffer.
Condition mBufferReadCond;
// Make sure the pushBuffer() wait for the current buffer consumed.
diff --git a/media/libstagefright/include/media/stagefright/MediaAppender.h b/media/libstagefright/include/media/stagefright/MediaAppender.h
new file mode 100644
index 0000000..c2f6f10
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/MediaAppender.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_APPENDER_H
+#define ANDROID_MEDIA_APPENDER_H
+
+#include <media/stagefright/MediaMuxer.h>
+#include <media/stagefright/NuMediaExtractor.h>
+#include <stack>
+
+namespace android {
+
+struct MediaAppender : public MediaMuxerBase {
+public:
+ enum AppendMode {
+ APPEND_MODE_FIRST = 0,
+ APPEND_MODE_IGNORE_LAST_VIDEO_GOP = APPEND_MODE_FIRST,
+ APPEND_MODE_ADD_TO_EXISTING_DATA = 1,
+ APPEND_MODE_LAST = APPEND_MODE_ADD_TO_EXISTING_DATA,
+ };
+
+ static sp<MediaAppender> create(int fd, AppendMode mode);
+
+ virtual ~MediaAppender();
+
+ status_t init();
+
+ status_t start();
+
+ status_t stop();
+
+ status_t writeSampleData(const sp<ABuffer>& buffer, size_t trackIndex, int64_t timeUs,
+ uint32_t flags);
+
+ status_t setOrientationHint(int degrees);
+
+ status_t setLocation(int latitude, int longitude);
+
+ ssize_t addTrack(const sp<AMessage> &format);
+
+ ssize_t getTrackCount();
+
+ sp<AMessage> getTrackFormat(size_t idx);
+
+private:
+ MediaAppender(int fd, AppendMode mode);
+
+ int mFd;
+ MediaMuxer::OutputFormat mFormat;
+ AppendMode mMode;
+ sp<NuMediaExtractor> mExtractor;
+ sp<MediaMuxer> mMuxer;
+ size_t mTrackCount;
+ // Map track index given by extractor to the ones received from muxer.
+ std::map<size_t, ssize_t> mTrackIndexMap;
+ // Count of the samples in each track, indexed by extractor track ids.
+ std::vector<size_t> mSampleCountVect;
+ // Extractor track index of samples.
+ std::vector<size_t> mSampleIndexVect;
+ // Track format indexed by extractor track ids.
+ std::map<size_t, sp<AMessage>> mFmtIndexMap;
+ // Size of samples.
+ std::vector<size_t> mSampleSizeVect;
+ // Presentation time stamp of samples.
+ std::vector<int64_t> mSampleTimeVect;
+ // Timestamp of last sample of tracks.
+ std::vector<int64_t> mMaxTimestampVect;
+ // Metadata of samples.
+ std::vector<sp<MetaData>> mSampleMetaVect;
+ std::mutex mMutex;
+ // Timestamp of the last sync sample of tracks.
+ std::vector<int64_t> mLastSyncSampleTimeVect;
+
+ struct sampleDataInfo;
+ std::vector<sampleDataInfo> mSDI;
+
+ enum : int {
+ UNINITIALIZED,
+ INITIALIZED,
+ STARTED,
+ STOPPED,
+ ERROR,
+ } mState GUARDED_BY(mMutex);
+};
+
+} // namespace android
+#endif // ANDROID_MEDIA_APPENDER_H
\ No newline at end of file
diff --git a/media/libstagefright/include/media/stagefright/MediaBuffer.h b/media/libstagefright/include/media/stagefright/MediaBuffer.h
index 9145b63..2c03f27 100644
--- a/media/libstagefright/include/media/stagefright/MediaBuffer.h
+++ b/media/libstagefright/include/media/stagefright/MediaBuffer.h
@@ -46,7 +46,7 @@
explicit MediaBuffer(size_t size);
explicit MediaBuffer(const sp<ABuffer> &buffer);
-#ifndef NO_IMEMORY
+#if !defined(NO_IMEMORY) && !defined(__ANDROID_APEX__)
MediaBuffer(const sp<IMemory> &mem) :
// TODO: Using unsecurePointer() has some associated security pitfalls
// (see declaration for details).
@@ -97,7 +97,7 @@
}
virtual int remoteRefcount() const {
-#ifndef NO_IMEMORY
+#if !defined(NO_IMEMORY) && !defined(__ANDROID_APEX__)
// TODO: Using unsecurePointer() has some associated security pitfalls
// (see declaration for details).
// Either document why it is safe in this case or address the
@@ -114,7 +114,7 @@
// returns old value
int addRemoteRefcount(int32_t value) {
-#ifndef NO_IMEMORY
+#if !defined(NO_IMEMORY) && !defined(__ANDROID_APEX__)
// TODO: Using unsecurePointer() has some associated security pitfalls
// (see declaration for details).
// Either document why it is safe in this case or address the
@@ -132,7 +132,7 @@
}
static bool isDeadObject(const sp<IMemory> &memory) {
-#ifndef NO_IMEMORY
+#if !defined(NO_IMEMORY) && !defined(__ANDROID_APEX__)
// TODO: Using unsecurePointer() has some associated security pitfalls
// (see declaration for details).
// Either document why it is safe in this case or address the
@@ -235,7 +235,7 @@
};
inline SharedControl *getSharedControl() const {
-#ifndef NO_IMEMORY
+#if !defined(NO_IMEMORY) && !defined(__ANDROID_APEX__)
// TODO: Using unsecurePointer() has some associated security pitfalls
// (see declaration for details).
// Either document why it is safe in this case or address the
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 8952376..3517bae 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -104,6 +104,10 @@
const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err = NULL,
pid_t pid = kNoPid, uid_t uid = kNoUid);
+ static sp<MediaCodec> CreateByType(
+ const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err,
+ pid_t pid, uid_t uid, sp<AMessage> format);
+
static sp<MediaCodec> CreateByComponentName(
const sp<ALooper> &looper, const AString &name, status_t *err = NULL,
pid_t pid = kNoPid, uid_t uid = kNoUid);
@@ -129,6 +133,8 @@
status_t setOnFrameRenderedNotification(const sp<AMessage> ¬ify);
+ status_t setOnFirstTunnelFrameReadyNotification(const sp<AMessage> ¬ify);
+
status_t createInputSurface(sp<IGraphicBufferProducer>* bufferProducer);
status_t setInputSurface(const sp<PersistentSurface> &surface);
@@ -367,6 +373,22 @@
bool mOwnedByClient;
};
+ // This type is used to track the tunnel mode video peek state machine:
+ //
+ // DisabledNoBuffer -> EnabledNoBuffer when tunnel-peek = true
+ // EnabledNoBuffer -> DisabledNoBuffer when tunnel-peek = false
+ // DisabledNoBuffer -> BufferDecoded when kWhatFirstTunnelFrameReady
+ // EnabledNoBuffer -> BufferDecoded when kWhatFirstTunnelFrameReady
+ // BufferDecoded -> BufferRendered when kWhatFrameRendered
+ // <all states> -> EnabledNoBuffer when flush
+ // <all states> -> EnabledNoBuffer when stop then configure then start
+ enum struct TunnelPeekState {
+ kDisabledNoBuffer,
+ kEnabledNoBuffer,
+ kBufferDecoded,
+ kBufferRendered,
+ };
+
struct ResourceManagerServiceProxy;
State mState;
@@ -382,6 +404,7 @@
std::string mLastReplyOrigin;
std::vector<sp<AMessage>> mDeferredMessages;
uint32_t mFlags;
+ int64_t mPresentationTimeUs = 0;
status_t mStickyError;
sp<Surface> mSurface;
SoftwareRenderer *mSoftRenderer;
@@ -393,12 +416,15 @@
void flushMediametrics();
void updateEphemeralMediametrics(mediametrics_handle_t item);
void updateLowLatency(const sp<AMessage> &msg);
+ constexpr const char *asString(TunnelPeekState state, const char *default_string="?");
+ void updateTunnelPeek(const sp<AMessage> &msg);
sp<AMessage> mOutputFormat;
sp<AMessage> mInputFormat;
sp<AMessage> mCallback;
sp<AMessage> mOnFrameRenderedNotification;
sp<AMessage> mAsyncReleaseCompleteNotification;
+ sp<AMessage> mOnFirstTunnelFrameReadyNotification;
sp<ResourceManagerServiceProxy> mResourceManagerProxy;
@@ -414,6 +440,17 @@
// configure parameter
sp<AMessage> mConfigureMsg;
+ // rewrites the format description during configure() for encoding.
+ // format and flags as they exist within configure()
+ // the (possibly) updated format is returned in place.
+ status_t shapeMediaFormat(
+ const sp<AMessage> &format,
+ uint32_t flags);
+
+ // populate the format shaper library with information for this codec encoding
+ // for the indicated media type
+ status_t setupFormatShaper(AString mediaType);
+
// Used only to synchronize asynchronous getBufferAndFormat
// across all the other (synchronous) buffer state change
// operations, such as de/queueIn/OutputBuffer, start and
@@ -434,6 +471,7 @@
int32_t mTunneledInputWidth;
int32_t mTunneledInputHeight;
bool mTunneled;
+ TunnelPeekState mTunnelPeekState;
sp<IDescrambler> mDescrambler;
@@ -534,6 +572,15 @@
std::deque<BufferFlightTiming_t> mBuffersInFlight;
Mutex mLatencyLock;
int64_t mLatencyUnknown; // buffers for which we couldn't calculate latency
+
+ Mutex mOutputStatsLock;
+ int64_t mBytesEncoded = 0;
+ int64_t mEarliestEncodedPtsUs = INT64_MAX;
+ int64_t mLatestEncodedPtsUs = INT64_MIN;
+ int64_t mFramesEncoded = 0;
+ int64_t mBytesInput = 0;
+ int64_t mFramesInput = 0;
+
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
@@ -549,8 +596,8 @@
sp<BatteryChecker> mBatteryChecker;
- void statsBufferSent(int64_t presentationUs);
- void statsBufferReceived(int64_t presentationUs);
+ void statsBufferSent(int64_t presentationUs, const sp<MediaCodecBuffer> &buffer);
+ void statsBufferReceived(int64_t presentationUs, const sp<MediaCodecBuffer> &buffer);
enum {
// the default shape of our latency histogram buckets
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
index bee96b1..6371769 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
@@ -684,6 +684,7 @@
constexpr char FEATURE_AdaptivePlayback[] = "adaptive-playback";
constexpr char FEATURE_IntraRefresh[] = "intra-refresh";
constexpr char FEATURE_PartialFrame[] = "partial-frame";
+constexpr char FEATURE_QpBounds[] = "qp-bounds";
constexpr char FEATURE_SecurePlayback[] = "secure-playback";
constexpr char FEATURE_TunneledPlayback[] = "tunneled-playback";
@@ -745,6 +746,7 @@
constexpr char KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT[] = "aac-max-output-channel_count";
constexpr char KEY_AAC_PROFILE[] = "aac-profile";
constexpr char KEY_AAC_SBR_MODE[] = "aac-sbr-mode";
+constexpr char KEY_ALLOW_FRAME_DROP[] = "allow-frame-drop";
constexpr char KEY_AUDIO_SESSION_ID[] = "audio-session-id";
constexpr char KEY_BIT_RATE[] = "bitrate";
constexpr char KEY_BITRATE_MODE[] = "bitrate-mode";
@@ -856,9 +858,9 @@
constexpr char PARAMETER_KEY_REQUEST_SYNC_FRAME[] = "request-sync";
constexpr char PARAMETER_KEY_SUSPEND[] = "drop-input-frames";
constexpr char PARAMETER_KEY_SUSPEND_TIME[] = "drop-start-time-us";
+constexpr char PARAMETER_KEY_TUNNEL_PEEK[] = "tunnel-peek";
constexpr char PARAMETER_KEY_VIDEO_BITRATE[] = "video-bitrate";
}
#endif // MEDIA_CODEC_CONSTANTS_H_
-
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecList.h b/media/libstagefright/include/media/stagefright/MediaCodecList.h
index 78d1005..3cf455c 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecList.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecList.h
@@ -75,6 +75,16 @@
uint32_t flags,
Vector<AString> *matchingCodecs);
+ // add optional format, to further refine matching codecs
+ static void findMatchingCodecs(
+ const char *mime,
+ bool createEncoder,
+ uint32_t flags,
+ sp<AMessage> format,
+ Vector<AString> *matchingCodecs);
+
+ static bool codecHandlesFormat(const char *mime, sp<MediaCodecInfo> info, sp<AMessage> format);
+
static bool isSoftwareCodec(const AString &componentName);
private:
diff --git a/media/libstagefright/include/media/stagefright/MediaErrors.h b/media/libstagefright/include/media/stagefright/MediaErrors.h
index 5418f10..d1df2ca 100644
--- a/media/libstagefright/include/media/stagefright/MediaErrors.h
+++ b/media/libstagefright/include/media/stagefright/MediaErrors.h
@@ -75,7 +75,32 @@
ERROR_DRM_RESOURCE_CONTENTION = DRM_ERROR_BASE - 14,
ERROR_DRM_SESSION_LOST_STATE = DRM_ERROR_BASE - 15,
ERROR_DRM_INVALID_STATE = DRM_ERROR_BASE - 16,
- ERROR_DRM_LAST_USED_ERRORCODE = DRM_ERROR_BASE - 16,
+
+ // New in S / drm@1.4:
+ ERROR_DRM_CERTIFICATE_MALFORMED = DRM_ERROR_BASE - 17,
+ ERROR_DRM_CERTIFICATE_MISSING = DRM_ERROR_BASE - 18,
+ ERROR_DRM_CRYPTO_LIBRARY = DRM_ERROR_BASE - 19,
+ ERROR_DRM_GENERIC_OEM = DRM_ERROR_BASE - 20,
+ ERROR_DRM_GENERIC_PLUGIN = DRM_ERROR_BASE - 21,
+ ERROR_DRM_INIT_DATA = DRM_ERROR_BASE - 22,
+ ERROR_DRM_KEY_NOT_LOADED = DRM_ERROR_BASE - 23,
+ ERROR_DRM_LICENSE_PARSE = DRM_ERROR_BASE - 24,
+ ERROR_DRM_LICENSE_POLICY = DRM_ERROR_BASE - 25,
+ ERROR_DRM_LICENSE_RELEASE = DRM_ERROR_BASE - 26,
+ ERROR_DRM_LICENSE_REQUEST_REJECTED = DRM_ERROR_BASE - 27,
+ ERROR_DRM_LICENSE_RESTORE = DRM_ERROR_BASE - 28,
+ ERROR_DRM_LICENSE_STATE = DRM_ERROR_BASE - 29,
+ ERROR_DRM_MEDIA_FRAMEWORK = DRM_ERROR_BASE - 30,
+ ERROR_DRM_PROVISIONING_CERTIFICATE = DRM_ERROR_BASE - 31,
+ ERROR_DRM_PROVISIONING_CONFIG = DRM_ERROR_BASE - 32,
+ ERROR_DRM_PROVISIONING_PARSE = DRM_ERROR_BASE - 33,
+ ERROR_DRM_PROVISIONING_REQUEST_REJECTED = DRM_ERROR_BASE - 34,
+ ERROR_DRM_PROVISIONING_RETRY = DRM_ERROR_BASE - 35,
+ ERROR_DRM_SECURE_STOP_RELEASE = DRM_ERROR_BASE - 36,
+ ERROR_DRM_STORAGE_READ = DRM_ERROR_BASE - 37,
+ ERROR_DRM_STORAGE_WRITE = DRM_ERROR_BASE - 38,
+ ERROR_DRM_ZERO_SUBSAMPLES = DRM_ERROR_BASE - 39,
+ ERROR_DRM_LAST_USED_ERRORCODE = ERROR_DRM_ZERO_SUBSAMPLES,
ERROR_DRM_VENDOR_MAX = DRM_ERROR_BASE - 500,
ERROR_DRM_VENDOR_MIN = DRM_ERROR_BASE - 999,
@@ -138,6 +163,57 @@
|| (ERROR_DRM_VENDOR_MIN <= err && err <= ERROR_DRM_VENDOR_MAX);
}
+static inline std::string StrCryptoError(status_t err) {
+#define STATUS_CASE(STATUS) \
+ case STATUS: \
+ return #STATUS
+
+ switch (err) {
+ STATUS_CASE(ERROR_DRM_UNKNOWN);
+ STATUS_CASE(ERROR_DRM_NO_LICENSE);
+ STATUS_CASE(ERROR_DRM_LICENSE_EXPIRED);
+ STATUS_CASE(ERROR_DRM_SESSION_NOT_OPENED);
+ STATUS_CASE(ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED);
+ STATUS_CASE(ERROR_DRM_DECRYPT);
+ STATUS_CASE(ERROR_DRM_CANNOT_HANDLE);
+ STATUS_CASE(ERROR_DRM_TAMPER_DETECTED);
+ STATUS_CASE(ERROR_DRM_NOT_PROVISIONED);
+ STATUS_CASE(ERROR_DRM_DEVICE_REVOKED);
+ STATUS_CASE(ERROR_DRM_RESOURCE_BUSY);
+ STATUS_CASE(ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION);
+ STATUS_CASE(ERROR_DRM_INSUFFICIENT_SECURITY);
+ STATUS_CASE(ERROR_DRM_FRAME_TOO_LARGE);
+ STATUS_CASE(ERROR_DRM_RESOURCE_CONTENTION);
+ STATUS_CASE(ERROR_DRM_SESSION_LOST_STATE);
+ STATUS_CASE(ERROR_DRM_INVALID_STATE);
+ STATUS_CASE(ERROR_DRM_CERTIFICATE_MALFORMED);
+ STATUS_CASE(ERROR_DRM_CERTIFICATE_MISSING);
+ STATUS_CASE(ERROR_DRM_CRYPTO_LIBRARY);
+ STATUS_CASE(ERROR_DRM_GENERIC_OEM);
+ STATUS_CASE(ERROR_DRM_GENERIC_PLUGIN);
+ STATUS_CASE(ERROR_DRM_INIT_DATA);
+ STATUS_CASE(ERROR_DRM_KEY_NOT_LOADED);
+ STATUS_CASE(ERROR_DRM_LICENSE_PARSE);
+ STATUS_CASE(ERROR_DRM_LICENSE_POLICY);
+ STATUS_CASE(ERROR_DRM_LICENSE_RELEASE);
+ STATUS_CASE(ERROR_DRM_LICENSE_REQUEST_REJECTED);
+ STATUS_CASE(ERROR_DRM_LICENSE_RESTORE);
+ STATUS_CASE(ERROR_DRM_LICENSE_STATE);
+ STATUS_CASE(ERROR_DRM_MEDIA_FRAMEWORK);
+ STATUS_CASE(ERROR_DRM_PROVISIONING_CERTIFICATE);
+ STATUS_CASE(ERROR_DRM_PROVISIONING_CONFIG);
+ STATUS_CASE(ERROR_DRM_PROVISIONING_PARSE);
+ STATUS_CASE(ERROR_DRM_PROVISIONING_REQUEST_REJECTED);
+ STATUS_CASE(ERROR_DRM_PROVISIONING_RETRY);
+ STATUS_CASE(ERROR_DRM_SECURE_STOP_RELEASE);
+ STATUS_CASE(ERROR_DRM_STORAGE_READ);
+ STATUS_CASE(ERROR_DRM_STORAGE_WRITE);
+ STATUS_CASE(ERROR_DRM_ZERO_SUBSAMPLES);
+#undef STATUS_CASE
+ }
+ return statusToString(err);
+}
+
} // namespace android
#endif // MEDIA_ERRORS_H_
diff --git a/media/libstagefright/include/media/stagefright/MediaMuxer.h b/media/libstagefright/include/media/stagefright/MediaMuxer.h
index a1b9465..e97a65e 100644
--- a/media/libstagefright/include/media/stagefright/MediaMuxer.h
+++ b/media/libstagefright/include/media/stagefright/MediaMuxer.h
@@ -22,7 +22,12 @@
#include <utils/Vector.h>
#include <utils/threads.h>
+#include <map>
+#include <mutex>
+#include <vector>
+
#include "media/stagefright/foundation/ABase.h"
+#include "MediaMuxerBase.h"
namespace android {
@@ -33,6 +38,7 @@
struct MediaSource;
class MetaData;
struct MediaWriter;
+struct NuMediaExtractor;
// MediaMuxer is used to mux multiple tracks into a video. Currently, we only
// support a mp4 file as the output.
@@ -40,19 +46,8 @@
// Constructor -> addTrack+ -> start -> writeSampleData+ -> stop
// If muxing operation need to be cancelled, the app is responsible for
// deleting the output file after stop.
-struct MediaMuxer : public RefBase {
+struct MediaMuxer : public MediaMuxerBase {
public:
- // Please update media/java/android/media/MediaMuxer.java if the
- // OutputFormat is updated.
- enum OutputFormat {
- OUTPUT_FORMAT_MPEG_4 = 0,
- OUTPUT_FORMAT_WEBM = 1,
- OUTPUT_FORMAT_THREE_GPP = 2,
- OUTPUT_FORMAT_HEIF = 3,
- OUTPUT_FORMAT_OGG = 4,
- OUTPUT_FORMAT_LIST_END // must be last - used to validate format type
- };
-
// Construct the muxer with the file descriptor. Note that the MediaMuxer
// will close this file at stop().
MediaMuxer(int fd, OutputFormat format);
@@ -117,10 +112,25 @@
status_t writeSampleData(const sp<ABuffer> &buffer, size_t trackIndex,
int64_t timeUs, uint32_t flags) ;
+ /**
+ * Gets the number of tracks added successfully. Should be called in
+ * INITIALIZED(after constructor) or STARTED(after start()) state.
+ * @return the number of tracks or -1 in wrong state.
+ */
+ ssize_t getTrackCount();
+
+ /**
+ * Gets the format of the track by their index.
+ * @param idx : index of the track whose format is wanted.
+ * @return smart pointer to AMessage containing the format details.
+ */
+ sp<AMessage> getTrackFormat(size_t idx);
+
private:
const OutputFormat mFormat;
sp<MediaWriter> mWriter;
Vector< sp<MediaAdapter> > mTrackList; // Each track has its MediaAdapter.
+ Vector< sp<AMessage> > mFormatList; // Format of each track.
sp<MetaData> mFileMeta; // Metadata for the whole file.
Mutex mMuxerLock;
diff --git a/media/libstagefright/include/media/stagefright/MediaMuxerBase.h b/media/libstagefright/include/media/stagefright/MediaMuxerBase.h
new file mode 100644
index 0000000..f02d510
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/MediaMuxerBase.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_MUXER_BASE_H_
+#define MEDIA_MUXER_BASE_H_
+
+#include <utils/RefBase.h>
+#include "media/stagefright/foundation/ABase.h"
+
+namespace android {
+
+struct ABuffer;
+struct AMessage;
+
+// MediaMuxer is used to mux multiple tracks into a video. Currently, we only
+// support a mp4 file as the output.
+// The expected calling order of the functions is:
+// Constructor -> addTrack+ -> start -> writeSampleData+ -> stop
+// If muxing operation need to be cancelled, the app is responsible for
+// deleting the output file after stop.
+struct MediaMuxerBase : public RefBase {
+public:
+ // Please update media/java/android/media/MediaMuxer.java if the
+ // OutputFormat is updated.
+ enum OutputFormat {
+ OUTPUT_FORMAT_MPEG_4 = 0,
+ OUTPUT_FORMAT_WEBM = 1,
+ OUTPUT_FORMAT_THREE_GPP = 2,
+ OUTPUT_FORMAT_HEIF = 3,
+ OUTPUT_FORMAT_OGG = 4,
+ OUTPUT_FORMAT_LIST_END // must be last - used to validate format type
+ };
+
+ // Construct the muxer with the file descriptor. Note that the MediaMuxer
+ // will close this file at stop().
+ MediaMuxerBase() {};
+
+ virtual ~MediaMuxerBase() {};
+
+ /**
+ * Add a track with its format information. This should be
+ * called before start().
+ * @param format the track's format.
+ * @return the track's index or negative number if error.
+ */
+ virtual ssize_t addTrack(const sp<AMessage> &format) = 0;
+
+ /**
+ * Start muxing. Make sure all the tracks have been added before
+ * calling this.
+ */
+ virtual status_t start() = 0;
+
+ /**
+ * Set the orientation hint.
+ * @param degrees The rotation degrees. It has to be either 0,
+ * 90, 180 or 270.
+ * @return OK if no error.
+ */
+ virtual status_t setOrientationHint(int degrees) = 0;
+
+ /**
+ * Set the location.
+ * @param latitude The latitude in degree x 1000. Its value must be in the range
+ * [-900000, 900000].
+ * @param longitude The longitude in degree x 1000. Its value must be in the range
+ * [-1800000, 1800000].
+ * @return OK if no error.
+ */
+ virtual status_t setLocation(int latitude, int longitude) = 0;
+
+ /**
+ * Stop muxing.
+ * This method is a blocking call. Depending on how
+ * much data is bufferred internally, the time needed for stopping
+ * the muxer may be time consuming. UI thread is
+ * not recommended for launching this call.
+ * @return OK if no error.
+ */
+ virtual status_t stop() = 0;
+
+ /**
+ * Send a sample buffer for muxing.
+ * The buffer can be reused once this method returns. Typically,
+ * this function won't be blocked for very long, and thus there
+ * is no need to use a separate thread calling this method to
+ * push a buffer.
+ * @param buffer the incoming sample buffer.
+ * @param trackIndex the buffer's track index number.
+ * @param timeUs the buffer's time stamp.
+ * @param flags the only supported flag for now is
+ * MediaCodec::BUFFER_FLAG_SYNCFRAME.
+ * @return OK if no error.
+ */
+ virtual status_t writeSampleData(const sp<ABuffer> &buffer, size_t trackIndex,
+ int64_t timeUs, uint32_t flags) = 0 ;
+
+ /**
+ * Gets the number of tracks added successfully. Should be called in
+ * INITIALIZED(after constructor) or STARTED(after start()) state.
+ * @return the number of tracks or -1 in wrong state.
+ */
+ virtual ssize_t getTrackCount() = 0;
+
+ /**
+ * Gets the format of the track by their index.
+ * @param idx : index of the track whose format is wanted.
+ * @return smart pointer to AMessage containing the format details.
+ */
+ virtual sp<AMessage> getTrackFormat(size_t idx) = 0;
+
+private:
+
+ DISALLOW_EVIL_CONSTRUCTORS(MediaMuxerBase);
+};
+
+} // namespace android
+
+#endif // MEDIA_MUXER_BASE_H_
+
diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h
index aae5ef9..c80012e 100644
--- a/media/libstagefright/include/media/stagefright/MetaDataBase.h
+++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h
@@ -62,6 +62,7 @@
kKeyDVCC = 'dvcc', // raw data
kKeyAV1C = 'av1c', // raw data
kKeyThumbnailHVCC = 'thvc', // raw data
+ kKeyThumbnailAV1C = 'tav1', // raw data
kKeyD263 = 'd263', // raw data
kKeyOpusHeader = 'ohdr', // raw data
kKeyOpusCodecDelay = 'ocod', // uint64_t (codec delay in ns)
@@ -126,6 +127,8 @@
kKeyTrackTimeStatus = 'tktm', // int64_t
kKeyRealTimeRecording = 'rtrc', // bool (int32_t)
+ kKeyBackgroundMode = 'bkmd', // bool (int32_t)
+
kKeyNumBuffers = 'nbbf', // int32_t
// Ogg files can be tagged to be automatically looping...
@@ -228,6 +231,8 @@
kKeyExifSize = 'exsz', // int64_t, Exif data size
kKeyExifTiffOffset = 'thdr', // int32_t, if > 0, buffer contains exif data block with
// tiff hdr at specified offset
+ kKeyXmpOffset = 'xmof', // int64_t, XMP data offset
+ kKeyXmpSize = 'xmsz', // int64_t, XMP data size
kKeyPcmBigEndian = 'pcmb', // bool (int32_t)
// Key for ALAC Magic Cookie
@@ -251,6 +256,7 @@
// Treat empty track as malformed for MediaRecorder.
kKeyEmptyTrackMalFormed = 'nemt', // bool (int32_t)
+
kKeyVps = 'sVps', // int32_t, indicates that a buffer has vps.
kKeySps = 'sSps', // int32_t, indicates that a buffer has sps.
kKeyPps = 'sPps', // int32_t, indicates that a buffer has pps.
@@ -260,6 +266,15 @@
kKeyRtpCvoDegrees = 'cvod', // int32_t, rtp cvo degrees as per 3GPP 26.114.
kKeyRtpDscp = 'dscp', // int32_t, DSCP(Differentiated services codepoint) of RFC 2474.
kKeySocketNetwork = 'sNet', // int64_t, socket will be bound to network handle.
+
+ // Slow-motion markers
+ kKeySlowMotionMarkers = 'slmo', // raw data, byte array following spec for
+ // MediaFormat#KEY_SLOW_MOTION_MARKERS
+
+ kKeySampleFileOffset = 'sfof', // int64_t, sample's offset in a media file.
+ kKeyLastSampleIndexInChunk = 'lsic', //int64_t, index of last sample in a chunk.
+ kKeySampleTimeBeforeAppend = 'lsba', // int64_t, timestamp of last sample of a track.
+
};
enum {
diff --git a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
index d8f2b00..d17a480 100644
--- a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
+++ b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
@@ -100,6 +100,10 @@
status_t getAudioPresentations(size_t trackIdx, AudioPresentationCollection *presentations);
+ status_t setLogSessionId(const String8& logSessionId);
+
+ const char* getName() const;
+
protected:
virtual ~NuMediaExtractor();
diff --git a/media/libstagefright/include/media/stagefright/ProcessInfo.h b/media/libstagefright/include/media/stagefright/ProcessInfo.h
index 0be1a52..b8a3c10 100644
--- a/media/libstagefright/include/media/stagefright/ProcessInfo.h
+++ b/media/libstagefright/include/media/stagefright/ProcessInfo.h
@@ -20,6 +20,9 @@
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/ProcessInfoInterface.h>
+#include <map>
+#include <mutex>
+#include <utils/Condition.h>
namespace android {
@@ -28,11 +31,20 @@
virtual bool getPriority(int pid, int* priority);
virtual bool isValidPid(int pid);
+ virtual bool overrideProcessInfo(int pid, int procState, int oomScore);
+ virtual void removeProcessInfoOverride(int pid);
protected:
virtual ~ProcessInfo();
private:
+ struct ProcessInfoOverride {
+ int procState;
+ int oomScore;
+ };
+ std::mutex mOverrideLock;
+ std::map<int, ProcessInfoOverride> mOverrideMap GUARDED_BY(mOverrideLock);
+
DISALLOW_EVIL_CONSTRUCTORS(ProcessInfo);
};
diff --git a/media/libstagefright/include/media/stagefright/ProcessInfoInterface.h b/media/libstagefright/include/media/stagefright/ProcessInfoInterface.h
index b39112a..9260181 100644
--- a/media/libstagefright/include/media/stagefright/ProcessInfoInterface.h
+++ b/media/libstagefright/include/media/stagefright/ProcessInfoInterface.h
@@ -24,6 +24,8 @@
struct ProcessInfoInterface : public RefBase {
virtual bool getPriority(int pid, int* priority) = 0;
virtual bool isValidPid(int pid) = 0;
+ virtual bool overrideProcessInfo(int pid, int procState, int oomScore);
+ virtual void removeProcessInfoOverride(int pid);
protected:
virtual ~ProcessInfoInterface() {}
diff --git a/media/libstagefright/include/media/stagefright/RemoteDataSource.h b/media/libstagefright/include/media/stagefright/RemoteDataSource.h
index d82be8a..d605cda 100644
--- a/media/libstagefright/include/media/stagefright/RemoteDataSource.h
+++ b/media/libstagefright/include/media/stagefright/RemoteDataSource.h
@@ -41,6 +41,11 @@
close();
}
virtual sp<IMemory> getIMemory() {
+ Mutex::Autolock lock(mLock);
+ if (mMemory.get() == nullptr) {
+ ALOGE("getIMemory() failed, mMemory is nullptr");
+ return nullptr;
+ }
return mMemory;
}
virtual ssize_t readAt(off64_t offset, size_t size) {
@@ -48,19 +53,35 @@
if (size > kBufferSize) {
size = kBufferSize;
}
+
+ Mutex::Autolock lock(mLock);
+ if (mSource.get() == nullptr) {
+ ALOGE("readAt() failed, mSource is nullptr");
+ return 0;
+ }
return mSource->readAt(offset, mMemory->unsecurePointer(), size);
}
virtual status_t getSize(off64_t *size) {
+ Mutex::Autolock lock(mLock);
+ if (mSource.get() == nullptr) {
+ ALOGE("getSize() failed, mSource is nullptr");
+ return INVALID_OPERATION;
+ }
return mSource->getSize(size);
}
virtual void close() {
// Protect strong pointer assignments. This also can be called from the binder
// clean-up procedure which is running on a separate thread.
- Mutex::Autolock lock(mCloseLock);
+ Mutex::Autolock lock(mLock);
mSource = nullptr;
mMemory = nullptr;
}
virtual uint32_t getFlags() {
+ Mutex::Autolock lock(mLock);
+ if (mSource.get() == nullptr) {
+ ALOGE("getSize() failed, mSource is nullptr");
+ return 0;
+ }
return mSource->flags();
}
virtual String8 toString() {
@@ -75,9 +96,10 @@
sp<IMemory> mMemory;
sp<DataSource> mSource;
String8 mName;
- Mutex mCloseLock;
+ Mutex mLock;
explicit RemoteDataSource(const sp<DataSource> &source) {
+ Mutex::Autolock lock(mLock);
mSource = source;
sp<MemoryDealer> memoryDealer = new MemoryDealer(kBufferSize, "RemoteDataSource");
mMemory = memoryDealer->allocate(kBufferSize);
diff --git a/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h b/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h
index 25125f2..9ad90d7 100644
--- a/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h
+++ b/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h
@@ -43,6 +43,7 @@
virtual status_t setMediaCas(const HInterfaceToken &casToken);
virtual String8 name();
virtual status_t setEntryPoint(EntryPoint entryPoint);
+ virtual status_t setLogSessionId(const String8& logSessionId);
private:
MediaExtractor *mExtractor;
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 62e3a4b..27a94fd 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -60,21 +60,23 @@
mIsAudio = false;
mIsVideo = false;
+ const char *mime;
- if (meta == NULL) {
+ // Do not use meta if no mime.
+ if (meta == NULL || !meta->findCString(kKeyMIMEType, &mime)) {
return;
}
mFormat = meta;
- const char *mime;
- CHECK(meta->findCString(kKeyMIMEType, &mime));
if (!strncasecmp("audio/", mime, 6)) {
mIsAudio = true;
- } else if (!strncasecmp("video/", mime, 6)) {
+ } else if (!strncasecmp("video/", mime, 6)) {
mIsVideo = true;
+ } else if (!strncasecmp("text/", mime, 5) || !strncasecmp("application/", mime, 12)) {
+ return;
} else {
- CHECK(!strncasecmp("text/", mime, 5) || !strncasecmp("application/", mime, 12));
+ ALOGW("Unsupported mime type: %s", mime);
}
}
diff --git a/media/libstagefright/mpeg2ts/TEST_MAPPING b/media/libstagefright/mpeg2ts/TEST_MAPPING
new file mode 100644
index 0000000..9f4bbdf
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/TEST_MAPPING
@@ -0,0 +1,9 @@
+// frameworks/av/media/libstagefright/mpeg2ts
+{
+ // tests which require dynamic content
+ // invoke with: atest -- --enable-module-dynamic-download=true
+ // TODO(b/148094059): unit tests not allowed to download content
+ "dynamic-presubmit": [
+ { "name": "Mpeg2tsUnitTest" }
+ ]
+}
diff --git a/media/libstagefright/mpeg2ts/test/Android.bp b/media/libstagefright/mpeg2ts/test/Android.bp
index db2794f..464b039 100644
--- a/media/libstagefright/mpeg2ts/test/Android.bp
+++ b/media/libstagefright/mpeg2ts/test/Android.bp
@@ -28,6 +28,7 @@
cc_test{
name: "Mpeg2tsUnitTest",
gtest: true,
+ test_suites: ["device-tests"],
srcs: [
"Mpeg2tsUnitTest.cpp"
diff --git a/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp b/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
index 7d217eb..f7bf3ba 100644
--- a/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
+++ b/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
@@ -67,7 +67,7 @@
int32_t dataSpace, int32_t aspects, int32_t pixelFormat) override {
Message tMsg;
tMsg.type = Message::Type::EVENT;
- tMsg.fence = native_handle_create(0, 0);
+ tMsg.fence.setTo(native_handle_create(0, 0), /* shouldOwn = */ true);
tMsg.data.eventData.event = uint32_t(OMX_EventDataSpaceChanged);
tMsg.data.eventData.data1 = dataSpace;
tMsg.data.eventData.data2 = aspects;
diff --git a/media/libstagefright/omx/OMXUtils.cpp b/media/libstagefright/omx/OMXUtils.cpp
index d6d280f..49b2dec 100644
--- a/media/libstagefright/omx/OMXUtils.cpp
+++ b/media/libstagefright/omx/OMXUtils.cpp
@@ -172,6 +172,8 @@
"audio_decoder.ac4", "audio_encoder.ac4" },
{ MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC,
"image_decoder.heic", "image_encoder.heic" },
+ { MEDIA_MIMETYPE_IMAGE_AVIF,
+ "image_decoder.avif", "image_encoder.avif" },
};
static const size_t kNumMimeToRole =
diff --git a/media/libstagefright/renderfright/Android.bp b/media/libstagefright/renderfright/Android.bp
new file mode 100644
index 0000000..9a7bad9
--- /dev/null
+++ b/media/libstagefright/renderfright/Android.bp
@@ -0,0 +1,119 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_media_libstagefright_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_media_libstagefright_license"],
+}
+
+cc_defaults {
+ name: "renderfright_defaults",
+ cflags: [
+ "-DLOG_TAG=\"renderfright\"",
+ "-Wall",
+ "-Werror",
+ "-Wthread-safety",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
+
+cc_defaults {
+ name: "librenderfright_defaults",
+ defaults: ["renderfright_defaults"],
+ cflags: [
+ "-DGL_GLEXT_PROTOTYPES",
+ "-DEGL_EGLEXT_PROTOTYPES",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libEGL",
+ "libGLESv1_CM",
+ "libGLESv2",
+ "liblog",
+ "libnativewindow",
+ "libprocessgroup",
+ "libsync",
+ "libui",
+ "libutils",
+ ],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+}
+
+filegroup {
+ name: "librenderfright_sources",
+ srcs: [
+ "Description.cpp",
+ "Mesh.cpp",
+ "RenderEngine.cpp",
+ "Texture.cpp",
+ ],
+}
+
+filegroup {
+ name: "librenderfright_gl_sources",
+ srcs: [
+ "gl/GLESRenderEngine.cpp",
+ "gl/GLExtensions.cpp",
+ "gl/GLFramebuffer.cpp",
+ "gl/GLImage.cpp",
+ "gl/GLShadowTexture.cpp",
+ "gl/GLShadowVertexGenerator.cpp",
+ "gl/GLSkiaShadowPort.cpp",
+ "gl/GLVertexBuffer.cpp",
+ "gl/ImageManager.cpp",
+ "gl/Program.cpp",
+ "gl/ProgramCache.cpp",
+ "gl/filters/BlurFilter.cpp",
+ "gl/filters/GenericProgram.cpp",
+ ],
+}
+
+filegroup {
+ name: "librenderfright_threaded_sources",
+ srcs: [
+ "threaded/RenderEngineThreaded.cpp",
+ ],
+}
+
+cc_library_static {
+ name: "librenderfright",
+ defaults: ["librenderfright_defaults"],
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+ double_loadable: true,
+ clang: true,
+ cflags: [
+ "-fvisibility=hidden",
+ "-Werror=format",
+ ],
+ srcs: [
+ ":librenderfright_sources",
+ ":librenderfright_gl_sources",
+ ":librenderfright_threaded_sources",
+ ],
+ lto: {
+ thin: true,
+ },
+}
+
+cc_library_static {
+ name: "librenderfright_mocks",
+ defaults: ["librenderfright_defaults"],
+ srcs: [
+ "mock/Framebuffer.cpp",
+ "mock/Image.cpp",
+ "mock/RenderEngine.cpp",
+ ],
+ static_libs: [
+ "libgtest",
+ "libgmock",
+ ],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+}
diff --git a/media/libstagefright/renderfright/Description.cpp b/media/libstagefright/renderfright/Description.cpp
new file mode 100644
index 0000000..b9cea10
--- /dev/null
+++ b/media/libstagefright/renderfright/Description.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#include <renderengine/private/Description.h>
+
+#include <stdint.h>
+
+#include <utils/TypeHelpers.h>
+
+namespace android {
+namespace renderengine {
+
+Description::TransferFunction Description::dataSpaceToTransferFunction(ui::Dataspace dataSpace) {
+ ui::Dataspace transfer = static_cast<ui::Dataspace>(dataSpace & ui::Dataspace::TRANSFER_MASK);
+ switch (transfer) {
+ case ui::Dataspace::TRANSFER_ST2084:
+ return Description::TransferFunction::ST2084;
+ case ui::Dataspace::TRANSFER_HLG:
+ return Description::TransferFunction::HLG;
+ case ui::Dataspace::TRANSFER_LINEAR:
+ return Description::TransferFunction::LINEAR;
+ default:
+ return Description::TransferFunction::SRGB;
+ }
+}
+
+bool Description::hasInputTransformMatrix() const {
+ const mat4 identity;
+ return inputTransformMatrix != identity;
+}
+
+bool Description::hasOutputTransformMatrix() const {
+ const mat4 identity;
+ return outputTransformMatrix != identity;
+}
+
+bool Description::hasColorMatrix() const {
+ const mat4 identity;
+ return colorMatrix != identity;
+}
+
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/Mesh.cpp b/media/libstagefright/renderfright/Mesh.cpp
new file mode 100644
index 0000000..ed2f45f
--- /dev/null
+++ b/media/libstagefright/renderfright/Mesh.cpp
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+#include <renderengine/Mesh.h>
+
+#include <utils/Log.h>
+
+namespace android {
+namespace renderengine {
+
+Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize,
+ size_t cropCoordsSize, size_t shadowColorSize, size_t shadowParamsSize,
+ size_t indexCount)
+ : mVertexCount(vertexCount),
+ mVertexSize(vertexSize),
+ mTexCoordsSize(texCoordSize),
+ mCropCoordsSize(cropCoordsSize),
+ mShadowColorSize(shadowColorSize),
+ mShadowParamsSize(shadowParamsSize),
+ mPrimitive(primitive),
+ mIndexCount(indexCount) {
+ if (vertexCount == 0) {
+ mVertices.resize(1);
+ mVertices[0] = 0.0f;
+ mStride = 0;
+ return;
+ }
+ size_t stride = vertexSize + texCoordSize + cropCoordsSize + shadowColorSize + shadowParamsSize;
+ size_t remainder = (stride * vertexCount) / vertexCount;
+ // Since all of the input parameters are unsigned, if stride is less than
+ // either vertexSize or texCoordSize, it must have overflowed. remainder
+ // will be equal to stride as long as stride * vertexCount doesn't overflow.
+ if ((stride < vertexSize) || (remainder != stride)) {
+ ALOGE("Overflow in Mesh(..., %zu, %zu, %zu, %zu, %zu, %zu)", vertexCount, vertexSize,
+ texCoordSize, cropCoordsSize, shadowColorSize, shadowParamsSize);
+ mVertices.resize(1);
+ mVertices[0] = 0.0f;
+ mVertexCount = 0;
+ mVertexSize = 0;
+ mTexCoordsSize = 0;
+ mCropCoordsSize = 0;
+ mShadowColorSize = 0;
+ mShadowParamsSize = 0;
+ mStride = 0;
+ return;
+ }
+
+ mVertices.resize(stride * vertexCount);
+ mStride = stride;
+ mIndices.resize(indexCount);
+}
+
+Mesh::Primitive Mesh::getPrimitive() const {
+ return mPrimitive;
+}
+
+float const* Mesh::getPositions() const {
+ return mVertices.data();
+}
+float* Mesh::getPositions() {
+ return mVertices.data();
+}
+
+float const* Mesh::getTexCoords() const {
+ return mVertices.data() + mVertexSize;
+}
+float* Mesh::getTexCoords() {
+ return mVertices.data() + mVertexSize;
+}
+
+float const* Mesh::getCropCoords() const {
+ return mVertices.data() + mVertexSize + mTexCoordsSize;
+}
+float* Mesh::getCropCoords() {
+ return mVertices.data() + mVertexSize + mTexCoordsSize;
+}
+
+float const* Mesh::getShadowColor() const {
+ return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize;
+}
+float* Mesh::getShadowColor() {
+ return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize;
+}
+
+float const* Mesh::getShadowParams() const {
+ return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize + mShadowColorSize;
+}
+float* Mesh::getShadowParams() {
+ return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize + mShadowColorSize;
+}
+
+uint16_t const* Mesh::getIndices() const {
+ return mIndices.data();
+}
+
+uint16_t* Mesh::getIndices() {
+ return mIndices.data();
+}
+
+size_t Mesh::getVertexCount() const {
+ return mVertexCount;
+}
+
+size_t Mesh::getVertexSize() const {
+ return mVertexSize;
+}
+
+size_t Mesh::getTexCoordsSize() const {
+ return mTexCoordsSize;
+}
+
+size_t Mesh::getShadowColorSize() const {
+ return mShadowColorSize;
+}
+
+size_t Mesh::getShadowParamsSize() const {
+ return mShadowParamsSize;
+}
+
+size_t Mesh::getByteStride() const {
+ return mStride * sizeof(float);
+}
+
+size_t Mesh::getStride() const {
+ return mStride;
+}
+
+size_t Mesh::getIndexCount() const {
+ return mIndexCount;
+}
+
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/RenderEngine.cpp b/media/libstagefright/renderfright/RenderEngine.cpp
new file mode 100644
index 0000000..e96ffdb
--- /dev/null
+++ b/media/libstagefright/renderfright/RenderEngine.cpp
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+#include <renderengine/RenderEngine.h>
+
+#include <cutils/properties.h>
+#include <log/log.h>
+#include "gl/GLESRenderEngine.h"
+#include "threaded/RenderEngineThreaded.h"
+
+namespace android {
+namespace renderengine {
+
+std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
+ RenderEngineType renderEngineType = args.renderEngineType;
+
+ // Keep the ability to override by PROPERTIES:
+ char prop[PROPERTY_VALUE_MAX];
+ property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
+ if (strcmp(prop, "gles") == 0) {
+ renderEngineType = RenderEngineType::GLES;
+ }
+ if (strcmp(prop, "threaded") == 0) {
+ renderEngineType = RenderEngineType::THREADED;
+ }
+
+ switch (renderEngineType) {
+ case RenderEngineType::THREADED:
+ ALOGD("Threaded RenderEngine with GLES Backend");
+ return renderengine::threaded::RenderEngineThreaded::create(
+ [args]() { return android::renderengine::gl::GLESRenderEngine::create(args); });
+ case RenderEngineType::GLES:
+ default:
+ ALOGD("RenderEngine with GLES Backend");
+ return renderengine::gl::GLESRenderEngine::create(args);
+ }
+}
+
+RenderEngine::~RenderEngine() = default;
+
+// static
+SyncFeatures &SyncFeatures::GetInstance() {
+ static SyncFeatures syncFeatures;
+ return syncFeatures;
+}
+
+bool SyncFeatures::useNativeFenceSync() const { return mHasNativeFenceSync; }
+bool SyncFeatures::useFenceSync() const { return mHasFenceSync; }
+bool SyncFeatures::useWaitSync() const { return mHasWaitSync; }
+
+SyncFeatures::SyncFeatures()
+ : mHasNativeFenceSync(false),
+ mHasFenceSync(false),
+ mHasWaitSync(false) {
+ EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ // This can only be called after EGL has been initialized; otherwise the
+ // check below will abort.
+ const char* exts = eglQueryString(dpy, EGL_EXTENSIONS);
+ LOG_ALWAYS_FATAL_IF(exts == nullptr, "eglQueryString failed");
+ if (strstr(exts, "EGL_ANDROID_native_fence_sync")) {
+ // This makes GLConsumer use the EGL_ANDROID_native_fence_sync
+ // extension to create Android native fences to signal when all
+ // GLES reads for a given buffer have completed.
+ mHasNativeFenceSync = true;
+ }
+ if (strstr(exts, "EGL_KHR_fence_sync")) {
+ mHasFenceSync = true;
+ }
+ if (strstr(exts, "EGL_KHR_wait_sync")) {
+ mHasWaitSync = true;
+ }
+}
+
+namespace impl {
+
+RenderEngine::RenderEngine(const RenderEngineCreationArgs& args) : mArgs(args) {}
+
+RenderEngine::~RenderEngine() = default;
+
+bool RenderEngine::useNativeFenceSync() const {
+ return SyncFeatures::GetInstance().useNativeFenceSync();
+}
+
+bool RenderEngine::useWaitSync() const {
+ return SyncFeatures::GetInstance().useWaitSync();
+}
+
+} // namespace impl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/Texture.cpp b/media/libstagefright/renderfright/Texture.cpp
new file mode 100644
index 0000000..154cde8
--- /dev/null
+++ b/media/libstagefright/renderfright/Texture.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+#include <renderengine/Texture.h>
+
+namespace android {
+namespace renderengine {
+
+Texture::Texture()
+ : mTextureName(0), mTextureTarget(TEXTURE_2D), mWidth(0), mHeight(0), mFiltering(false) {}
+
+Texture::Texture(Target textureTarget, uint32_t textureName)
+ : mTextureName(textureName),
+ mTextureTarget(textureTarget),
+ mWidth(0),
+ mHeight(0),
+ mFiltering(false) {}
+
+void Texture::init(Target textureTarget, uint32_t textureName) {
+ mTextureName = textureName;
+ mTextureTarget = textureTarget;
+}
+
+Texture::~Texture() {}
+
+void Texture::setMatrix(float const* matrix) {
+ mTextureMatrix = mat4(matrix);
+}
+
+void Texture::setFiltering(bool enabled) {
+ mFiltering = enabled;
+}
+
+void Texture::setDimensions(size_t width, size_t height) {
+ mWidth = width;
+ mHeight = height;
+}
+
+uint32_t Texture::getTextureName() const {
+ return mTextureName;
+}
+
+uint32_t Texture::getTextureTarget() const {
+ return mTextureTarget;
+}
+
+const mat4& Texture::getMatrix() const {
+ return mTextureMatrix;
+}
+
+bool Texture::getFiltering() const {
+ return mFiltering;
+}
+
+size_t Texture::getWidth() const {
+ return mWidth;
+}
+
+size_t Texture::getHeight() const {
+ return mHeight;
+}
+
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/gl/GLESRenderEngine.cpp b/media/libstagefright/renderfright/gl/GLESRenderEngine.cpp
new file mode 100644
index 0000000..04da9a5
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/GLESRenderEngine.cpp
@@ -0,0 +1,1766 @@
+/*
+ * 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
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <sched.h>
+#include <cmath>
+#include <fstream>
+#include <sstream>
+#include <unordered_set>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <android-base/stringprintf.h>
+#include <cutils/compiler.h>
+#include <cutils/properties.h>
+#include <renderengine/Mesh.h>
+#include <renderengine/Texture.h>
+#include <renderengine/private/Description.h>
+#include <sync/sync.h>
+#include <ui/ColorSpace.h>
+#include <ui/DebugUtils.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <utils/KeyedVector.h>
+#include <utils/Trace.h>
+#include "GLESRenderEngine.h"
+#include "GLExtensions.h"
+#include "GLFramebuffer.h"
+#include "GLImage.h"
+#include "GLShadowVertexGenerator.h"
+#include "Program.h"
+#include "ProgramCache.h"
+#include "filters/BlurFilter.h"
+
+bool checkGlError(const char* op, int lineNumber) {
+ bool errorFound = false;
+ GLint error = glGetError();
+ while (error != GL_NO_ERROR) {
+ errorFound = true;
+ error = glGetError();
+ ALOGV("after %s() (line # %d) glError (0x%x)\n", op, lineNumber, error);
+ }
+ return errorFound;
+}
+
+static constexpr bool outputDebugPPMs = false;
+
+void writePPM(const char* basename, GLuint width, GLuint height) {
+ ALOGV("writePPM #%s: %d x %d", basename, width, height);
+
+ std::vector<GLubyte> pixels(width * height * 4);
+ std::vector<GLubyte> outBuffer(width * height * 3);
+
+ // TODO(courtneygo): We can now have float formats, need
+ // to remove this code or update to support.
+ // Make returned pixels fit in uint32_t, one byte per component
+ glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
+ if (checkGlError(__FUNCTION__, __LINE__)) {
+ return;
+ }
+
+ std::string filename(basename);
+ filename.append(".ppm");
+ std::ofstream file(filename.c_str(), std::ios::binary);
+ if (!file.is_open()) {
+ ALOGE("Unable to open file: %s", filename.c_str());
+ ALOGE("You may need to do: \"adb shell setenforce 0\" to enable "
+ "surfaceflinger to write debug images");
+ return;
+ }
+
+ file << "P6\n";
+ file << width << "\n";
+ file << height << "\n";
+ file << 255 << "\n";
+
+ auto ptr = reinterpret_cast<char*>(pixels.data());
+ auto outPtr = reinterpret_cast<char*>(outBuffer.data());
+ for (int y = height - 1; y >= 0; y--) {
+ char* data = ptr + y * width * sizeof(uint32_t);
+
+ for (GLuint x = 0; x < width; x++) {
+ // Only copy R, G and B components
+ outPtr[0] = data[0];
+ outPtr[1] = data[1];
+ outPtr[2] = data[2];
+ data += sizeof(uint32_t);
+ outPtr += 3;
+ }
+ }
+ file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size());
+}
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+using base::StringAppendF;
+using ui::Dataspace;
+
+static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute,
+ EGLint wanted, EGLConfig* outConfig) {
+ EGLint numConfigs = -1, n = 0;
+ eglGetConfigs(dpy, nullptr, 0, &numConfigs);
+ std::vector<EGLConfig> configs(numConfigs, EGL_NO_CONFIG_KHR);
+ eglChooseConfig(dpy, attrs, configs.data(), configs.size(), &n);
+ configs.resize(n);
+
+ if (!configs.empty()) {
+ if (attribute != EGL_NONE) {
+ for (EGLConfig config : configs) {
+ EGLint value = 0;
+ eglGetConfigAttrib(dpy, config, attribute, &value);
+ if (wanted == value) {
+ *outConfig = config;
+ return NO_ERROR;
+ }
+ }
+ } else {
+ // just pick the first one
+ *outConfig = configs[0];
+ return NO_ERROR;
+ }
+ }
+
+ return NAME_NOT_FOUND;
+}
+
+static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint renderableType,
+ EGLConfig* config) {
+ // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if
+ // it is to be used with WIFI displays
+ status_t err;
+ EGLint wantedAttribute;
+ EGLint wantedAttributeValue;
+
+ std::vector<EGLint> attribs;
+ if (renderableType) {
+ const ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(format);
+ const bool is1010102 = pixelFormat == ui::PixelFormat::RGBA_1010102;
+
+ // Default to 8 bits per channel.
+ const EGLint tmpAttribs[] = {
+ EGL_RENDERABLE_TYPE,
+ renderableType,
+ EGL_RECORDABLE_ANDROID,
+ EGL_TRUE,
+ EGL_SURFACE_TYPE,
+ EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
+ EGL_FRAMEBUFFER_TARGET_ANDROID,
+ EGL_TRUE,
+ EGL_RED_SIZE,
+ is1010102 ? 10 : 8,
+ EGL_GREEN_SIZE,
+ is1010102 ? 10 : 8,
+ EGL_BLUE_SIZE,
+ is1010102 ? 10 : 8,
+ EGL_ALPHA_SIZE,
+ is1010102 ? 2 : 8,
+ EGL_NONE,
+ };
+ std::copy(tmpAttribs, tmpAttribs + (sizeof(tmpAttribs) / sizeof(EGLint)),
+ std::back_inserter(attribs));
+ wantedAttribute = EGL_NONE;
+ wantedAttributeValue = EGL_NONE;
+ } else {
+ // if no renderable type specified, fallback to a simplified query
+ wantedAttribute = EGL_NATIVE_VISUAL_ID;
+ wantedAttributeValue = format;
+ }
+
+ err = selectConfigForAttribute(display, attribs.data(), wantedAttribute, wantedAttributeValue,
+ config);
+ if (err == NO_ERROR) {
+ EGLint caveat;
+ if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat))
+ ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!");
+ }
+
+ return err;
+}
+
+std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(const RenderEngineCreationArgs& args) {
+ // initialize EGL for the default display
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (!eglInitialize(display, nullptr, nullptr)) {
+ LOG_ALWAYS_FATAL("failed to initialize EGL");
+ }
+
+ const auto eglVersion = eglQueryString(display, EGL_VERSION);
+ if (!eglVersion) {
+ checkGlError(__FUNCTION__, __LINE__);
+ LOG_ALWAYS_FATAL("eglQueryString(EGL_VERSION) failed");
+ }
+
+ const auto eglExtensions = eglQueryString(display, EGL_EXTENSIONS);
+ if (!eglExtensions) {
+ checkGlError(__FUNCTION__, __LINE__);
+ LOG_ALWAYS_FATAL("eglQueryString(EGL_EXTENSIONS) failed");
+ }
+
+ GLExtensions& extensions = GLExtensions::getInstance();
+ extensions.initWithEGLStrings(eglVersion, eglExtensions);
+
+ // The code assumes that ES2 or later is available if this extension is
+ // supported.
+ EGLConfig config = EGL_NO_CONFIG;
+ if (!extensions.hasNoConfigContext()) {
+ config = chooseEglConfig(display, args.pixelFormat, /*logConfig*/ true);
+ }
+
+ bool useContextPriority =
+ extensions.hasContextPriority() && args.contextPriority == ContextPriority::HIGH;
+ EGLContext protectedContext = EGL_NO_CONTEXT;
+ if (args.enableProtectedContext && extensions.hasProtectedContent()) {
+ protectedContext = createEglContext(display, config, nullptr, useContextPriority,
+ Protection::PROTECTED);
+ ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context");
+ }
+
+ EGLContext ctxt = createEglContext(display, config, protectedContext, useContextPriority,
+ Protection::UNPROTECTED);
+
+ // if can't create a GL context, we can only abort.
+ LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
+
+ EGLSurface stub = EGL_NO_SURFACE;
+ if (!extensions.hasSurfacelessContext()) {
+ stub = createStubEglPbufferSurface(display, config, args.pixelFormat,
+ Protection::UNPROTECTED);
+ LOG_ALWAYS_FATAL_IF(stub == EGL_NO_SURFACE, "can't create stub pbuffer");
+ }
+ EGLBoolean success = eglMakeCurrent(display, stub, stub, ctxt);
+ LOG_ALWAYS_FATAL_IF(!success, "can't make stub pbuffer current");
+ extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER),
+ glGetString(GL_VERSION), glGetString(GL_EXTENSIONS));
+
+ EGLSurface protectedStub = EGL_NO_SURFACE;
+ if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) {
+ protectedStub = createStubEglPbufferSurface(display, config, args.pixelFormat,
+ Protection::PROTECTED);
+ ALOGE_IF(protectedStub == EGL_NO_SURFACE, "can't create protected stub pbuffer");
+ }
+
+ // now figure out what version of GL did we actually get
+ GlesVersion version = parseGlesVersion(extensions.getVersion());
+
+ LOG_ALWAYS_FATAL_IF(args.supportsBackgroundBlur && version < GLES_VERSION_3_0,
+ "Blurs require OpenGL ES 3.0. Please unset ro.surface_flinger.supports_background_blur");
+
+ // initialize the renderer while GL is current
+ std::unique_ptr<GLESRenderEngine> engine;
+ switch (version) {
+ case GLES_VERSION_1_0:
+ case GLES_VERSION_1_1:
+ LOG_ALWAYS_FATAL("SurfaceFlinger requires OpenGL ES 2.0 minimum to run.");
+ break;
+ case GLES_VERSION_2_0:
+ case GLES_VERSION_3_0:
+ engine = std::make_unique<GLESRenderEngine>(args, display, config, ctxt, stub,
+ protectedContext, protectedStub);
+ break;
+ }
+
+ ALOGI("OpenGL ES informations:");
+ ALOGI("vendor : %s", extensions.getVendor());
+ ALOGI("renderer : %s", extensions.getRenderer());
+ ALOGI("version : %s", extensions.getVersion());
+ ALOGI("extensions: %s", extensions.getExtensions());
+ ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize());
+ ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims());
+
+ return engine;
+}
+
+EGLConfig GLESRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) {
+ status_t err;
+ EGLConfig config;
+
+ // First try to get an ES3 config
+ err = selectEGLConfig(display, format, EGL_OPENGL_ES3_BIT, &config);
+ if (err != NO_ERROR) {
+ // If ES3 fails, try to get an ES2 config
+ err = selectEGLConfig(display, format, EGL_OPENGL_ES2_BIT, &config);
+ if (err != NO_ERROR) {
+ // If ES2 still doesn't work, probably because we're on the emulator.
+ // try a simplified query
+ ALOGW("no suitable EGLConfig found, trying a simpler query");
+ err = selectEGLConfig(display, format, 0, &config);
+ if (err != NO_ERROR) {
+ // this EGL is too lame for android
+ LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up");
+ }
+ }
+ }
+
+ if (logConfig) {
+ // print some debugging info
+ EGLint r, g, b, a;
+ eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r);
+ eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g);
+ eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b);
+ eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);
+ ALOGI("EGL information:");
+ ALOGI("vendor : %s", eglQueryString(display, EGL_VENDOR));
+ ALOGI("version : %s", eglQueryString(display, EGL_VERSION));
+ ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS));
+ ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported");
+ ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);
+ }
+
+ return config;
+}
+
+GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
+ EGLConfig config, EGLContext ctxt, EGLSurface stub,
+ EGLContext protectedContext, EGLSurface protectedStub)
+ : renderengine::impl::RenderEngine(args),
+ mEGLDisplay(display),
+ mEGLConfig(config),
+ mEGLContext(ctxt),
+ mStubSurface(stub),
+ mProtectedEGLContext(protectedContext),
+ mProtectedStubSurface(protectedStub),
+ mVpWidth(0),
+ mVpHeight(0),
+ mFramebufferImageCacheSize(args.imageCacheSize),
+ mUseColorManagement(args.useColorManagement) {
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
+ glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ glPixelStorei(GL_PACK_ALIGNMENT, 4);
+
+ // Initialize protected EGL Context.
+ if (mProtectedEGLContext != EGL_NO_CONTEXT) {
+ EGLBoolean success = eglMakeCurrent(display, mProtectedStubSurface, mProtectedStubSurface,
+ mProtectedEGLContext);
+ ALOGE_IF(!success, "can't make protected context current");
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ glPixelStorei(GL_PACK_ALIGNMENT, 4);
+ success = eglMakeCurrent(display, mStubSurface, mStubSurface, mEGLContext);
+ LOG_ALWAYS_FATAL_IF(!success, "can't make default context current");
+ }
+
+ // mColorBlindnessCorrection = M;
+
+ if (mUseColorManagement) {
+ const ColorSpace srgb(ColorSpace::sRGB());
+ const ColorSpace displayP3(ColorSpace::DisplayP3());
+ const ColorSpace bt2020(ColorSpace::BT2020());
+
+ // no chromatic adaptation needed since all color spaces use D65 for their white points.
+ mSrgbToXyz = mat4(srgb.getRGBtoXYZ());
+ mDisplayP3ToXyz = mat4(displayP3.getRGBtoXYZ());
+ mBt2020ToXyz = mat4(bt2020.getRGBtoXYZ());
+ mXyzToSrgb = mat4(srgb.getXYZtoRGB());
+ mXyzToDisplayP3 = mat4(displayP3.getXYZtoRGB());
+ mXyzToBt2020 = mat4(bt2020.getXYZtoRGB());
+
+ // Compute sRGB to Display P3 and BT2020 transform matrix.
+ // NOTE: For now, we are limiting output wide color space support to
+ // Display-P3 and BT2020 only.
+ mSrgbToDisplayP3 = mXyzToDisplayP3 * mSrgbToXyz;
+ mSrgbToBt2020 = mXyzToBt2020 * mSrgbToXyz;
+
+ // Compute Display P3 to sRGB and BT2020 transform matrix.
+ mDisplayP3ToSrgb = mXyzToSrgb * mDisplayP3ToXyz;
+ mDisplayP3ToBt2020 = mXyzToBt2020 * mDisplayP3ToXyz;
+
+ // Compute BT2020 to sRGB and Display P3 transform matrix
+ mBt2020ToSrgb = mXyzToSrgb * mBt2020ToXyz;
+ mBt2020ToDisplayP3 = mXyzToDisplayP3 * mBt2020ToXyz;
+ }
+
+ char value[PROPERTY_VALUE_MAX];
+ property_get("debug.egl.traceGpuCompletion", value, "0");
+ if (atoi(value)) {
+ mTraceGpuCompletion = true;
+ mFlushTracer = std::make_unique<FlushTracer>(this);
+ }
+
+ if (args.supportsBackgroundBlur) {
+ mBlurFilter = new BlurFilter(*this);
+ checkErrors("BlurFilter creation");
+ }
+
+ mImageManager = std::make_unique<ImageManager>(this);
+ mImageManager->initThread(args.realtime);
+ mDrawingBuffer = createFramebuffer();
+ sp<GraphicBuffer> buf =
+ new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE, "placeholder");
+
+ const status_t err = buf->initCheck();
+ if (err != OK) {
+ ALOGE("Error allocating placeholder buffer: %d", err);
+ return;
+ }
+ mPlaceholderBuffer = buf.get();
+ EGLint attributes[] = {
+ EGL_NONE,
+ };
+ mPlaceholderImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+ mPlaceholderBuffer, attributes);
+ ALOGE_IF(mPlaceholderImage == EGL_NO_IMAGE_KHR, "Failed to create placeholder image: %#x",
+ eglGetError());
+}
+
+GLESRenderEngine::~GLESRenderEngine() {
+ // Destroy the image manager first.
+ mImageManager = nullptr;
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ unbindFrameBuffer(mDrawingBuffer.get());
+ mDrawingBuffer = nullptr;
+ while (!mFramebufferImageCache.empty()) {
+ EGLImageKHR expired = mFramebufferImageCache.front().second;
+ mFramebufferImageCache.pop_front();
+ eglDestroyImageKHR(mEGLDisplay, expired);
+ }
+ eglDestroyImageKHR(mEGLDisplay, mPlaceholderImage);
+ mImageCache.clear();
+ eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglTerminate(mEGLDisplay);
+}
+
+std::unique_ptr<Framebuffer> GLESRenderEngine::createFramebuffer() {
+ return std::make_unique<GLFramebuffer>(*this);
+}
+
+std::unique_ptr<Image> GLESRenderEngine::createImage() {
+ return std::make_unique<GLImage>(*this);
+}
+
+Framebuffer* GLESRenderEngine::getFramebufferForDrawing() {
+ return mDrawingBuffer.get();
+}
+
+void GLESRenderEngine::primeCache() const {
+ ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
+ mArgs.useColorManagement,
+ mArgs.precacheToneMapperShaderOnly);
+}
+
+base::unique_fd GLESRenderEngine::flush() {
+ ATRACE_CALL();
+ if (!GLExtensions::getInstance().hasNativeFenceSync()) {
+ return base::unique_fd();
+ }
+
+ EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+ if (sync == EGL_NO_SYNC_KHR) {
+ ALOGW("failed to create EGL native fence sync: %#x", eglGetError());
+ return base::unique_fd();
+ }
+
+ // native fence fd will not be populated until flush() is done.
+ glFlush();
+
+ // get the fence fd
+ base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync));
+ eglDestroySyncKHR(mEGLDisplay, sync);
+ if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+ ALOGW("failed to dup EGL native fence sync: %#x", eglGetError());
+ }
+
+ // Only trace if we have a valid fence, as current usage falls back to
+ // calling finish() if the fence fd is invalid.
+ if (CC_UNLIKELY(mTraceGpuCompletion && mFlushTracer) && fenceFd.get() >= 0) {
+ mFlushTracer->queueSync(eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr));
+ }
+
+ return fenceFd;
+}
+
+bool GLESRenderEngine::finish() {
+ ATRACE_CALL();
+ if (!GLExtensions::getInstance().hasFenceSync()) {
+ ALOGW("no synchronization support");
+ return false;
+ }
+
+ EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr);
+ if (sync == EGL_NO_SYNC_KHR) {
+ ALOGW("failed to create EGL fence sync: %#x", eglGetError());
+ return false;
+ }
+
+ if (CC_UNLIKELY(mTraceGpuCompletion && mFlushTracer)) {
+ mFlushTracer->queueSync(eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr));
+ }
+
+ return waitSync(sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR);
+}
+
+bool GLESRenderEngine::waitSync(EGLSyncKHR sync, EGLint flags) {
+ EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, flags, 2000000000 /*2 sec*/);
+ EGLint error = eglGetError();
+ eglDestroySyncKHR(mEGLDisplay, sync);
+ if (result != EGL_CONDITION_SATISFIED_KHR) {
+ if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+ ALOGW("fence wait timed out");
+ } else {
+ ALOGW("error waiting on EGL fence: %#x", error);
+ }
+ return false;
+ }
+
+ return true;
+}
+
+bool GLESRenderEngine::waitFence(base::unique_fd fenceFd) {
+ if (!GLExtensions::getInstance().hasNativeFenceSync() ||
+ !GLExtensions::getInstance().hasWaitSync()) {
+ return false;
+ }
+
+ // release the fd and transfer the ownership to EGLSync
+ EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd.release(), EGL_NONE};
+ EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+ if (sync == EGL_NO_SYNC_KHR) {
+ ALOGE("failed to create EGL native fence sync: %#x", eglGetError());
+ return false;
+ }
+
+ // XXX: The spec draft is inconsistent as to whether this should return an
+ // EGLint or void. Ignore the return value for now, as it's not strictly
+ // needed.
+ eglWaitSyncKHR(mEGLDisplay, sync, 0);
+ EGLint error = eglGetError();
+ eglDestroySyncKHR(mEGLDisplay, sync);
+ if (error != EGL_SUCCESS) {
+ ALOGE("failed to wait for EGL native fence sync: %#x", error);
+ return false;
+ }
+
+ return true;
+}
+
+void GLESRenderEngine::clearWithColor(float red, float green, float blue, float alpha) {
+ ATRACE_CALL();
+ glDisable(GL_BLEND);
+ glClearColor(red, green, blue, alpha);
+ glClear(GL_COLOR_BUFFER_BIT);
+}
+
+void GLESRenderEngine::fillRegionWithColor(const Region& region, float red, float green, float blue,
+ float alpha) {
+ size_t c;
+ Rect const* r = region.getArray(&c);
+ Mesh mesh = Mesh::Builder()
+ .setPrimitive(Mesh::TRIANGLES)
+ .setVertices(c * 6 /* count */, 2 /* size */)
+ .build();
+ Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
+ for (size_t i = 0; i < c; i++, r++) {
+ position[i * 6 + 0].x = r->left;
+ position[i * 6 + 0].y = r->top;
+ position[i * 6 + 1].x = r->left;
+ position[i * 6 + 1].y = r->bottom;
+ position[i * 6 + 2].x = r->right;
+ position[i * 6 + 2].y = r->bottom;
+ position[i * 6 + 3].x = r->left;
+ position[i * 6 + 3].y = r->top;
+ position[i * 6 + 4].x = r->right;
+ position[i * 6 + 4].y = r->bottom;
+ position[i * 6 + 5].x = r->right;
+ position[i * 6 + 5].y = r->top;
+ }
+ setupFillWithColor(red, green, blue, alpha);
+ drawMesh(mesh);
+}
+
+void GLESRenderEngine::setScissor(const Rect& region) {
+ glScissor(region.left, region.top, region.getWidth(), region.getHeight());
+ glEnable(GL_SCISSOR_TEST);
+}
+
+void GLESRenderEngine::disableScissor() {
+ glDisable(GL_SCISSOR_TEST);
+}
+
+void GLESRenderEngine::genTextures(size_t count, uint32_t* names) {
+ glGenTextures(count, names);
+}
+
+void GLESRenderEngine::deleteTextures(size_t count, uint32_t const* names) {
+ for (int i = 0; i < count; ++i) {
+ mTextureView.erase(names[i]);
+ }
+ glDeleteTextures(count, names);
+}
+
+void GLESRenderEngine::bindExternalTextureImage(uint32_t texName, const Image& image) {
+ ATRACE_CALL();
+ const GLImage& glImage = static_cast<const GLImage&>(image);
+ const GLenum target = GL_TEXTURE_EXTERNAL_OES;
+
+ glBindTexture(target, texName);
+ if (glImage.getEGLImage() != EGL_NO_IMAGE_KHR) {
+ glEGLImageTargetTexture2DOES(target, static_cast<GLeglImageOES>(glImage.getEGLImage()));
+ }
+}
+
+status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName,
+ const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& bufferFence) {
+ if (buffer == nullptr) {
+ return BAD_VALUE;
+ }
+
+ ATRACE_CALL();
+
+ bool found = false;
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ auto cachedImage = mImageCache.find(buffer->getId());
+ found = (cachedImage != mImageCache.end());
+ }
+
+ // If we couldn't find the image in the cache at this time, then either
+ // SurfaceFlinger messed up registering the buffer ahead of time or we got
+ // backed up creating other EGLImages.
+ if (!found) {
+ status_t cacheResult = mImageManager->cache(buffer);
+ if (cacheResult != NO_ERROR) {
+ return cacheResult;
+ }
+ }
+
+ // Whether or not we needed to cache, re-check mImageCache to make sure that
+ // there's an EGLImage. The current threading model guarantees that we don't
+ // destroy a cached image until it's really not needed anymore (i.e. this
+ // function should not be called), so the only possibility is that something
+ // terrible went wrong and we should just bind something and move on.
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ auto cachedImage = mImageCache.find(buffer->getId());
+
+ if (cachedImage == mImageCache.end()) {
+ // We failed creating the image if we got here, so bail out.
+ ALOGE("Failed to create an EGLImage when rendering");
+ bindExternalTextureImage(texName, *createImage());
+ return NO_INIT;
+ }
+
+ bindExternalTextureImage(texName, *cachedImage->second);
+ mTextureView.insert_or_assign(texName, buffer->getId());
+ }
+
+ // Wait for the new buffer to be ready.
+ if (bufferFence != nullptr && bufferFence->isValid()) {
+ if (GLExtensions::getInstance().hasWaitSync()) {
+ base::unique_fd fenceFd(bufferFence->dup());
+ if (fenceFd == -1) {
+ ALOGE("error dup'ing fence fd: %d", errno);
+ return -errno;
+ }
+ if (!waitFence(std::move(fenceFd))) {
+ ALOGE("failed to wait on fence fd");
+ return UNKNOWN_ERROR;
+ }
+ } else {
+ status_t err = bufferFence->waitForever("RenderEngine::bindExternalTextureBuffer");
+ if (err != NO_ERROR) {
+ ALOGE("error waiting for fence: %d", err);
+ return err;
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+void GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+ mImageManager->cacheAsync(buffer, nullptr);
+}
+
+std::shared_ptr<ImageManager::Barrier> GLESRenderEngine::cacheExternalTextureBufferForTesting(
+ const sp<GraphicBuffer>& buffer) {
+ auto barrier = std::make_shared<ImageManager::Barrier>();
+ mImageManager->cacheAsync(buffer, barrier);
+ return barrier;
+}
+
+status_t GLESRenderEngine::cacheExternalTextureBufferInternal(const sp<GraphicBuffer>& buffer) {
+ if (buffer == nullptr) {
+ return BAD_VALUE;
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ if (mImageCache.count(buffer->getId()) > 0) {
+ // If there's already an image then fail fast here.
+ return NO_ERROR;
+ }
+ }
+ ATRACE_CALL();
+
+ // Create the image without holding a lock so that we don't block anything.
+ std::unique_ptr<Image> newImage = createImage();
+
+ bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(),
+ buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
+ if (!created) {
+ ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
+ buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(),
+ buffer->getPixelFormat());
+ return NO_INIT;
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ if (mImageCache.count(buffer->getId()) > 0) {
+ // In theory it's possible for another thread to recache the image,
+ // so bail out if another thread won.
+ return NO_ERROR;
+ }
+ mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage)));
+ }
+
+ return NO_ERROR;
+}
+
+void GLESRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
+ mImageManager->releaseAsync(bufferId, nullptr);
+}
+
+std::shared_ptr<ImageManager::Barrier> GLESRenderEngine::unbindExternalTextureBufferForTesting(
+ uint64_t bufferId) {
+ auto barrier = std::make_shared<ImageManager::Barrier>();
+ mImageManager->releaseAsync(bufferId, barrier);
+ return barrier;
+}
+
+void GLESRenderEngine::unbindExternalTextureBufferInternal(uint64_t bufferId) {
+ std::unique_ptr<Image> image;
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ const auto& cachedImage = mImageCache.find(bufferId);
+
+ if (cachedImage != mImageCache.end()) {
+ ALOGV("Destroying image for buffer: %" PRIu64, bufferId);
+ // Move the buffer out of cache first, so that we can destroy
+ // without holding the cache's lock.
+ image = std::move(cachedImage->second);
+ mImageCache.erase(bufferId);
+ return;
+ }
+ }
+ ALOGV("Failed to find image for buffer: %" PRIu64, bufferId);
+}
+
+FloatRect GLESRenderEngine::setupLayerCropping(const LayerSettings& layer, Mesh& mesh) {
+ // Translate win by the rounded corners rect coordinates, to have all values in
+ // layer coordinate space.
+ FloatRect cropWin = layer.geometry.boundaries;
+ const FloatRect& roundedCornersCrop = layer.geometry.roundedCornersCrop;
+ cropWin.left -= roundedCornersCrop.left;
+ cropWin.right -= roundedCornersCrop.left;
+ cropWin.top -= roundedCornersCrop.top;
+ cropWin.bottom -= roundedCornersCrop.top;
+ Mesh::VertexArray<vec2> cropCoords(mesh.getCropCoordArray<vec2>());
+ cropCoords[0] = vec2(cropWin.left, cropWin.top);
+ cropCoords[1] = vec2(cropWin.left, cropWin.top + cropWin.getHeight());
+ cropCoords[2] = vec2(cropWin.right, cropWin.top + cropWin.getHeight());
+ cropCoords[3] = vec2(cropWin.right, cropWin.top);
+
+ setupCornerRadiusCropSize(roundedCornersCrop.getWidth(), roundedCornersCrop.getHeight());
+ return cropWin;
+}
+
+void GLESRenderEngine::handleRoundedCorners(const DisplaySettings& display,
+ const LayerSettings& layer, const Mesh& mesh) {
+ // We separate the layer into 3 parts essentially, such that we only turn on blending for the
+ // top rectangle and the bottom rectangle, and turn off blending for the middle rectangle.
+ FloatRect bounds = layer.geometry.roundedCornersCrop;
+
+ // Explicitly compute the transform from the clip rectangle to the physical
+ // display. Normally, this is done in glViewport but we explicitly compute
+ // it here so that we can get the scissor bounds correct.
+ const Rect& source = display.clip;
+ const Rect& destination = display.physicalDisplay;
+ // Here we compute the following transform:
+ // 1. Translate the top left corner of the source clip to (0, 0)
+ // 2. Rotate the clip rectangle about the origin in accordance with the
+ // orientation flag
+ // 3. Translate the top left corner back to the origin.
+ // 4. Scale the clip rectangle to the destination rectangle dimensions
+ // 5. Translate the top left corner to the destination rectangle's top left
+ // corner.
+ const mat4 translateSource = mat4::translate(vec4(-source.left, -source.top, 0, 1));
+ mat4 rotation;
+ int displacementX = 0;
+ int displacementY = 0;
+ float destinationWidth = static_cast<float>(destination.getWidth());
+ float destinationHeight = static_cast<float>(destination.getHeight());
+ float sourceWidth = static_cast<float>(source.getWidth());
+ float sourceHeight = static_cast<float>(source.getHeight());
+ const float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f;
+ switch (display.orientation) {
+ case ui::Transform::ROT_90:
+ rotation = mat4::rotate(rot90InRadians, vec3(0, 0, 1));
+ displacementX = source.getHeight();
+ std::swap(sourceHeight, sourceWidth);
+ break;
+ case ui::Transform::ROT_180:
+ rotation = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1));
+ displacementY = source.getHeight();
+ displacementX = source.getWidth();
+ break;
+ case ui::Transform::ROT_270:
+ rotation = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1));
+ displacementY = source.getWidth();
+ std::swap(sourceHeight, sourceWidth);
+ break;
+ default:
+ break;
+ }
+
+ const mat4 intermediateTranslation = mat4::translate(vec4(displacementX, displacementY, 0, 1));
+ const mat4 scale = mat4::scale(
+ vec4(destinationWidth / sourceWidth, destinationHeight / sourceHeight, 1, 1));
+ const mat4 translateDestination =
+ mat4::translate(vec4(destination.left, destination.top, 0, 1));
+ const mat4 globalTransform =
+ translateDestination * scale * intermediateTranslation * rotation * translateSource;
+
+ const mat4 transformMatrix = globalTransform * layer.geometry.positionTransform;
+ const vec4 leftTopCoordinate(bounds.left, bounds.top, 1.0, 1.0);
+ const vec4 rightBottomCoordinate(bounds.right, bounds.bottom, 1.0, 1.0);
+ const vec4 leftTopCoordinateInBuffer = transformMatrix * leftTopCoordinate;
+ const vec4 rightBottomCoordinateInBuffer = transformMatrix * rightBottomCoordinate;
+ bounds = FloatRect(std::min(leftTopCoordinateInBuffer[0], rightBottomCoordinateInBuffer[0]),
+ std::min(leftTopCoordinateInBuffer[1], rightBottomCoordinateInBuffer[1]),
+ std::max(leftTopCoordinateInBuffer[0], rightBottomCoordinateInBuffer[0]),
+ std::max(leftTopCoordinateInBuffer[1], rightBottomCoordinateInBuffer[1]));
+
+ // Finally, we cut the layer into 3 parts, with top and bottom parts having rounded corners
+ // and the middle part without rounded corners.
+ const int32_t radius = ceil(layer.geometry.roundedCornersRadius);
+ const Rect topRect(bounds.left, bounds.top, bounds.right, bounds.top + radius);
+ setScissor(topRect);
+ drawMesh(mesh);
+ const Rect bottomRect(bounds.left, bounds.bottom - radius, bounds.right, bounds.bottom);
+ setScissor(bottomRect);
+ drawMesh(mesh);
+
+ // The middle part of the layer can turn off blending.
+ if (topRect.bottom < bottomRect.top) {
+ const Rect middleRect(bounds.left, bounds.top + radius, bounds.right,
+ bounds.bottom - radius);
+ setScissor(middleRect);
+ mState.cornerRadius = 0.0;
+ disableBlending();
+ drawMesh(mesh);
+ }
+ disableScissor();
+}
+
+status_t GLESRenderEngine::bindFrameBuffer(Framebuffer* framebuffer) {
+ ATRACE_CALL();
+ GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(framebuffer);
+ EGLImageKHR eglImage = glFramebuffer->getEGLImage();
+ uint32_t textureName = glFramebuffer->getTextureName();
+ uint32_t framebufferName = glFramebuffer->getFramebufferName();
+
+ // Bind the texture and turn our EGLImage into a texture
+ glBindTexture(GL_TEXTURE_2D, textureName);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)eglImage);
+
+ // Bind the Framebuffer to render into
+ glBindFramebuffer(GL_FRAMEBUFFER, framebufferName);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureName, 0);
+
+ uint32_t glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ ALOGE_IF(glStatus != GL_FRAMEBUFFER_COMPLETE_OES, "glCheckFramebufferStatusOES error %d",
+ glStatus);
+
+ return glStatus == GL_FRAMEBUFFER_COMPLETE_OES ? NO_ERROR : BAD_VALUE;
+}
+
+void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /*framebuffer*/) {
+ ATRACE_CALL();
+
+ // back to main framebuffer
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+bool GLESRenderEngine::cleanupPostRender(CleanupMode mode) {
+ ATRACE_CALL();
+
+ if (mPriorResourcesCleaned ||
+ (mLastDrawFence != nullptr && mLastDrawFence->getStatus() != Fence::Status::Signaled)) {
+ // If we don't have a prior frame needing cleanup, then don't do anything.
+ return false;
+ }
+
+ // This is a bit of a band-aid fix for FrameCaptureProcessor, as we should
+ // not need to keep memory around if we don't need to do so.
+ if (mode == CleanupMode::CLEAN_ALL) {
+ // TODO: SurfaceFlinger memory utilization may benefit from resetting
+ // texture bindings as well. Assess if it does and there's no performance regression
+ // when rebinding the same image data to the same texture, and if so then its mode
+ // behavior can be tweaked.
+ if (mPlaceholderImage != EGL_NO_IMAGE_KHR) {
+ for (auto [textureName, bufferId] : mTextureView) {
+ if (bufferId && mPlaceholderImage != EGL_NO_IMAGE_KHR) {
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureName);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES,
+ static_cast<GLeglImageOES>(mPlaceholderImage));
+ mTextureView[textureName] = std::nullopt;
+ checkErrors();
+ }
+ }
+ }
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ mImageCache.clear();
+ }
+ }
+
+ // Bind the texture to placeholder so that backing image data can be freed.
+ GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing());
+ glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer);
+ // Release the cached fence here, so that we don't churn reallocations when
+ // we could no-op repeated calls of this method instead.
+ mLastDrawFence = nullptr;
+ mPriorResourcesCleaned = true;
+ return true;
+}
+
+void GLESRenderEngine::checkErrors() const {
+ checkErrors(nullptr);
+}
+
+void GLESRenderEngine::checkErrors(const char* tag) const {
+ do {
+ // there could be more than one error flag
+ GLenum error = glGetError();
+ if (error == GL_NO_ERROR) break;
+ if (tag == nullptr) {
+ ALOGE("GL error 0x%04x", int(error));
+ } else {
+ ALOGE("GL error: %s -> 0x%04x", tag, int(error));
+ }
+ } while (true);
+}
+
+bool GLESRenderEngine::supportsProtectedContent() const {
+ return mProtectedEGLContext != EGL_NO_CONTEXT;
+}
+
+bool GLESRenderEngine::useProtectedContext(bool useProtectedContext) {
+ if (useProtectedContext == mInProtectedContext) {
+ return true;
+ }
+ if (useProtectedContext && mProtectedEGLContext == EGL_NO_CONTEXT) {
+ return false;
+ }
+ const EGLSurface surface = useProtectedContext ? mProtectedStubSurface : mStubSurface;
+ const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext;
+ const bool success = eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE;
+ if (success) {
+ mInProtectedContext = useProtectedContext;
+ }
+ return success;
+}
+EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer,
+ bool isProtected,
+ bool useFramebufferCache) {
+ sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(nativeBuffer);
+ if (useFramebufferCache) {
+ std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
+ for (const auto& image : mFramebufferImageCache) {
+ if (image.first == graphicBuffer->getId()) {
+ return image.second;
+ }
+ }
+ }
+ EGLint attributes[] = {
+ isProtected ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE,
+ isProtected ? EGL_TRUE : EGL_NONE,
+ EGL_NONE,
+ };
+ EGLImageKHR image = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+ nativeBuffer, attributes);
+ if (useFramebufferCache) {
+ if (image != EGL_NO_IMAGE_KHR) {
+ std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
+ if (mFramebufferImageCache.size() >= mFramebufferImageCacheSize) {
+ EGLImageKHR expired = mFramebufferImageCache.front().second;
+ mFramebufferImageCache.pop_front();
+ eglDestroyImageKHR(mEGLDisplay, expired);
+ }
+ mFramebufferImageCache.push_back({graphicBuffer->getId(), image});
+ }
+ }
+
+ return image;
+}
+
+status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
+ const std::vector<const LayerSettings*>& layers,
+ const sp<GraphicBuffer>& buffer,
+ const bool useFramebufferCache, base::unique_fd&& bufferFence,
+ base::unique_fd* drawFence) {
+ ATRACE_CALL();
+ if (layers.empty()) {
+ ALOGV("Drawing empty layer stack");
+ return NO_ERROR;
+ }
+
+ if (bufferFence.get() >= 0) {
+ // Duplicate the fence for passing to waitFence.
+ base::unique_fd bufferFenceDup(dup(bufferFence.get()));
+ if (bufferFenceDup < 0 || !waitFence(std::move(bufferFenceDup))) {
+ ATRACE_NAME("Waiting before draw");
+ sync_wait(bufferFence.get(), -1);
+ }
+ }
+
+ if (buffer == nullptr) {
+ ALOGE("No output buffer provided. Aborting GPU composition.");
+ return BAD_VALUE;
+ }
+
+ std::unique_ptr<BindNativeBufferAsFramebuffer> fbo;
+ // Gathering layers that requested blur, we'll need them to decide when to render to an
+ // offscreen buffer, and when to render to the native buffer.
+ std::deque<const LayerSettings*> blurLayers;
+ if (CC_LIKELY(mBlurFilter != nullptr)) {
+ for (auto layer : layers) {
+ if (layer->backgroundBlurRadius > 0) {
+ blurLayers.push_back(layer);
+ }
+ }
+ }
+ const auto blurLayersSize = blurLayers.size();
+
+ if (blurLayersSize == 0) {
+ fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this,
+ buffer.get()->getNativeBuffer(),
+ useFramebufferCache);
+ if (fbo->getStatus() != NO_ERROR) {
+ ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
+ buffer->handle);
+ checkErrors();
+ return fbo->getStatus();
+ }
+ setViewportAndProjection(display.physicalDisplay, display.clip);
+ } else {
+ setViewportAndProjection(display.physicalDisplay, display.clip);
+ auto status =
+ mBlurFilter->setAsDrawTarget(display, blurLayers.front()->backgroundBlurRadius);
+ if (status != NO_ERROR) {
+ ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).",
+ buffer->handle);
+ checkErrors();
+ return status;
+ }
+ }
+
+ // clear the entire buffer, sometimes when we reuse buffers we'd persist
+ // ghost images otherwise.
+ // we also require a full transparent framebuffer for overlays. This is
+ // probably not quite efficient on all GPUs, since we could filter out
+ // opaque layers.
+ clearWithColor(0.0, 0.0, 0.0, 0.0);
+
+ setOutputDataSpace(display.outputDataspace);
+ setDisplayMaxLuminance(display.maxLuminance);
+
+ const mat4 projectionMatrix =
+ ui::Transform(display.orientation).asMatrix4() * mState.projectionMatrix;
+ if (!display.clearRegion.isEmpty()) {
+ glDisable(GL_BLEND);
+ fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
+ }
+
+ Mesh mesh = Mesh::Builder()
+ .setPrimitive(Mesh::TRIANGLE_FAN)
+ .setVertices(4 /* count */, 2 /* size */)
+ .setTexCoords(2 /* size */)
+ .setCropCoords(2 /* size */)
+ .build();
+ for (auto const layer : layers) {
+ if (blurLayers.size() > 0 && blurLayers.front() == layer) {
+ blurLayers.pop_front();
+
+ auto status = mBlurFilter->prepare();
+ if (status != NO_ERROR) {
+ ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
+ buffer->handle);
+ checkErrors("Can't render first blur pass");
+ return status;
+ }
+
+ if (blurLayers.size() == 0) {
+ // Done blurring, time to bind the native FBO and render our blur onto it.
+ fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this,
+ buffer.get()
+ ->getNativeBuffer(),
+ useFramebufferCache);
+ status = fbo->getStatus();
+ setViewportAndProjection(display.physicalDisplay, display.clip);
+ } else {
+ // There's still something else to blur, so let's keep rendering to our FBO
+ // instead of to the display.
+ status = mBlurFilter->setAsDrawTarget(display,
+ blurLayers.front()->backgroundBlurRadius);
+ }
+ if (status != NO_ERROR) {
+ ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
+ buffer->handle);
+ checkErrors("Can't bind native framebuffer");
+ return status;
+ }
+
+ status = mBlurFilter->render(blurLayersSize > 1);
+ if (status != NO_ERROR) {
+ ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
+ buffer->handle);
+ checkErrors("Can't render blur filter");
+ return status;
+ }
+ }
+
+ mState.maxMasteringLuminance = layer->source.buffer.maxMasteringLuminance;
+ mState.maxContentLuminance = layer->source.buffer.maxContentLuminance;
+ mState.projectionMatrix = projectionMatrix * layer->geometry.positionTransform;
+
+ const FloatRect bounds = layer->geometry.boundaries;
+ Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
+ position[0] = vec2(bounds.left, bounds.top);
+ position[1] = vec2(bounds.left, bounds.bottom);
+ position[2] = vec2(bounds.right, bounds.bottom);
+ position[3] = vec2(bounds.right, bounds.top);
+
+ setupLayerCropping(*layer, mesh);
+ setColorTransform(display.colorTransform * layer->colorTransform);
+
+ bool usePremultipliedAlpha = true;
+ bool disableTexture = true;
+ bool isOpaque = false;
+ if (layer->source.buffer.buffer != nullptr) {
+ disableTexture = false;
+ isOpaque = layer->source.buffer.isOpaque;
+
+ sp<GraphicBuffer> gBuf = layer->source.buffer.buffer;
+ bindExternalTextureBuffer(layer->source.buffer.textureName, gBuf,
+ layer->source.buffer.fence);
+
+ usePremultipliedAlpha = layer->source.buffer.usePremultipliedAlpha;
+ Texture texture(Texture::TEXTURE_EXTERNAL, layer->source.buffer.textureName);
+ mat4 texMatrix = layer->source.buffer.textureTransform;
+
+ texture.setMatrix(texMatrix.asArray());
+ texture.setFiltering(layer->source.buffer.useTextureFiltering);
+
+ texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
+ setSourceY410BT2020(layer->source.buffer.isY410BT2020);
+
+ renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
+ texCoords[0] = vec2(0.0, 0.0);
+ texCoords[1] = vec2(0.0, 1.0);
+ texCoords[2] = vec2(1.0, 1.0);
+ texCoords[3] = vec2(1.0, 0.0);
+ setupLayerTexturing(texture);
+ }
+
+ const half3 solidColor = layer->source.solidColor;
+ const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer->alpha);
+ // Buffer sources will have a black solid color ignored in the shader,
+ // so in that scenario the solid color passed here is arbitrary.
+ setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
+ layer->geometry.roundedCornersRadius);
+ if (layer->disableBlending) {
+ glDisable(GL_BLEND);
+ }
+ setSourceDataSpace(layer->sourceDataspace);
+
+ if (layer->shadow.length > 0.0f) {
+ handleShadow(layer->geometry.boundaries, layer->geometry.roundedCornersRadius,
+ layer->shadow);
+ }
+ // We only want to do a special handling for rounded corners when having rounded corners
+ // is the only reason it needs to turn on blending, otherwise, we handle it like the
+ // usual way since it needs to turn on blending anyway.
+ else if (layer->geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
+ handleRoundedCorners(display, *layer, mesh);
+ } else {
+ drawMesh(mesh);
+ }
+
+ // Cleanup if there's a buffer source
+ if (layer->source.buffer.buffer != nullptr) {
+ disableBlending();
+ setSourceY410BT2020(false);
+ disableTexturing();
+ }
+ }
+
+ if (drawFence != nullptr) {
+ *drawFence = flush();
+ }
+ // If flush failed or we don't support native fences, we need to force the
+ // gl command stream to be executed.
+ if (drawFence == nullptr || drawFence->get() < 0) {
+ bool success = finish();
+ if (!success) {
+ ALOGE("Failed to flush RenderEngine commands");
+ checkErrors();
+ // Chances are, something illegal happened (either the caller passed
+ // us bad parameters, or we messed up our shader generation).
+ return INVALID_OPERATION;
+ }
+ mLastDrawFence = nullptr;
+ } else {
+ // The caller takes ownership of drawFence, so we need to duplicate the
+ // fd here.
+ mLastDrawFence = new Fence(dup(drawFence->get()));
+ }
+ mPriorResourcesCleaned = false;
+
+ checkErrors();
+ return NO_ERROR;
+}
+
+void GLESRenderEngine::setViewportAndProjection(Rect viewport, Rect clip) {
+ ATRACE_CALL();
+ mVpWidth = viewport.getWidth();
+ mVpHeight = viewport.getHeight();
+
+ // We pass the top left corner instead of the bottom left corner,
+ // because since we're rendering off-screen first.
+ glViewport(viewport.left, viewport.top, mVpWidth, mVpHeight);
+
+ mState.projectionMatrix = mat4::ortho(clip.left, clip.right, clip.top, clip.bottom, 0, 1);
+}
+
+void GLESRenderEngine::setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
+ const half4& color, float cornerRadius) {
+ mState.isPremultipliedAlpha = premultipliedAlpha;
+ mState.isOpaque = opaque;
+ mState.color = color;
+ mState.cornerRadius = cornerRadius;
+
+ if (disableTexture) {
+ mState.textureEnabled = false;
+ }
+
+ if (color.a < 1.0f || !opaque || cornerRadius > 0.0f) {
+ glEnable(GL_BLEND);
+ glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ } else {
+ glDisable(GL_BLEND);
+ }
+}
+
+void GLESRenderEngine::setSourceY410BT2020(bool enable) {
+ mState.isY410BT2020 = enable;
+}
+
+void GLESRenderEngine::setSourceDataSpace(Dataspace source) {
+ mDataSpace = source;
+}
+
+void GLESRenderEngine::setOutputDataSpace(Dataspace dataspace) {
+ mOutputDataSpace = dataspace;
+}
+
+void GLESRenderEngine::setDisplayMaxLuminance(const float maxLuminance) {
+ mState.displayMaxLuminance = maxLuminance;
+}
+
+void GLESRenderEngine::setupLayerTexturing(const Texture& texture) {
+ GLuint target = texture.getTextureTarget();
+ glBindTexture(target, texture.getTextureName());
+ GLenum filter = GL_NEAREST;
+ if (texture.getFiltering()) {
+ filter = GL_LINEAR;
+ }
+ glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
+ glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
+
+ mState.texture = texture;
+ mState.textureEnabled = true;
+}
+
+void GLESRenderEngine::setColorTransform(const mat4& colorTransform) {
+ mState.colorMatrix = colorTransform;
+}
+
+void GLESRenderEngine::disableTexturing() {
+ mState.textureEnabled = false;
+}
+
+void GLESRenderEngine::disableBlending() {
+ glDisable(GL_BLEND);
+}
+
+void GLESRenderEngine::setupFillWithColor(float r, float g, float b, float a) {
+ mState.isPremultipliedAlpha = true;
+ mState.isOpaque = false;
+ mState.color = half4(r, g, b, a);
+ mState.textureEnabled = false;
+ glDisable(GL_BLEND);
+}
+
+void GLESRenderEngine::setupCornerRadiusCropSize(float width, float height) {
+ mState.cropSize = half2(width, height);
+}
+
+void GLESRenderEngine::drawMesh(const Mesh& mesh) {
+ ATRACE_CALL();
+ if (mesh.getTexCoordsSize()) {
+ glEnableVertexAttribArray(Program::texCoords);
+ glVertexAttribPointer(Program::texCoords, mesh.getTexCoordsSize(), GL_FLOAT, GL_FALSE,
+ mesh.getByteStride(), mesh.getTexCoords());
+ }
+
+ glVertexAttribPointer(Program::position, mesh.getVertexSize(), GL_FLOAT, GL_FALSE,
+ mesh.getByteStride(), mesh.getPositions());
+
+ if (mState.cornerRadius > 0.0f) {
+ glEnableVertexAttribArray(Program::cropCoords);
+ glVertexAttribPointer(Program::cropCoords, mesh.getVertexSize(), GL_FLOAT, GL_FALSE,
+ mesh.getByteStride(), mesh.getCropCoords());
+ }
+
+ if (mState.drawShadows) {
+ glEnableVertexAttribArray(Program::shadowColor);
+ glVertexAttribPointer(Program::shadowColor, mesh.getShadowColorSize(), GL_FLOAT, GL_FALSE,
+ mesh.getByteStride(), mesh.getShadowColor());
+
+ glEnableVertexAttribArray(Program::shadowParams);
+ glVertexAttribPointer(Program::shadowParams, mesh.getShadowParamsSize(), GL_FLOAT, GL_FALSE,
+ mesh.getByteStride(), mesh.getShadowParams());
+ }
+
+ Description managedState = mState;
+ // By default, DISPLAY_P3 is the only supported wide color output. However,
+ // when HDR content is present, hardware composer may be able to handle
+ // BT2020 data space, in that case, the output data space is set to be
+ // BT2020_HLG or BT2020_PQ respectively. In GPU fall back we need
+ // to respect this and convert non-HDR content to HDR format.
+ if (mUseColorManagement) {
+ Dataspace inputStandard = static_cast<Dataspace>(mDataSpace & Dataspace::STANDARD_MASK);
+ Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK);
+ Dataspace outputStandard =
+ static_cast<Dataspace>(mOutputDataSpace & Dataspace::STANDARD_MASK);
+ Dataspace outputTransfer =
+ static_cast<Dataspace>(mOutputDataSpace & Dataspace::TRANSFER_MASK);
+ bool needsXYZConversion = needsXYZTransformMatrix();
+
+ // NOTE: if the input standard of the input dataspace is not STANDARD_DCI_P3 or
+ // STANDARD_BT2020, it will be treated as STANDARD_BT709
+ if (inputStandard != Dataspace::STANDARD_DCI_P3 &&
+ inputStandard != Dataspace::STANDARD_BT2020) {
+ inputStandard = Dataspace::STANDARD_BT709;
+ }
+
+ if (needsXYZConversion) {
+ // The supported input color spaces are standard RGB, Display P3 and BT2020.
+ switch (inputStandard) {
+ case Dataspace::STANDARD_DCI_P3:
+ managedState.inputTransformMatrix = mDisplayP3ToXyz;
+ break;
+ case Dataspace::STANDARD_BT2020:
+ managedState.inputTransformMatrix = mBt2020ToXyz;
+ break;
+ default:
+ managedState.inputTransformMatrix = mSrgbToXyz;
+ break;
+ }
+
+ // The supported output color spaces are BT2020, Display P3 and standard RGB.
+ switch (outputStandard) {
+ case Dataspace::STANDARD_BT2020:
+ managedState.outputTransformMatrix = mXyzToBt2020;
+ break;
+ case Dataspace::STANDARD_DCI_P3:
+ managedState.outputTransformMatrix = mXyzToDisplayP3;
+ break;
+ default:
+ managedState.outputTransformMatrix = mXyzToSrgb;
+ break;
+ }
+ } else if (inputStandard != outputStandard) {
+ // At this point, the input data space and output data space could be both
+ // HDR data spaces, but they match each other, we do nothing in this case.
+ // In addition to the case above, the input data space could be
+ // - scRGB linear
+ // - scRGB non-linear
+ // - sRGB
+ // - Display P3
+ // - BT2020
+ // The output data spaces could be
+ // - sRGB
+ // - Display P3
+ // - BT2020
+ switch (outputStandard) {
+ case Dataspace::STANDARD_BT2020:
+ if (inputStandard == Dataspace::STANDARD_BT709) {
+ managedState.outputTransformMatrix = mSrgbToBt2020;
+ } else if (inputStandard == Dataspace::STANDARD_DCI_P3) {
+ managedState.outputTransformMatrix = mDisplayP3ToBt2020;
+ }
+ break;
+ case Dataspace::STANDARD_DCI_P3:
+ if (inputStandard == Dataspace::STANDARD_BT709) {
+ managedState.outputTransformMatrix = mSrgbToDisplayP3;
+ } else if (inputStandard == Dataspace::STANDARD_BT2020) {
+ managedState.outputTransformMatrix = mBt2020ToDisplayP3;
+ }
+ break;
+ default:
+ if (inputStandard == Dataspace::STANDARD_DCI_P3) {
+ managedState.outputTransformMatrix = mDisplayP3ToSrgb;
+ } else if (inputStandard == Dataspace::STANDARD_BT2020) {
+ managedState.outputTransformMatrix = mBt2020ToSrgb;
+ }
+ break;
+ }
+ }
+
+ // we need to convert the RGB value to linear space and convert it back when:
+ // - there is a color matrix that is not an identity matrix, or
+ // - there is an output transform matrix that is not an identity matrix, or
+ // - the input transfer function doesn't match the output transfer function.
+ if (managedState.hasColorMatrix() || managedState.hasOutputTransformMatrix() ||
+ inputTransfer != outputTransfer) {
+ managedState.inputTransferFunction =
+ Description::dataSpaceToTransferFunction(inputTransfer);
+ managedState.outputTransferFunction =
+ Description::dataSpaceToTransferFunction(outputTransfer);
+ }
+ }
+
+ ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
+ managedState);
+
+ if (mState.drawShadows) {
+ glDrawElements(mesh.getPrimitive(), mesh.getIndexCount(), GL_UNSIGNED_SHORT,
+ mesh.getIndices());
+ } else {
+ glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
+ }
+
+ if (mUseColorManagement && outputDebugPPMs) {
+ static uint64_t managedColorFrameCount = 0;
+ std::ostringstream out;
+ out << "/data/texture_out" << managedColorFrameCount++;
+ writePPM(out.str().c_str(), mVpWidth, mVpHeight);
+ }
+
+ if (mesh.getTexCoordsSize()) {
+ glDisableVertexAttribArray(Program::texCoords);
+ }
+
+ if (mState.cornerRadius > 0.0f) {
+ glDisableVertexAttribArray(Program::cropCoords);
+ }
+
+ if (mState.drawShadows) {
+ glDisableVertexAttribArray(Program::shadowColor);
+ glDisableVertexAttribArray(Program::shadowParams);
+ }
+}
+
+size_t GLESRenderEngine::getMaxTextureSize() const {
+ return mMaxTextureSize;
+}
+
+size_t GLESRenderEngine::getMaxViewportDims() const {
+ return mMaxViewportDims[0] < mMaxViewportDims[1] ? mMaxViewportDims[0] : mMaxViewportDims[1];
+}
+
+void GLESRenderEngine::dump(std::string& result) {
+ const GLExtensions& extensions = GLExtensions::getInstance();
+ ProgramCache& cache = ProgramCache::getInstance();
+
+ StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion());
+ StringAppendF(&result, "%s\n", extensions.getEGLExtensions());
+ StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(),
+ extensions.getVersion());
+ StringAppendF(&result, "%s\n", extensions.getExtensions());
+ StringAppendF(&result, "RenderEngine supports protected context: %d\n",
+ supportsProtectedContent());
+ StringAppendF(&result, "RenderEngine is in protected context: %d\n", mInProtectedContext);
+ StringAppendF(&result, "RenderEngine program cache size for unprotected context: %zu\n",
+ cache.getSize(mEGLContext));
+ StringAppendF(&result, "RenderEngine program cache size for protected context: %zu\n",
+ cache.getSize(mProtectedEGLContext));
+ StringAppendF(&result, "RenderEngine last dataspace conversion: (%s) to (%s)\n",
+ dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(),
+ dataspaceDetails(static_cast<android_dataspace>(mOutputDataSpace)).c_str());
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ StringAppendF(&result, "RenderEngine image cache size: %zu\n", mImageCache.size());
+ StringAppendF(&result, "Dumping buffer ids...\n");
+ for (const auto& [id, unused] : mImageCache) {
+ StringAppendF(&result, "0x%" PRIx64 "\n", id);
+ }
+ }
+ {
+ std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
+ StringAppendF(&result, "RenderEngine framebuffer image cache size: %zu\n",
+ mFramebufferImageCache.size());
+ StringAppendF(&result, "Dumping buffer ids...\n");
+ for (const auto& [id, unused] : mFramebufferImageCache) {
+ StringAppendF(&result, "0x%" PRIx64 "\n", id);
+ }
+ }
+}
+
+GLESRenderEngine::GlesVersion GLESRenderEngine::parseGlesVersion(const char* str) {
+ int major, minor;
+ if (sscanf(str, "OpenGL ES-CM %d.%d", &major, &minor) != 2) {
+ if (sscanf(str, "OpenGL ES %d.%d", &major, &minor) != 2) {
+ ALOGW("Unable to parse GL_VERSION string: \"%s\"", str);
+ return GLES_VERSION_1_0;
+ }
+ }
+
+ if (major == 1 && minor == 0) return GLES_VERSION_1_0;
+ if (major == 1 && minor >= 1) return GLES_VERSION_1_1;
+ if (major == 2 && minor >= 0) return GLES_VERSION_2_0;
+ if (major == 3 && minor >= 0) return GLES_VERSION_3_0;
+
+ ALOGW("Unrecognized OpenGL ES version: %d.%d", major, minor);
+ return GLES_VERSION_1_0;
+}
+
+EGLContext GLESRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
+ EGLContext shareContext, bool useContextPriority,
+ Protection protection) {
+ EGLint renderableType = 0;
+ if (config == EGL_NO_CONFIG) {
+ renderableType = EGL_OPENGL_ES3_BIT;
+ } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) {
+ LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE");
+ }
+ EGLint contextClientVersion = 0;
+ if (renderableType & EGL_OPENGL_ES3_BIT) {
+ contextClientVersion = 3;
+ } else if (renderableType & EGL_OPENGL_ES2_BIT) {
+ contextClientVersion = 2;
+ } else if (renderableType & EGL_OPENGL_ES_BIT) {
+ contextClientVersion = 1;
+ } else {
+ LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
+ }
+
+ std::vector<EGLint> contextAttributes;
+ contextAttributes.reserve(7);
+ contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
+ contextAttributes.push_back(contextClientVersion);
+ if (useContextPriority) {
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+ }
+ if (protection == Protection::PROTECTED) {
+ contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT);
+ contextAttributes.push_back(EGL_TRUE);
+ }
+ contextAttributes.push_back(EGL_NONE);
+
+ EGLContext context = eglCreateContext(display, config, shareContext, contextAttributes.data());
+
+ if (contextClientVersion == 3 && context == EGL_NO_CONTEXT) {
+ // eglGetConfigAttrib indicated we can create GLES 3 context, but we failed, thus
+ // EGL_NO_CONTEXT so that we can abort.
+ if (config != EGL_NO_CONFIG) {
+ return context;
+ }
+ // If |config| is EGL_NO_CONFIG, we speculatively try to create GLES 3 context, so we should
+ // try to fall back to GLES 2.
+ contextAttributes[1] = 2;
+ context = eglCreateContext(display, config, shareContext, contextAttributes.data());
+ }
+
+ return context;
+}
+
+EGLSurface GLESRenderEngine::createStubEglPbufferSurface(EGLDisplay display, EGLConfig config,
+ int hwcFormat, Protection protection) {
+ EGLConfig stubConfig = config;
+ if (stubConfig == EGL_NO_CONFIG) {
+ stubConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
+ }
+ std::vector<EGLint> attributes;
+ attributes.reserve(7);
+ attributes.push_back(EGL_WIDTH);
+ attributes.push_back(1);
+ attributes.push_back(EGL_HEIGHT);
+ attributes.push_back(1);
+ if (protection == Protection::PROTECTED) {
+ attributes.push_back(EGL_PROTECTED_CONTENT_EXT);
+ attributes.push_back(EGL_TRUE);
+ }
+ attributes.push_back(EGL_NONE);
+
+ return eglCreatePbufferSurface(display, stubConfig, attributes.data());
+}
+
+bool GLESRenderEngine::isHdrDataSpace(const Dataspace dataSpace) const {
+ const Dataspace standard = static_cast<Dataspace>(dataSpace & Dataspace::STANDARD_MASK);
+ const Dataspace transfer = static_cast<Dataspace>(dataSpace & Dataspace::TRANSFER_MASK);
+ return standard == Dataspace::STANDARD_BT2020 &&
+ (transfer == Dataspace::TRANSFER_ST2084 || transfer == Dataspace::TRANSFER_HLG);
+}
+
+// For convenience, we want to convert the input color space to XYZ color space first,
+// and then convert from XYZ color space to output color space when
+// - SDR and HDR contents are mixed, either SDR content will be converted to HDR or
+// HDR content will be tone-mapped to SDR; Or,
+// - there are HDR PQ and HLG contents presented at the same time, where we want to convert
+// HLG content to PQ content.
+// In either case above, we need to operate the Y value in XYZ color space. Thus, when either
+// input data space or output data space is HDR data space, and the input transfer function
+// doesn't match the output transfer function, we would enable an intermediate transfrom to
+// XYZ color space.
+bool GLESRenderEngine::needsXYZTransformMatrix() const {
+ const bool isInputHdrDataSpace = isHdrDataSpace(mDataSpace);
+ const bool isOutputHdrDataSpace = isHdrDataSpace(mOutputDataSpace);
+ const Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK);
+ const Dataspace outputTransfer =
+ static_cast<Dataspace>(mOutputDataSpace & Dataspace::TRANSFER_MASK);
+
+ return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer;
+}
+
+bool GLESRenderEngine::isImageCachedForTesting(uint64_t bufferId) {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ const auto& cachedImage = mImageCache.find(bufferId);
+ return cachedImage != mImageCache.end();
+}
+
+bool GLESRenderEngine::isTextureNameKnownForTesting(uint32_t texName) {
+ const auto& entry = mTextureView.find(texName);
+ return entry != mTextureView.end();
+}
+
+std::optional<uint64_t> GLESRenderEngine::getBufferIdForTextureNameForTesting(uint32_t texName) {
+ const auto& entry = mTextureView.find(texName);
+ return entry != mTextureView.end() ? entry->second : std::nullopt;
+}
+
+bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) {
+ std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
+ return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(),
+ [=](std::pair<uint64_t, EGLImageKHR> image) {
+ return image.first == bufferId;
+ });
+}
+
+// FlushTracer implementation
+GLESRenderEngine::FlushTracer::FlushTracer(GLESRenderEngine* engine) : mEngine(engine) {
+ mThread = std::thread(&GLESRenderEngine::FlushTracer::loop, this);
+}
+
+GLESRenderEngine::FlushTracer::~FlushTracer() {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mRunning = false;
+ }
+ mCondition.notify_all();
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+}
+
+void GLESRenderEngine::FlushTracer::queueSync(EGLSyncKHR sync) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ char name[64];
+ const uint64_t frameNum = mFramesQueued++;
+ snprintf(name, sizeof(name), "Queueing sync for frame: %lu",
+ static_cast<unsigned long>(frameNum));
+ ATRACE_NAME(name);
+ mQueue.push({sync, frameNum});
+ ATRACE_INT("GPU Frames Outstanding", mQueue.size());
+ mCondition.notify_one();
+}
+
+void GLESRenderEngine::FlushTracer::loop() {
+ while (mRunning) {
+ QueueEntry entry;
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ mCondition.wait(mMutex,
+ [&]() REQUIRES(mMutex) { return !mQueue.empty() || !mRunning; });
+
+ if (!mRunning) {
+ // if mRunning is false, then FlushTracer is being destroyed, so
+ // bail out now.
+ break;
+ }
+ entry = mQueue.front();
+ mQueue.pop();
+ }
+ {
+ char name[64];
+ snprintf(name, sizeof(name), "waiting for frame %lu",
+ static_cast<unsigned long>(entry.mFrameNum));
+ ATRACE_NAME(name);
+ mEngine->waitSync(entry.mSync, 0);
+ }
+ }
+}
+
+void GLESRenderEngine::handleShadow(const FloatRect& casterRect, float casterCornerRadius,
+ const ShadowSettings& settings) {
+ ATRACE_CALL();
+ const float casterZ = settings.length / 2.0f;
+ const GLShadowVertexGenerator shadows(casterRect, casterCornerRadius, casterZ,
+ settings.casterIsTranslucent, settings.ambientColor,
+ settings.spotColor, settings.lightPos,
+ settings.lightRadius);
+
+ // setup mesh for both shadows
+ Mesh mesh = Mesh::Builder()
+ .setPrimitive(Mesh::TRIANGLES)
+ .setVertices(shadows.getVertexCount(), 2 /* size */)
+ .setShadowAttrs()
+ .setIndices(shadows.getIndexCount())
+ .build();
+
+ Mesh::VertexArray<vec2> position = mesh.getPositionArray<vec2>();
+ Mesh::VertexArray<vec4> shadowColor = mesh.getShadowColorArray<vec4>();
+ Mesh::VertexArray<vec3> shadowParams = mesh.getShadowParamsArray<vec3>();
+ shadows.fillVertices(position, shadowColor, shadowParams);
+ shadows.fillIndices(mesh.getIndicesArray());
+
+ mState.cornerRadius = 0.0f;
+ mState.drawShadows = true;
+ setupLayerTexturing(mShadowTexture.getTexture());
+ drawMesh(mesh);
+ mState.drawShadows = false;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/gl/GLESRenderEngine.h b/media/libstagefright/renderfright/gl/GLESRenderEngine.h
new file mode 100644
index 0000000..2c6eae2
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/GLESRenderEngine.h
@@ -0,0 +1,296 @@
+/*
+ * 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.
+ */
+
+#ifndef SF_GLESRENDERENGINE_H_
+#define SF_GLESRENDERENGINE_H_
+
+#include <condition_variable>
+#include <deque>
+#include <mutex>
+#include <queue>
+#include <thread>
+#include <unordered_map>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <android-base/thread_annotations.h>
+#include <renderengine/RenderEngine.h>
+#include <renderengine/private/Description.h>
+#include <sys/types.h>
+#include "GLShadowTexture.h"
+#include "ImageManager.h"
+
+#define EGL_NO_CONFIG ((EGLConfig)0)
+
+namespace android {
+
+namespace renderengine {
+
+class Mesh;
+class Texture;
+
+namespace gl {
+
+class GLImage;
+class BlurFilter;
+
+class GLESRenderEngine : public impl::RenderEngine {
+public:
+ static std::unique_ptr<GLESRenderEngine> create(const RenderEngineCreationArgs& args);
+
+ GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLConfig config,
+ EGLContext ctxt, EGLSurface stub, EGLContext protectedContext,
+ EGLSurface protectedStub);
+ ~GLESRenderEngine() override EXCLUDES(mRenderingMutex);
+
+ void primeCache() const override;
+ void genTextures(size_t count, uint32_t* names) override;
+ void deleteTextures(size_t count, uint32_t const* names) override;
+ void bindExternalTextureImage(uint32_t texName, const Image& image) override;
+ status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& fence) EXCLUDES(mRenderingMutex);
+ void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
+ void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex);
+ status_t bindFrameBuffer(Framebuffer* framebuffer) override;
+ void unbindFrameBuffer(Framebuffer* framebuffer) override;
+
+ bool isProtected() const override { return mInProtectedContext; }
+ bool supportsProtectedContent() const override;
+ bool useProtectedContext(bool useProtectedContext) override;
+ status_t drawLayers(const DisplaySettings& display,
+ const std::vector<const LayerSettings*>& layers,
+ const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
+ base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
+ bool cleanupPostRender(CleanupMode mode) override;
+
+ EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
+ // Creates an output image for rendering to
+ EGLImageKHR createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected,
+ bool useFramebufferCache)
+ EXCLUDES(mFramebufferImageCacheMutex);
+
+ // Test-only methods
+ // Returns true iff mImageCache contains an image keyed by bufferId
+ bool isImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex);
+ // Returns true iff texName was previously generated by RenderEngine and was
+ // not destroyed.
+ bool isTextureNameKnownForTesting(uint32_t texName);
+ // Returns the buffer ID of the content bound to texName, or nullopt if no
+ // such mapping exists.
+ std::optional<uint64_t> getBufferIdForTextureNameForTesting(uint32_t texName);
+ // Returns true iff mFramebufferImageCache contains an image keyed by bufferId
+ bool isFramebufferImageCachedForTesting(uint64_t bufferId)
+ EXCLUDES(mFramebufferImageCacheMutex);
+ // These are wrappers around public methods above, but exposing Barrier
+ // objects so that tests can block.
+ std::shared_ptr<ImageManager::Barrier> cacheExternalTextureBufferForTesting(
+ const sp<GraphicBuffer>& buffer);
+ std::shared_ptr<ImageManager::Barrier> unbindExternalTextureBufferForTesting(uint64_t bufferId);
+
+protected:
+ Framebuffer* getFramebufferForDrawing() override;
+ void dump(std::string& result) override EXCLUDES(mRenderingMutex)
+ EXCLUDES(mFramebufferImageCacheMutex);
+ size_t getMaxTextureSize() const override;
+ size_t getMaxViewportDims() const override;
+
+private:
+ enum GlesVersion {
+ GLES_VERSION_1_0 = 0x10000,
+ GLES_VERSION_1_1 = 0x10001,
+ GLES_VERSION_2_0 = 0x20000,
+ GLES_VERSION_3_0 = 0x30000,
+ };
+
+ static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
+ static GlesVersion parseGlesVersion(const char* str);
+ static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
+ EGLContext shareContext, bool useContextPriority,
+ Protection protection);
+ static EGLSurface createStubEglPbufferSurface(EGLDisplay display, EGLConfig config,
+ int hwcFormat, Protection protection);
+ std::unique_ptr<Framebuffer> createFramebuffer();
+ std::unique_ptr<Image> createImage();
+ void checkErrors() const;
+ void checkErrors(const char* tag) const;
+ void setScissor(const Rect& region);
+ void disableScissor();
+ bool waitSync(EGLSyncKHR sync, EGLint flags);
+ status_t cacheExternalTextureBufferInternal(const sp<GraphicBuffer>& buffer)
+ EXCLUDES(mRenderingMutex);
+ void unbindExternalTextureBufferInternal(uint64_t bufferId) EXCLUDES(mRenderingMutex);
+
+ // A data space is considered HDR data space if it has BT2020 color space
+ // with PQ or HLG transfer function.
+ bool isHdrDataSpace(const ui::Dataspace dataSpace) const;
+ bool needsXYZTransformMatrix() const;
+ // Defines the viewport, and sets the projection matrix to the projection
+ // defined by the clip.
+ void setViewportAndProjection(Rect viewport, Rect clip);
+ // Evicts stale images from the buffer cache.
+ void evictImages(const std::vector<LayerSettings>& layers);
+ // Computes the cropping window for the layer and sets up cropping
+ // coordinates for the mesh.
+ FloatRect setupLayerCropping(const LayerSettings& layer, Mesh& mesh);
+
+ // We do a special handling for rounded corners when it's possible to turn off blending
+ // for the majority of the layer. The rounded corners needs to turn on blending such that
+ // we can set the alpha value correctly, however, only the corners need this, and since
+ // blending is an expensive operation, we want to turn off blending when it's not necessary.
+ void handleRoundedCorners(const DisplaySettings& display, const LayerSettings& layer,
+ const Mesh& mesh);
+ base::unique_fd flush();
+ bool finish();
+ bool waitFence(base::unique_fd fenceFd);
+ void clearWithColor(float red, float green, float blue, float alpha);
+ void fillRegionWithColor(const Region& region, float red, float green, float blue, float alpha);
+ void handleShadow(const FloatRect& casterRect, float casterCornerRadius,
+ const ShadowSettings& shadowSettings);
+ void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
+ const half4& color, float cornerRadius);
+ void setupLayerTexturing(const Texture& texture);
+ void setupFillWithColor(float r, float g, float b, float a);
+ void setColorTransform(const mat4& colorTransform);
+ void disableTexturing();
+ void disableBlending();
+ void setupCornerRadiusCropSize(float width, float height);
+
+ // HDR and color management related functions and state
+ void setSourceY410BT2020(bool enable);
+ void setSourceDataSpace(ui::Dataspace source);
+ void setOutputDataSpace(ui::Dataspace dataspace);
+ void setDisplayMaxLuminance(const float maxLuminance);
+
+ // drawing
+ void drawMesh(const Mesh& mesh);
+
+ EGLDisplay mEGLDisplay;
+ EGLConfig mEGLConfig;
+ EGLContext mEGLContext;
+ EGLSurface mStubSurface;
+ EGLContext mProtectedEGLContext;
+ EGLSurface mProtectedStubSurface;
+ GLint mMaxViewportDims[2];
+ GLint mMaxTextureSize;
+ GLuint mVpWidth;
+ GLuint mVpHeight;
+ Description mState;
+ GLShadowTexture mShadowTexture;
+
+ mat4 mSrgbToXyz;
+ mat4 mDisplayP3ToXyz;
+ mat4 mBt2020ToXyz;
+ mat4 mXyzToSrgb;
+ mat4 mXyzToDisplayP3;
+ mat4 mXyzToBt2020;
+ mat4 mSrgbToDisplayP3;
+ mat4 mSrgbToBt2020;
+ mat4 mDisplayP3ToSrgb;
+ mat4 mDisplayP3ToBt2020;
+ mat4 mBt2020ToSrgb;
+ mat4 mBt2020ToDisplayP3;
+
+ bool mInProtectedContext = false;
+ // If set to true, then enables tracing flush() and finish() to systrace.
+ bool mTraceGpuCompletion = false;
+ // Maximum size of mFramebufferImageCache. If more images would be cached, then (approximately)
+ // the last recently used buffer should be kicked out.
+ uint32_t mFramebufferImageCacheSize = 0;
+
+ // Cache of output images, keyed by corresponding GraphicBuffer ID.
+ std::deque<std::pair<uint64_t, EGLImageKHR>> mFramebufferImageCache
+ GUARDED_BY(mFramebufferImageCacheMutex);
+ // The only reason why we have this mutex is so that we don't segfault when
+ // dumping info.
+ std::mutex mFramebufferImageCacheMutex;
+
+ // Current dataspace of layer being rendered
+ ui::Dataspace mDataSpace = ui::Dataspace::UNKNOWN;
+
+ // Current output dataspace of the render engine
+ ui::Dataspace mOutputDataSpace = ui::Dataspace::UNKNOWN;
+
+ // Whether device supports color management, currently color management
+ // supports sRGB, DisplayP3 color spaces.
+ const bool mUseColorManagement = false;
+
+ // Cache of GL images that we'll store per GraphicBuffer ID
+ std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache GUARDED_BY(mRenderingMutex);
+ std::unordered_map<uint32_t, std::optional<uint64_t>> mTextureView;
+
+ // Mutex guarding rendering operations, so that:
+ // 1. GL operations aren't interleaved, and
+ // 2. Internal state related to rendering that is potentially modified by
+ // multiple threads is guaranteed thread-safe.
+ std::mutex mRenderingMutex;
+
+ std::unique_ptr<Framebuffer> mDrawingBuffer;
+ // this is a 1x1 RGB buffer, but over-allocate in case a driver wants more
+ // memory or if it needs to satisfy alignment requirements. In this case:
+ // assume that each channel requires 4 bytes, and add 3 additional bytes to
+ // ensure that we align on a word. Allocating 16 bytes will provide a
+ // guarantee that we don't clobber memory.
+ uint32_t mPlaceholderDrawBuffer[4];
+ // Placeholder buffer and image, similar to mPlaceholderDrawBuffer, but
+ // instead these are intended for cleaning up texture memory with the
+ // GL_TEXTURE_EXTERNAL_OES target.
+ ANativeWindowBuffer* mPlaceholderBuffer = nullptr;
+ EGLImage mPlaceholderImage = EGL_NO_IMAGE_KHR;
+ sp<Fence> mLastDrawFence;
+ // Store a separate boolean checking if prior resources were cleaned up, as
+ // devices that don't support native sync fences can't rely on a last draw
+ // fence that doesn't exist.
+ bool mPriorResourcesCleaned = true;
+
+ // Blur effect processor, only instantiated when a layer requests it.
+ BlurFilter* mBlurFilter = nullptr;
+
+ class FlushTracer {
+ public:
+ FlushTracer(GLESRenderEngine* engine);
+ ~FlushTracer();
+ void queueSync(EGLSyncKHR sync) EXCLUDES(mMutex);
+
+ struct QueueEntry {
+ EGLSyncKHR mSync = nullptr;
+ uint64_t mFrameNum = 0;
+ };
+
+ private:
+ void loop();
+ GLESRenderEngine* const mEngine;
+ std::thread mThread;
+ std::condition_variable_any mCondition;
+ std::mutex mMutex;
+ std::queue<QueueEntry> mQueue GUARDED_BY(mMutex);
+ uint64_t mFramesQueued GUARDED_BY(mMutex) = 0;
+ bool mRunning = true;
+ };
+ friend class FlushTracer;
+ friend class ImageManager;
+ friend class GLFramebuffer;
+ friend class BlurFilter;
+ friend class GenericProgram;
+ std::unique_ptr<FlushTracer> mFlushTracer;
+ std::unique_ptr<ImageManager> mImageManager = std::make_unique<ImageManager>(this);
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
+
+#endif /* SF_GLESRENDERENGINE_H_ */
diff --git a/media/libstagefright/renderfright/gl/GLExtensions.cpp b/media/libstagefright/renderfright/gl/GLExtensions.cpp
new file mode 100644
index 0000000..2924b0e
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/GLExtensions.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "GLExtensions.h"
+
+#include <string>
+#include <unordered_set>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::GLExtensions)
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+namespace {
+
+class ExtensionSet {
+public:
+ ExtensionSet(const char* extensions) {
+ char const* curr = extensions;
+ char const* head = curr;
+ do {
+ head = strchr(curr, ' ');
+ size_t len = head ? head - curr : strlen(curr);
+ if (len > 0) {
+ mExtensions.emplace(curr, len);
+ }
+ curr = head + 1;
+ } while (head);
+ }
+
+ bool hasExtension(const char* extension) const { return mExtensions.count(extension) > 0; }
+
+private:
+ std::unordered_set<std::string> mExtensions;
+};
+
+} // anonymous namespace
+
+void GLExtensions::initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer,
+ GLubyte const* version, GLubyte const* extensions) {
+ mVendor = (char const*)vendor;
+ mRenderer = (char const*)renderer;
+ mVersion = (char const*)version;
+ mExtensions = (char const*)extensions;
+
+ ExtensionSet extensionSet(mExtensions.c_str());
+ if (extensionSet.hasExtension("GL_EXT_protected_textures")) {
+ mHasProtectedTexture = true;
+ }
+}
+
+char const* GLExtensions::getVendor() const {
+ return mVendor.string();
+}
+
+char const* GLExtensions::getRenderer() const {
+ return mRenderer.string();
+}
+
+char const* GLExtensions::getVersion() const {
+ return mVersion.string();
+}
+
+char const* GLExtensions::getExtensions() const {
+ return mExtensions.string();
+}
+
+void GLExtensions::initWithEGLStrings(char const* eglVersion, char const* eglExtensions) {
+ mEGLVersion = eglVersion;
+ mEGLExtensions = eglExtensions;
+
+ ExtensionSet extensionSet(eglExtensions);
+
+ // EGL_ANDROIDX_no_config_context is an experimental extension with no
+ // written specification. It will be replaced by something more formal.
+ // SurfaceFlinger is using it to allow a single EGLContext to render to
+ // both a 16-bit primary display framebuffer and a 32-bit virtual display
+ // framebuffer.
+ //
+ // EGL_KHR_no_config_context is official extension to allow creating a
+ // context that works with any surface of a display.
+ if (extensionSet.hasExtension("EGL_ANDROIDX_no_config_context") ||
+ extensionSet.hasExtension("EGL_KHR_no_config_context")) {
+ mHasNoConfigContext = true;
+ }
+
+ if (extensionSet.hasExtension("EGL_ANDROID_native_fence_sync")) {
+ mHasNativeFenceSync = true;
+ }
+ if (extensionSet.hasExtension("EGL_KHR_fence_sync")) {
+ mHasFenceSync = true;
+ }
+ if (extensionSet.hasExtension("EGL_KHR_wait_sync")) {
+ mHasWaitSync = true;
+ }
+ if (extensionSet.hasExtension("EGL_EXT_protected_content")) {
+ mHasProtectedContent = true;
+ }
+ if (extensionSet.hasExtension("EGL_IMG_context_priority")) {
+ mHasContextPriority = true;
+ }
+ if (extensionSet.hasExtension("EGL_KHR_surfaceless_context")) {
+ mHasSurfacelessContext = true;
+ }
+}
+
+char const* GLExtensions::getEGLVersion() const {
+ return mEGLVersion.string();
+}
+
+char const* GLExtensions::getEGLExtensions() const {
+ return mEGLExtensions.string();
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/gl/GLExtensions.h b/media/libstagefright/renderfright/gl/GLExtensions.h
new file mode 100644
index 0000000..ef00009
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/GLExtensions.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_SF_GLEXTENSION_H
+#define ANDROID_SF_GLEXTENSION_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <utils/Singleton.h>
+#include <utils/String8.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLExtensions : public Singleton<GLExtensions> {
+public:
+ bool hasNoConfigContext() const { return mHasNoConfigContext; }
+ bool hasNativeFenceSync() const { return mHasNativeFenceSync; }
+ bool hasFenceSync() const { return mHasFenceSync; }
+ bool hasWaitSync() const { return mHasWaitSync; }
+ bool hasProtectedContent() const { return mHasProtectedContent; }
+ bool hasContextPriority() const { return mHasContextPriority; }
+ bool hasSurfacelessContext() const { return mHasSurfacelessContext; }
+ bool hasProtectedTexture() const { return mHasProtectedTexture; }
+
+ void initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version,
+ GLubyte const* extensions);
+ char const* getVendor() const;
+ char const* getRenderer() const;
+ char const* getVersion() const;
+ char const* getExtensions() const;
+
+ void initWithEGLStrings(char const* eglVersion, char const* eglExtensions);
+ char const* getEGLVersion() const;
+ char const* getEGLExtensions() const;
+
+protected:
+ GLExtensions() = default;
+
+private:
+ friend class Singleton<GLExtensions>;
+
+ bool mHasNoConfigContext = false;
+ bool mHasNativeFenceSync = false;
+ bool mHasFenceSync = false;
+ bool mHasWaitSync = false;
+ bool mHasProtectedContent = false;
+ bool mHasContextPriority = false;
+ bool mHasSurfacelessContext = false;
+ bool mHasProtectedTexture = false;
+
+ String8 mVendor;
+ String8 mRenderer;
+ String8 mVersion;
+ String8 mExtensions;
+ String8 mEGLVersion;
+ String8 mEGLExtensions;
+
+ GLExtensions(const GLExtensions&);
+ GLExtensions& operator=(const GLExtensions&);
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
+
+#endif // ANDROID_SF_GLEXTENSION_H
diff --git a/media/libstagefright/renderfright/gl/GLFramebuffer.cpp b/media/libstagefright/renderfright/gl/GLFramebuffer.cpp
new file mode 100644
index 0000000..d6582b3
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/GLFramebuffer.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "GLFramebuffer.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+#include <nativebase/nativebase.h>
+#include <utils/Trace.h>
+#include "GLESRenderEngine.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine)
+ : mEngine(engine), mEGLDisplay(engine.getEGLDisplay()), mEGLImage(EGL_NO_IMAGE_KHR) {
+ glGenTextures(1, &mTextureName);
+ glGenFramebuffers(1, &mFramebufferName);
+}
+
+GLFramebuffer::~GLFramebuffer() {
+ glDeleteFramebuffers(1, &mFramebufferName);
+ glDeleteTextures(1, &mTextureName);
+}
+
+bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
+ const bool useFramebufferCache) {
+ ATRACE_CALL();
+ if (mEGLImage != EGL_NO_IMAGE_KHR) {
+ if (!usingFramebufferCache) {
+ eglDestroyImageKHR(mEGLDisplay, mEGLImage);
+ }
+ mEGLImage = EGL_NO_IMAGE_KHR;
+ mBufferWidth = 0;
+ mBufferHeight = 0;
+ }
+
+ if (nativeBuffer) {
+ mEGLImage = mEngine.createFramebufferImageIfNeeded(nativeBuffer, isProtected,
+ useFramebufferCache);
+ if (mEGLImage == EGL_NO_IMAGE_KHR) {
+ return false;
+ }
+ usingFramebufferCache = useFramebufferCache;
+ mBufferWidth = nativeBuffer->width;
+ mBufferHeight = nativeBuffer->height;
+ }
+ return true;
+}
+
+void GLFramebuffer::allocateBuffers(uint32_t width, uint32_t height, void* data) {
+ ATRACE_CALL();
+
+ glBindTexture(GL_TEXTURE_2D, mTextureName);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
+
+ mBufferHeight = height;
+ mBufferWidth = width;
+ mEngine.checkErrors("Allocating Fbo texture");
+
+ bind();
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextureName, 0);
+ mStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ unbind();
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ if (mStatus != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Frame buffer is not complete. Error %d", mStatus);
+ }
+}
+
+void GLFramebuffer::bind() const {
+ glBindFramebuffer(GL_FRAMEBUFFER, mFramebufferName);
+}
+
+void GLFramebuffer::bindAsReadBuffer() const {
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, mFramebufferName);
+}
+
+void GLFramebuffer::bindAsDrawBuffer() const {
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebufferName);
+}
+
+void GLFramebuffer::unbind() const {
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/gl/GLFramebuffer.h b/media/libstagefright/renderfright/gl/GLFramebuffer.h
new file mode 100644
index 0000000..6757695
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/GLFramebuffer.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <renderengine/Framebuffer.h>
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLESRenderEngine;
+
+class GLFramebuffer : public renderengine::Framebuffer {
+public:
+ explicit GLFramebuffer(GLESRenderEngine& engine);
+ explicit GLFramebuffer(GLESRenderEngine& engine, bool multiTarget);
+ ~GLFramebuffer() override;
+
+ bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
+ const bool useFramebufferCache) override;
+ void allocateBuffers(uint32_t width, uint32_t height, void* data = nullptr);
+ EGLImageKHR getEGLImage() const { return mEGLImage; }
+ uint32_t getTextureName() const { return mTextureName; }
+ uint32_t getFramebufferName() const { return mFramebufferName; }
+ int32_t getBufferHeight() const { return mBufferHeight; }
+ int32_t getBufferWidth() const { return mBufferWidth; }
+ GLenum getStatus() const { return mStatus; }
+ void bind() const;
+ void bindAsReadBuffer() const;
+ void bindAsDrawBuffer() const;
+ void unbind() const;
+
+private:
+ GLESRenderEngine& mEngine;
+ EGLDisplay mEGLDisplay;
+ EGLImageKHR mEGLImage;
+ bool usingFramebufferCache = false;
+ GLenum mStatus = GL_FRAMEBUFFER_UNSUPPORTED;
+ uint32_t mTextureName, mFramebufferName;
+
+ int32_t mBufferHeight = 0;
+ int32_t mBufferWidth = 0;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/gl/GLImage.cpp b/media/libstagefright/renderfright/gl/GLImage.cpp
new file mode 100644
index 0000000..77e648e
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/GLImage.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "GLImage.h"
+
+#include <vector>
+
+#include <log/log.h>
+#include <utils/Trace.h>
+#include "GLESRenderEngine.h"
+#include "GLExtensions.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+static std::vector<EGLint> buildAttributeList(bool isProtected) {
+ std::vector<EGLint> attrs;
+ attrs.reserve(16);
+
+ attrs.push_back(EGL_IMAGE_PRESERVED_KHR);
+ attrs.push_back(EGL_TRUE);
+
+ if (isProtected && GLExtensions::getInstance().hasProtectedContent()) {
+ attrs.push_back(EGL_PROTECTED_CONTENT_EXT);
+ attrs.push_back(EGL_TRUE);
+ }
+
+ attrs.push_back(EGL_NONE);
+
+ return attrs;
+}
+
+GLImage::GLImage(const GLESRenderEngine& engine) : mEGLDisplay(engine.getEGLDisplay()) {}
+
+GLImage::~GLImage() {
+ setNativeWindowBuffer(nullptr, false);
+}
+
+bool GLImage::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) {
+ ATRACE_CALL();
+ if (mEGLImage != EGL_NO_IMAGE_KHR) {
+ if (!eglDestroyImageKHR(mEGLDisplay, mEGLImage)) {
+ ALOGE("failed to destroy image: %#x", eglGetError());
+ }
+ mEGLImage = EGL_NO_IMAGE_KHR;
+ }
+
+ if (buffer) {
+ std::vector<EGLint> attrs = buildAttributeList(isProtected);
+ mEGLImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+ static_cast<EGLClientBuffer>(buffer), attrs.data());
+ if (mEGLImage == EGL_NO_IMAGE_KHR) {
+ ALOGE("failed to create EGLImage: %#x", eglGetError());
+ return false;
+ }
+ mProtected = isProtected;
+ }
+
+ return true;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/gl/GLImage.h b/media/libstagefright/renderfright/gl/GLImage.h
new file mode 100644
index 0000000..59d6ce3
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/GLImage.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <android-base/macros.h>
+#include <renderengine/Image.h>
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLESRenderEngine;
+
+class GLImage : public renderengine::Image {
+public:
+ explicit GLImage(const GLESRenderEngine& engine);
+ ~GLImage() override;
+
+ bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) override;
+
+ EGLImageKHR getEGLImage() const { return mEGLImage; }
+ bool isProtected() const { return mProtected; }
+
+private:
+ EGLDisplay mEGLDisplay;
+ EGLImageKHR mEGLImage = EGL_NO_IMAGE_KHR;
+ bool mProtected = false;
+
+ DISALLOW_COPY_AND_ASSIGN(GLImage);
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/gl/GLShadowTexture.cpp b/media/libstagefright/renderfright/gl/GLShadowTexture.cpp
new file mode 100644
index 0000000..2423a34
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/GLShadowTexture.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+
+#include "GLShadowTexture.h"
+#include "GLSkiaShadowPort.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GLShadowTexture::GLShadowTexture() {
+ fillShadowTextureData(mTextureData, SHADOW_TEXTURE_WIDTH);
+
+ glGenTextures(1, &mName);
+ glBindTexture(GL_TEXTURE_2D, mName);
+ glTexImage2D(GL_TEXTURE_2D, 0 /* base image level */, GL_ALPHA, SHADOW_TEXTURE_WIDTH,
+ SHADOW_TEXTURE_HEIGHT, 0 /* border */, GL_ALPHA, GL_UNSIGNED_BYTE, mTextureData);
+ mTexture.init(Texture::TEXTURE_2D, mName);
+ mTexture.setFiltering(true);
+ mTexture.setDimensions(SHADOW_TEXTURE_WIDTH, 1);
+}
+
+GLShadowTexture::~GLShadowTexture() {
+ glDeleteTextures(1, &mName);
+}
+
+const Texture& GLShadowTexture::getTexture() {
+ return mTexture;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/gl/GLShadowTexture.h b/media/libstagefright/renderfright/gl/GLShadowTexture.h
new file mode 100644
index 0000000..250a9d7
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/GLShadowTexture.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 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 <renderengine/Texture.h>
+#include <cstdint>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLShadowTexture {
+public:
+ GLShadowTexture();
+ ~GLShadowTexture();
+
+ const Texture& getTexture();
+
+private:
+ static constexpr int SHADOW_TEXTURE_WIDTH = 128;
+ static constexpr int SHADOW_TEXTURE_HEIGHT = 1;
+
+ GLuint mName;
+ Texture mTexture;
+ uint8_t mTextureData[SHADOW_TEXTURE_WIDTH];
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/gl/GLShadowVertexGenerator.cpp b/media/libstagefright/renderfright/gl/GLShadowVertexGenerator.cpp
new file mode 100644
index 0000000..3181f9b
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/GLShadowVertexGenerator.cpp
@@ -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.
+ */
+
+#include <renderengine/Mesh.h>
+
+#include <math/vec4.h>
+
+#include <ui/Rect.h>
+#include <ui/Transform.h>
+
+#include "GLShadowVertexGenerator.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GLShadowVertexGenerator::GLShadowVertexGenerator(const FloatRect& casterRect,
+ float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent, const vec4& ambientColor,
+ const vec4& spotColor, const vec3& lightPosition,
+ float lightRadius) {
+ mDrawAmbientShadow = ambientColor.a > 0.f;
+ mDrawSpotShadow = spotColor.a > 0.f;
+
+ // Generate geometries and find number of vertices to generate
+ if (mDrawAmbientShadow) {
+ mAmbientShadowGeometry = getAmbientShadowGeometry(casterRect, casterCornerRadius, casterZ,
+ casterIsTranslucent, ambientColor);
+ mAmbientShadowVertexCount = getVertexCountForGeometry(*mAmbientShadowGeometry.get());
+ mAmbientShadowIndexCount = getIndexCountForGeometry(*mAmbientShadowGeometry.get());
+ } else {
+ mAmbientShadowVertexCount = 0;
+ mAmbientShadowIndexCount = 0;
+ }
+
+ if (mDrawSpotShadow) {
+ mSpotShadowGeometry =
+ getSpotShadowGeometry(casterRect, casterCornerRadius, casterZ, casterIsTranslucent,
+ spotColor, lightPosition, lightRadius);
+ mSpotShadowVertexCount = getVertexCountForGeometry(*mSpotShadowGeometry.get());
+ mSpotShadowIndexCount = getIndexCountForGeometry(*mSpotShadowGeometry.get());
+ } else {
+ mSpotShadowVertexCount = 0;
+ mSpotShadowIndexCount = 0;
+ }
+}
+
+size_t GLShadowVertexGenerator::getVertexCount() const {
+ return mAmbientShadowVertexCount + mSpotShadowVertexCount;
+}
+
+size_t GLShadowVertexGenerator::getIndexCount() const {
+ return mAmbientShadowIndexCount + mSpotShadowIndexCount;
+}
+
+void GLShadowVertexGenerator::fillVertices(Mesh::VertexArray<vec2>& position,
+ Mesh::VertexArray<vec4>& color,
+ Mesh::VertexArray<vec3>& params) const {
+ if (mDrawAmbientShadow) {
+ fillVerticesForGeometry(*mAmbientShadowGeometry.get(), mAmbientShadowVertexCount, position,
+ color, params);
+ }
+ if (mDrawSpotShadow) {
+ fillVerticesForGeometry(*mSpotShadowGeometry.get(), mSpotShadowVertexCount,
+ Mesh::VertexArray<vec2>(position, mAmbientShadowVertexCount),
+ Mesh::VertexArray<vec4>(color, mAmbientShadowVertexCount),
+ Mesh::VertexArray<vec3>(params, mAmbientShadowVertexCount));
+ }
+}
+
+void GLShadowVertexGenerator::fillIndices(uint16_t* indices) const {
+ if (mDrawAmbientShadow) {
+ fillIndicesForGeometry(*mAmbientShadowGeometry.get(), mAmbientShadowIndexCount,
+ 0 /* starting vertex offset */, indices);
+ }
+ if (mDrawSpotShadow) {
+ fillIndicesForGeometry(*mSpotShadowGeometry.get(), mSpotShadowIndexCount,
+ mAmbientShadowVertexCount /* starting vertex offset */,
+ &(indices[mAmbientShadowIndexCount]));
+ }
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/gl/GLShadowVertexGenerator.h b/media/libstagefright/renderfright/gl/GLShadowVertexGenerator.h
new file mode 100644
index 0000000..112f976
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/GLShadowVertexGenerator.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <math/vec4.h>
+#include <ui/Rect.h>
+
+#include "GLSkiaShadowPort.h"
+
+namespace android {
+namespace renderengine {
+
+class Mesh;
+
+namespace gl {
+
+/**
+ * Generates gl attributes required to draw shadow spot and/or ambient shadows.
+ *
+ * Each shadow can support different colors. This class generates three vertex attributes for
+ * each shadow, its position, color and shadow params(offset and distance). These can be sent
+ * using a single glDrawElements call.
+ */
+class GLShadowVertexGenerator {
+public:
+ GLShadowVertexGenerator(const FloatRect& casterRect, float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent, const vec4& ambientColor,
+ const vec4& spotColor, const vec3& lightPosition, float lightRadius);
+ ~GLShadowVertexGenerator() = default;
+
+ size_t getVertexCount() const;
+ size_t getIndexCount() const;
+ void fillVertices(Mesh::VertexArray<vec2>& position, Mesh::VertexArray<vec4>& color,
+ Mesh::VertexArray<vec3>& params) const;
+ void fillIndices(uint16_t* indices) const;
+
+private:
+ bool mDrawAmbientShadow;
+ std::unique_ptr<Geometry> mAmbientShadowGeometry;
+ int mAmbientShadowVertexCount = 0;
+ int mAmbientShadowIndexCount = 0;
+
+ bool mDrawSpotShadow;
+ std::unique_ptr<Geometry> mSpotShadowGeometry;
+ int mSpotShadowVertexCount = 0;
+ int mSpotShadowIndexCount = 0;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/gl/GLSkiaShadowPort.cpp b/media/libstagefright/renderfright/gl/GLSkiaShadowPort.cpp
new file mode 100644
index 0000000..da8b435
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/GLSkiaShadowPort.cpp
@@ -0,0 +1,656 @@
+/*
+ * 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 <math/vec4.h>
+
+#include <renderengine/Mesh.h>
+
+#include <ui/Rect.h>
+#include <ui/Transform.h>
+
+#include <utils/Log.h>
+
+#include "GLSkiaShadowPort.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+/**
+ * The shadow geometry logic and vertex generation code has been ported from skia shadow
+ * fast path OpenGL implementation to draw shadows around rects and rounded rects including
+ * circles.
+ *
+ * path: skia/src/gpu/GrRenderTargetContext.cpp GrRenderTargetContext::drawFastShadow
+ *
+ * Modifications made:
+ * - Switched to using std lib math functions
+ * - Fall off function is implemented in vertex shader rather than a shadow texture
+ * - Removed transformations applied on the caster rect since the caster will be in local
+ * coordinate space and will be transformed by the vertex shader.
+ */
+
+static inline float divide_and_pin(float numer, float denom, float min, float max) {
+ if (denom == 0.0f) return min;
+ return std::clamp(numer / denom, min, max);
+}
+
+static constexpr auto SK_ScalarSqrt2 = 1.41421356f;
+static constexpr auto kAmbientHeightFactor = 1.0f / 128.0f;
+static constexpr auto kAmbientGeomFactor = 64.0f;
+// Assuming that we have a light height of 600 for the spot shadow,
+// the spot values will reach their maximum at a height of approximately 292.3077.
+// We'll round up to 300 to keep it simple.
+static constexpr auto kMaxAmbientRadius = 300 * kAmbientHeightFactor * kAmbientGeomFactor;
+
+inline float AmbientBlurRadius(float height) {
+ return std::min(height * kAmbientHeightFactor * kAmbientGeomFactor, kMaxAmbientRadius);
+}
+inline float AmbientRecipAlpha(float height) {
+ return 1.0f + std::max(height * kAmbientHeightFactor, 0.0f);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Circle Data
+//
+// We have two possible cases for geometry for a circle:
+
+// In the case of a normal fill, we draw geometry for the circle as an octagon.
+static const uint16_t gFillCircleIndices[] = {
+ // enter the octagon
+ // clang-format off
+ 0, 1, 8, 1, 2, 8,
+ 2, 3, 8, 3, 4, 8,
+ 4, 5, 8, 5, 6, 8,
+ 6, 7, 8, 7, 0, 8,
+ // clang-format on
+};
+
+// For stroked circles, we use two nested octagons.
+static const uint16_t gStrokeCircleIndices[] = {
+ // enter the octagon
+ // clang-format off
+ 0, 1, 9, 0, 9, 8,
+ 1, 2, 10, 1, 10, 9,
+ 2, 3, 11, 2, 11, 10,
+ 3, 4, 12, 3, 12, 11,
+ 4, 5, 13, 4, 13, 12,
+ 5, 6, 14, 5, 14, 13,
+ 6, 7, 15, 6, 15, 14,
+ 7, 0, 8, 7, 8, 15,
+ // clang-format on
+};
+
+#define SK_ARRAY_COUNT(a) (sizeof(a) / sizeof((a)[0]))
+static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
+static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
+static const int kVertsPerStrokeCircle = 16;
+static const int kVertsPerFillCircle = 9;
+
+static int circle_type_to_vert_count(bool stroked) {
+ return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
+}
+
+static int circle_type_to_index_count(bool stroked) {
+ return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
+}
+
+static const uint16_t* circle_type_to_indices(bool stroked) {
+ return stroked ? gStrokeCircleIndices : gFillCircleIndices;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// RoundRect Data
+//
+// The geometry for a shadow roundrect is similar to a 9-patch:
+// ____________
+// |_|________|_|
+// | | | |
+// | | | |
+// | | | |
+// |_|________|_|
+// |_|________|_|
+//
+// However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram
+// shows the upper part of the upper left corner. The bottom triangle would similarly be split
+// into two triangles.)
+// ________
+// |\ \ |
+// | \ \ |
+// | \\ |
+// | \|
+// --------
+//
+// The center of the fan handles the curve of the corner. For roundrects where the stroke width
+// is greater than the corner radius, the outer triangles blend from the curve to the straight
+// sides. Otherwise these triangles will be degenerate.
+//
+// In the case where the stroke width is greater than the corner radius and the
+// blur radius (overstroke), we add additional geometry to mark out the rectangle in the center.
+// This rectangle extends the coverage values of the center edges of the 9-patch.
+// ____________
+// |_|________|_|
+// | |\ ____ /| |
+// | | | | | |
+// | | |____| | |
+// |_|/______\|_|
+// |_|________|_|
+//
+// For filled rrects we reuse the stroke geometry but add an additional quad to the center.
+
+static const uint16_t gRRectIndices[] = {
+ // clang-format off
+ // overstroke quads
+ // we place this at the beginning so that we can skip these indices when rendering as filled
+ 0, 6, 25, 0, 25, 24,
+ 6, 18, 27, 6, 27, 25,
+ 18, 12, 26, 18, 26, 27,
+ 12, 0, 24, 12, 24, 26,
+
+ // corners
+ 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5,
+ 6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7,
+ 12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13,
+ 18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23,
+
+ // edges
+ 0, 5, 11, 0, 11, 6,
+ 6, 7, 19, 6, 19, 18,
+ 18, 23, 17, 18, 17, 12,
+ 12, 13, 1, 12, 1, 0,
+
+ // fill quad
+ // we place this at the end so that we can skip these indices when rendering as stroked
+ 0, 6, 18, 0, 18, 12,
+ // clang-format on
+};
+
+// overstroke count
+static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
+// simple stroke count skips overstroke indices
+static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6 * 4;
+// fill count adds final quad to stroke count
+static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
+static const int kVertsPerStrokeRRect = 24;
+static const int kVertsPerOverstrokeRRect = 28;
+static const int kVertsPerFillRRect = 24;
+
+static int rrect_type_to_vert_count(RRectType type) {
+ switch (type) {
+ case kFill_RRectType:
+ return kVertsPerFillRRect;
+ case kStroke_RRectType:
+ return kVertsPerStrokeRRect;
+ case kOverstroke_RRectType:
+ return kVertsPerOverstrokeRRect;
+ }
+ ALOGE("Invalid rect type: %d", type);
+ return -1;
+}
+
+static int rrect_type_to_index_count(RRectType type) {
+ switch (type) {
+ case kFill_RRectType:
+ return kIndicesPerFillRRect;
+ case kStroke_RRectType:
+ return kIndicesPerStrokeRRect;
+ case kOverstroke_RRectType:
+ return kIndicesPerOverstrokeRRect;
+ }
+ ALOGE("Invalid rect type: %d", type);
+ return -1;
+}
+
+static const uint16_t* rrect_type_to_indices(RRectType type) {
+ switch (type) {
+ case kFill_RRectType:
+ case kStroke_RRectType:
+ return gRRectIndices + 6 * 4;
+ case kOverstroke_RRectType:
+ return gRRectIndices;
+ }
+ ALOGE("Invalid rect type: %d", type);
+ return nullptr;
+}
+
+static void fillInCircleVerts(const Geometry& args, bool isStroked,
+ Mesh::VertexArray<vec2>& position,
+ Mesh::VertexArray<vec4>& shadowColor,
+ Mesh::VertexArray<vec3>& shadowParams) {
+ vec4 color = args.fColor;
+ float outerRadius = args.fOuterRadius;
+ float innerRadius = args.fInnerRadius;
+ float blurRadius = args.fBlurRadius;
+ float distanceCorrection = outerRadius / blurRadius;
+
+ const FloatRect& bounds = args.fDevBounds;
+
+ // The inner radius in the vertex data must be specified in normalized space.
+ innerRadius = innerRadius / outerRadius;
+
+ vec2 center = vec2(bounds.getWidth() / 2.0f, bounds.getHeight() / 2.0f);
+ float halfWidth = 0.5f * bounds.getWidth();
+ float octOffset = 0.41421356237f; // sqrt(2) - 1
+ int vertexCount = 0;
+
+ position[vertexCount] = center + vec2(-octOffset * halfWidth, -halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-octOffset, -1, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(octOffset * halfWidth, -halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(octOffset, -1, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(halfWidth, -octOffset * halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(1, -octOffset, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(halfWidth, octOffset * halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(1, octOffset, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(octOffset * halfWidth, halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(octOffset, 1, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-octOffset * halfWidth, halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-octOffset, 1, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-halfWidth, octOffset * halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-1, octOffset, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-halfWidth, -octOffset * halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-1, -octOffset, distanceCorrection);
+ vertexCount++;
+
+ if (isStroked) {
+ // compute the inner ring
+
+ // cosine and sine of pi/8
+ float c = 0.923579533f;
+ float s = 0.382683432f;
+ float r = args.fInnerRadius;
+
+ position[vertexCount] = center + vec2(-s * r, -c * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-s * innerRadius, -c * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(s * r, -c * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(s * innerRadius, -c * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(c * r, -s * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(c * innerRadius, -s * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(c * r, s * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(c * innerRadius, s * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(s * r, c * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(s * innerRadius, c * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-s * r, c * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-s * innerRadius, c * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-c * r, s * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-c * innerRadius, s * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-c * r, -s * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-c * innerRadius, -s * innerRadius, distanceCorrection);
+ vertexCount++;
+ } else {
+ // filled
+ position[vertexCount] = center;
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+ }
+}
+
+static void fillInRRectVerts(const Geometry& args, Mesh::VertexArray<vec2>& position,
+ Mesh::VertexArray<vec4>& shadowColor,
+ Mesh::VertexArray<vec3>& shadowParams) {
+ vec4 color = args.fColor;
+ float outerRadius = args.fOuterRadius;
+
+ const FloatRect& bounds = args.fDevBounds;
+
+ float umbraInset = args.fUmbraInset;
+ float minDim = 0.5f * std::min(bounds.getWidth(), bounds.getHeight());
+ if (umbraInset > minDim) {
+ umbraInset = minDim;
+ }
+
+ float xInner[4] = {bounds.left + umbraInset, bounds.right - umbraInset,
+ bounds.left + umbraInset, bounds.right - umbraInset};
+ float xMid[4] = {bounds.left + outerRadius, bounds.right - outerRadius,
+ bounds.left + outerRadius, bounds.right - outerRadius};
+ float xOuter[4] = {bounds.left, bounds.right, bounds.left, bounds.right};
+ float yInner[4] = {bounds.top + umbraInset, bounds.top + umbraInset, bounds.bottom - umbraInset,
+ bounds.bottom - umbraInset};
+ float yMid[4] = {bounds.top + outerRadius, bounds.top + outerRadius,
+ bounds.bottom - outerRadius, bounds.bottom - outerRadius};
+ float yOuter[4] = {bounds.top, bounds.top, bounds.bottom, bounds.bottom};
+
+ float blurRadius = args.fBlurRadius;
+
+ // In the case where we have to inset more for the umbra, our two triangles in the
+ // corner get skewed to a diamond rather than a square. To correct for that,
+ // we also skew the vectors we send to the shader that help define the circle.
+ // By doing so, we end up with a quarter circle in the corner rather than the
+ // elliptical curve.
+
+ // This is a bit magical, but it gives us the correct results at extrema:
+ // a) umbraInset == outerRadius produces an orthogonal vector
+ // b) outerRadius == 0 produces a diagonal vector
+ // And visually the corner looks correct.
+ vec2 outerVec = vec2(outerRadius - umbraInset, -outerRadius - umbraInset);
+ outerVec = normalize(outerVec);
+ // We want the circle edge to fall fractionally along the diagonal at
+ // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset
+ //
+ // Setting the components of the diagonal offset to the following value will give us that.
+ float diagVal = umbraInset / (SK_ScalarSqrt2 * (outerRadius - umbraInset) - outerRadius);
+ vec2 diagVec = vec2(diagVal, diagVal);
+ float distanceCorrection = umbraInset / blurRadius;
+
+ int vertexCount = 0;
+ // build corner by corner
+ for (int i = 0; i < 4; ++i) {
+ // inner point
+ position[vertexCount] = vec2(xInner[i], yInner[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+
+ // outer points
+ position[vertexCount] = vec2(xOuter[i], yInner[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, -1, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = vec2(xOuter[i], yMid[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(outerVec.x, outerVec.y, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = vec2(xOuter[i], yOuter[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(diagVec.x, diagVec.y, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = vec2(xMid[i], yOuter[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(outerVec.x, outerVec.y, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = vec2(xInner[i], yOuter[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, -1, distanceCorrection);
+ vertexCount++;
+ }
+
+ // Add the additional vertices for overstroked rrects.
+ // Effectively this is an additional stroked rrect, with its
+ // parameters equal to those in the center of the 9-patch. This will
+ // give constant values across this inner ring.
+ if (kOverstroke_RRectType == args.fType) {
+ float inset = umbraInset + args.fInnerRadius;
+
+ // TL
+ position[vertexCount] = vec2(bounds.left + inset, bounds.top + inset);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+
+ // TR
+ position[vertexCount] = vec2(bounds.right - inset, bounds.top + inset);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+
+ // BL
+ position[vertexCount] = vec2(bounds.left + inset, bounds.bottom - inset);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+
+ // BR
+ position[vertexCount] = vec2(bounds.right - inset, bounds.bottom - inset);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+ }
+}
+
+int getVertexCountForGeometry(const Geometry& shadowGeometry) {
+ if (shadowGeometry.fIsCircle) {
+ return circle_type_to_vert_count(shadowGeometry.fType);
+ }
+
+ return rrect_type_to_vert_count(shadowGeometry.fType);
+}
+
+int getIndexCountForGeometry(const Geometry& shadowGeometry) {
+ if (shadowGeometry.fIsCircle) {
+ return circle_type_to_index_count(kStroke_RRectType == shadowGeometry.fType);
+ }
+
+ return rrect_type_to_index_count(shadowGeometry.fType);
+}
+
+void fillVerticesForGeometry(const Geometry& shadowGeometry, int /* vertexCount */,
+ Mesh::VertexArray<vec2> position, Mesh::VertexArray<vec4> shadowColor,
+ Mesh::VertexArray<vec3> shadowParams) {
+ if (shadowGeometry.fIsCircle) {
+ fillInCircleVerts(shadowGeometry, shadowGeometry.fIsStroked, position, shadowColor,
+ shadowParams);
+ } else {
+ fillInRRectVerts(shadowGeometry, position, shadowColor, shadowParams);
+ }
+}
+
+void fillIndicesForGeometry(const Geometry& shadowGeometry, int indexCount,
+ int startingVertexOffset, uint16_t* indices) {
+ if (shadowGeometry.fIsCircle) {
+ const uint16_t* primIndices = circle_type_to_indices(shadowGeometry.fIsStroked);
+ for (int i = 0; i < indexCount; ++i) {
+ indices[i] = primIndices[i] + startingVertexOffset;
+ }
+ } else {
+ const uint16_t* primIndices = rrect_type_to_indices(shadowGeometry.fType);
+ for (int i = 0; i < indexCount; ++i) {
+ indices[i] = primIndices[i] + startingVertexOffset;
+ }
+ }
+}
+
+inline void GetSpotParams(float occluderZ, float lightX, float lightY, float lightZ,
+ float lightRadius, float& blurRadius, float& scale, vec2& translate) {
+ float zRatio = divide_and_pin(occluderZ, lightZ - occluderZ, 0.0f, 0.95f);
+ blurRadius = lightRadius * zRatio;
+ scale = divide_and_pin(lightZ, lightZ - occluderZ, 1.0f, 1.95f);
+ translate.x = -zRatio * lightX;
+ translate.y = -zRatio * lightY;
+}
+
+static std::unique_ptr<Geometry> getShadowGeometry(const vec4& color, const FloatRect& devRect,
+ float devRadius, float blurRadius,
+ float insetWidth) {
+ // An insetWidth > 1/2 rect width or height indicates a simple fill.
+ const bool isCircle = ((devRadius >= devRect.getWidth()) && (devRadius >= devRect.getHeight()));
+
+ FloatRect bounds = devRect;
+ float innerRadius = 0.0f;
+ float outerRadius = devRadius;
+ float umbraInset;
+
+ RRectType type = kFill_RRectType;
+ if (isCircle) {
+ umbraInset = 0;
+ } else {
+ umbraInset = std::max(outerRadius, blurRadius);
+ }
+
+ // If stroke is greater than width or height, this is still a fill,
+ // otherwise we compute stroke params.
+ if (isCircle) {
+ innerRadius = devRadius - insetWidth;
+ type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
+ } else {
+ if (insetWidth <= 0.5f * std::min(devRect.getWidth(), devRect.getHeight())) {
+ // We don't worry about a real inner radius, we just need to know if we
+ // need to create overstroke vertices.
+ innerRadius = std::max(insetWidth - umbraInset, 0.0f);
+ type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
+ }
+ }
+ const bool isStroked = (kStroke_RRectType == type);
+ return std::make_unique<Geometry>(Geometry{color, outerRadius, umbraInset, innerRadius,
+ blurRadius, bounds, type, isCircle, isStroked});
+}
+
+std::unique_ptr<Geometry> getAmbientShadowGeometry(const FloatRect& casterRect,
+ float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent,
+ const vec4& ambientColor) {
+ float devSpaceInsetWidth = AmbientBlurRadius(casterZ);
+ const float umbraRecipAlpha = AmbientRecipAlpha(casterZ);
+ const float devSpaceAmbientBlur = devSpaceInsetWidth * umbraRecipAlpha;
+
+ // Outset the shadow rrect to the border of the penumbra
+ float ambientPathOutset = devSpaceInsetWidth;
+ FloatRect outsetRect(casterRect);
+ outsetRect.left -= ambientPathOutset;
+ outsetRect.top -= ambientPathOutset;
+ outsetRect.right += ambientPathOutset;
+ outsetRect.bottom += ambientPathOutset;
+
+ float outsetRad = casterCornerRadius + ambientPathOutset;
+ if (casterIsTranslucent) {
+ // set a large inset to force a fill
+ devSpaceInsetWidth = outsetRect.getWidth();
+ }
+
+ return getShadowGeometry(ambientColor, outsetRect, std::abs(outsetRad), devSpaceAmbientBlur,
+ std::abs(devSpaceInsetWidth));
+}
+
+std::unique_ptr<Geometry> getSpotShadowGeometry(const FloatRect& casterRect,
+ float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent, const vec4& spotColor,
+ const vec3& lightPosition, float lightRadius) {
+ float devSpaceSpotBlur;
+ float spotScale;
+ vec2 spotOffset;
+ GetSpotParams(casterZ, lightPosition.x, lightPosition.y, lightPosition.z, lightRadius,
+ devSpaceSpotBlur, spotScale, spotOffset);
+ // handle scale of radius due to CTM
+ const float srcSpaceSpotBlur = devSpaceSpotBlur;
+
+ // Adjust translate for the effect of the scale.
+ spotOffset.x += spotScale;
+ spotOffset.y += spotScale;
+
+ // Compute the transformed shadow rect
+ ui::Transform shadowTransform;
+ shadowTransform.set(spotOffset.x, spotOffset.y);
+ shadowTransform.set(spotScale, 0, 0, spotScale);
+ FloatRect spotShadowRect = shadowTransform.transform(casterRect);
+ float spotShadowRadius = casterCornerRadius * spotScale;
+
+ // Compute the insetWidth
+ float blurOutset = srcSpaceSpotBlur;
+ float insetWidth = blurOutset;
+ if (casterIsTranslucent) {
+ // If transparent, just do a fill
+ insetWidth += spotShadowRect.getWidth();
+ } else {
+ // For shadows, instead of using a stroke we specify an inset from the penumbra
+ // border. We want to extend this inset area so that it meets up with the caster
+ // geometry. The inset geometry will by default already be inset by the blur width.
+ //
+ // We compare the min and max corners inset by the radius between the original
+ // rrect and the shadow rrect. The distance between the two plus the difference
+ // between the scaled radius and the original radius gives the distance from the
+ // transformed shadow shape to the original shape in that corner. The max
+ // of these gives the maximum distance we need to cover.
+ //
+ // Since we are outsetting by 1/2 the blur distance, we just add the maxOffset to
+ // that to get the full insetWidth.
+ float maxOffset;
+ if (casterCornerRadius <= 0.f) {
+ // Manhattan distance works better for rects
+ maxOffset = std::max(std::max(std::abs(spotShadowRect.left - casterRect.left),
+ std::abs(spotShadowRect.top - casterRect.top)),
+ std::max(std::abs(spotShadowRect.right - casterRect.right),
+ std::abs(spotShadowRect.bottom - casterRect.bottom)));
+ } else {
+ float dr = spotShadowRadius - casterCornerRadius;
+ vec2 upperLeftOffset = vec2(spotShadowRect.left - casterRect.left + dr,
+ spotShadowRect.top - casterRect.top + dr);
+ vec2 lowerRightOffset = vec2(spotShadowRect.right - casterRect.right - dr,
+ spotShadowRect.bottom - casterRect.bottom - dr);
+ maxOffset = sqrt(std::max(dot(upperLeftOffset, lowerRightOffset),
+ dot(lowerRightOffset, lowerRightOffset))) +
+ dr;
+ }
+ insetWidth += std::max(blurOutset, maxOffset);
+ }
+
+ // Outset the shadow rrect to the border of the penumbra
+ spotShadowRadius += blurOutset;
+ spotShadowRect.left -= blurOutset;
+ spotShadowRect.top -= blurOutset;
+ spotShadowRect.right += blurOutset;
+ spotShadowRect.bottom += blurOutset;
+
+ return getShadowGeometry(spotColor, spotShadowRect, std::abs(spotShadowRadius),
+ 2.0f * devSpaceSpotBlur, std::abs(insetWidth));
+}
+
+void fillShadowTextureData(uint8_t* data, size_t shadowTextureWidth) {
+ for (int i = 0; i < shadowTextureWidth; i++) {
+ const float d = 1 - i / ((shadowTextureWidth * 1.0f) - 1.0f);
+ data[i] = static_cast<uint8_t>((exp(-4.0f * d * d) - 0.018f) * 255);
+ }
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/gl/GLSkiaShadowPort.h b/media/libstagefright/renderfright/gl/GLSkiaShadowPort.h
new file mode 100644
index 0000000..912c8bb
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/GLSkiaShadowPort.h
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <math/vec4.h>
+#include <renderengine/Mesh.h>
+#include <ui/Rect.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+/**
+ * The shadow geometry logic and vertex generation code has been ported from skia shadow
+ * fast path OpenGL implementation to draw shadows around rects and rounded rects including
+ * circles.
+ *
+ * path: skia/src/gpu/GrRenderTargetContext.cpp GrRenderTargetContext::drawFastShadow
+ *
+ * Modifications made:
+ * - Switched to using std lib math functions
+ * - Fall off function is implemented in vertex shader rather than a shadow texture
+ * - Removed transformations applied on the caster rect since the caster will be in local
+ * coordinate space and will be transformed by the vertex shader.
+ */
+
+enum RRectType {
+ kFill_RRectType,
+ kStroke_RRectType,
+ kOverstroke_RRectType,
+};
+
+struct Geometry {
+ vec4 fColor;
+ float fOuterRadius;
+ float fUmbraInset;
+ float fInnerRadius;
+ float fBlurRadius;
+ FloatRect fDevBounds;
+ RRectType fType;
+ bool fIsCircle;
+ bool fIsStroked;
+};
+
+std::unique_ptr<Geometry> getSpotShadowGeometry(const FloatRect& casterRect,
+ float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent, const vec4& spotColor,
+ const vec3& lightPosition, float lightRadius);
+
+std::unique_ptr<Geometry> getAmbientShadowGeometry(const FloatRect& casterRect,
+ float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent,
+ const vec4& ambientColor);
+
+int getVertexCountForGeometry(const Geometry& shadowGeometry);
+
+int getIndexCountForGeometry(const Geometry& shadowGeometry);
+
+void fillVerticesForGeometry(const Geometry& shadowGeometry, int vertexCount,
+ Mesh::VertexArray<vec2> position, Mesh::VertexArray<vec4> shadowColor,
+ Mesh::VertexArray<vec3> shadowParams);
+
+void fillIndicesForGeometry(const Geometry& shadowGeometry, int indexCount,
+ int startingVertexOffset, uint16_t* indices);
+
+/**
+ * Maps shadow geometry 'alpha' varying (1 for darkest, 0 for transparent) to
+ * darkness at that spot. Values are determined by an exponential falloff
+ * function provided by UX.
+ *
+ * The texture is used for quick lookup in theshadow shader.
+ *
+ * textureData - filled with shadow texture data that needs to be at least of
+ * size textureWidth
+ *
+ * textureWidth - width of the texture, height is always 1
+ */
+void fillShadowTextureData(uint8_t* textureData, size_t textureWidth);
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/gl/GLVertexBuffer.cpp b/media/libstagefright/renderfright/gl/GLVertexBuffer.cpp
new file mode 100644
index 0000000..e50c471
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/GLVertexBuffer.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "GLVertexBuffer.h"
+
+#include <GLES/gl.h>
+#include <GLES2/gl2.h>
+#include <nativebase/nativebase.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GLVertexBuffer::GLVertexBuffer() {
+ glGenBuffers(1, &mBufferName);
+}
+
+GLVertexBuffer::~GLVertexBuffer() {
+ glDeleteBuffers(1, &mBufferName);
+}
+
+void GLVertexBuffer::allocateBuffers(const GLfloat data[], const GLuint size) {
+ ATRACE_CALL();
+ bind();
+ glBufferData(GL_ARRAY_BUFFER, size * sizeof(GLfloat), data, GL_STATIC_DRAW);
+ unbind();
+}
+
+void GLVertexBuffer::bind() const {
+ glBindBuffer(GL_ARRAY_BUFFER, mBufferName);
+}
+
+void GLVertexBuffer::unbind() const {
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/gl/GLVertexBuffer.h b/media/libstagefright/renderfright/gl/GLVertexBuffer.h
new file mode 100644
index 0000000..c0fd0c1
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/GLVertexBuffer.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 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 <cstdint>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLESRenderEngine;
+
+class GLVertexBuffer {
+public:
+ explicit GLVertexBuffer();
+ ~GLVertexBuffer();
+
+ void allocateBuffers(const GLfloat data[], const GLuint size);
+ uint32_t getBufferName() const { return mBufferName; }
+ void bind() const;
+ void unbind() const;
+
+private:
+ uint32_t mBufferName;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/gl/ImageManager.cpp b/media/libstagefright/renderfright/gl/ImageManager.cpp
new file mode 100644
index 0000000..5b0cf52
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/ImageManager.cpp
@@ -0,0 +1,150 @@
+/*
+ * 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
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <pthread.h>
+
+#include <processgroup/sched_policy.h>
+#include <utils/Trace.h>
+#include "GLESRenderEngine.h"
+#include "ImageManager.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+ImageManager::ImageManager(GLESRenderEngine* engine) : mEngine(engine) {}
+
+void ImageManager::initThread(bool realtime) {
+ mThread = std::thread([this]() { threadMain(); });
+ pthread_setname_np(mThread.native_handle(), "ImageManager");
+ if (realtime) {
+ // Use SCHED_FIFO to minimize jitter
+ struct sched_param param = {0};
+ param.sched_priority = 2;
+ if (pthread_setschedparam(mThread.native_handle(), SCHED_FIFO, ¶m) != 0) {
+ ALOGE("Couldn't set SCHED_FIFO for ImageManager");
+ }
+ }
+}
+
+ImageManager::~ImageManager() {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mRunning = false;
+ }
+ mCondition.notify_all();
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+}
+
+void ImageManager::cacheAsync(const sp<GraphicBuffer>& buffer,
+ const std::shared_ptr<Barrier>& barrier) {
+ if (buffer == nullptr) {
+ {
+ std::lock_guard<std::mutex> lock(barrier->mutex);
+ barrier->isOpen = true;
+ barrier->result = BAD_VALUE;
+ }
+ barrier->condition.notify_one();
+ return;
+ }
+ ATRACE_CALL();
+ QueueEntry entry = {QueueEntry::Operation::Insert, buffer, buffer->getId(), barrier};
+ queueOperation(std::move(entry));
+}
+
+status_t ImageManager::cache(const sp<GraphicBuffer>& buffer) {
+ ATRACE_CALL();
+ auto barrier = std::make_shared<Barrier>();
+ cacheAsync(buffer, barrier);
+ std::lock_guard<std::mutex> lock(barrier->mutex);
+ barrier->condition.wait(barrier->mutex,
+ [&]() REQUIRES(barrier->mutex) { return barrier->isOpen; });
+ return barrier->result;
+}
+
+void ImageManager::releaseAsync(uint64_t bufferId, const std::shared_ptr<Barrier>& barrier) {
+ ATRACE_CALL();
+ QueueEntry entry = {QueueEntry::Operation::Delete, nullptr, bufferId, barrier};
+ queueOperation(std::move(entry));
+}
+
+void ImageManager::queueOperation(const QueueEntry&& entry) {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mQueue.emplace(entry);
+ ATRACE_INT("ImageManagerQueueDepth", mQueue.size());
+ }
+ mCondition.notify_one();
+}
+
+void ImageManager::threadMain() {
+ set_sched_policy(0, SP_FOREGROUND);
+ bool run;
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ run = mRunning;
+ }
+ while (run) {
+ QueueEntry entry;
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mCondition.wait(mMutex,
+ [&]() REQUIRES(mMutex) { return !mQueue.empty() || !mRunning; });
+ run = mRunning;
+
+ if (!mRunning) {
+ // if mRunning is false, then ImageManager is being destroyed, so
+ // bail out now.
+ break;
+ }
+
+ entry = mQueue.front();
+ mQueue.pop();
+ ATRACE_INT("ImageManagerQueueDepth", mQueue.size());
+ }
+
+ status_t result = NO_ERROR;
+ switch (entry.op) {
+ case QueueEntry::Operation::Delete:
+ mEngine->unbindExternalTextureBufferInternal(entry.bufferId);
+ break;
+ case QueueEntry::Operation::Insert:
+ result = mEngine->cacheExternalTextureBufferInternal(entry.buffer);
+ break;
+ }
+ if (entry.barrier != nullptr) {
+ {
+ std::lock_guard<std::mutex> entryLock(entry.barrier->mutex);
+ entry.barrier->result = result;
+ entry.barrier->isOpen = true;
+ }
+ entry.barrier->condition.notify_one();
+ }
+ }
+
+ ALOGD("Reached end of threadMain, terminating ImageManager thread!");
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/gl/ImageManager.h b/media/libstagefright/renderfright/gl/ImageManager.h
new file mode 100644
index 0000000..6be8e3c
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/ImageManager.h
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLESRenderEngine;
+
+class ImageManager {
+public:
+ struct Barrier {
+ std::mutex mutex;
+ std::condition_variable_any condition;
+ bool isOpen GUARDED_BY(mutex) = false;
+ status_t result GUARDED_BY(mutex) = NO_ERROR;
+ };
+ ImageManager(GLESRenderEngine* engine);
+ ~ImageManager();
+ // Starts the background thread for the ImageManager
+ // We need this to guarantee that the class is fully-constructed before the
+ // thread begins running.
+ void initThread(bool realtime);
+ void cacheAsync(const sp<GraphicBuffer>& buffer, const std::shared_ptr<Barrier>& barrier)
+ EXCLUDES(mMutex);
+ status_t cache(const sp<GraphicBuffer>& buffer);
+ void releaseAsync(uint64_t bufferId, const std::shared_ptr<Barrier>& barrier) EXCLUDES(mMutex);
+
+private:
+ struct QueueEntry {
+ enum class Operation { Delete, Insert };
+
+ Operation op = Operation::Delete;
+ sp<GraphicBuffer> buffer = nullptr;
+ uint64_t bufferId = 0;
+ std::shared_ptr<Barrier> barrier = nullptr;
+ };
+
+ void queueOperation(const QueueEntry&& entry);
+ void threadMain();
+ GLESRenderEngine* const mEngine;
+ std::thread mThread;
+ std::condition_variable_any mCondition;
+ std::mutex mMutex;
+ std::queue<QueueEntry> mQueue GUARDED_BY(mMutex);
+
+ bool mRunning GUARDED_BY(mMutex) = true;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/gl/Program.cpp b/media/libstagefright/renderfright/gl/Program.cpp
new file mode 100644
index 0000000..f4fbf35
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/Program.cpp
@@ -0,0 +1,163 @@
+/*Gluint
+ * 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.
+ */
+
+#include "Program.h"
+
+#include <stdint.h>
+
+#include <log/log.h>
+#include <math/mat4.h>
+#include <utils/String8.h>
+#include "ProgramCache.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+Program::Program(const ProgramCache::Key& /*needs*/, const char* vertex, const char* fragment)
+ : mInitialized(false) {
+ GLuint vertexId = buildShader(vertex, GL_VERTEX_SHADER);
+ GLuint fragmentId = buildShader(fragment, GL_FRAGMENT_SHADER);
+ GLuint programId = glCreateProgram();
+ glAttachShader(programId, vertexId);
+ glAttachShader(programId, fragmentId);
+ glBindAttribLocation(programId, position, "position");
+ glBindAttribLocation(programId, texCoords, "texCoords");
+ glBindAttribLocation(programId, cropCoords, "cropCoords");
+ glBindAttribLocation(programId, shadowColor, "shadowColor");
+ glBindAttribLocation(programId, shadowParams, "shadowParams");
+ glLinkProgram(programId);
+
+ GLint status;
+ glGetProgramiv(programId, GL_LINK_STATUS, &status);
+ if (status != GL_TRUE) {
+ ALOGE("Error while linking shaders:");
+ GLint infoLen = 0;
+ glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &infoLen);
+ if (infoLen > 1) {
+ GLchar log[infoLen];
+ glGetProgramInfoLog(programId, infoLen, 0, &log[0]);
+ ALOGE("%s", log);
+ }
+ glDetachShader(programId, vertexId);
+ glDetachShader(programId, fragmentId);
+ glDeleteShader(vertexId);
+ glDeleteShader(fragmentId);
+ glDeleteProgram(programId);
+ } else {
+ mProgram = programId;
+ mVertexShader = vertexId;
+ mFragmentShader = fragmentId;
+ mInitialized = true;
+ mProjectionMatrixLoc = glGetUniformLocation(programId, "projection");
+ mTextureMatrixLoc = glGetUniformLocation(programId, "texture");
+ mSamplerLoc = glGetUniformLocation(programId, "sampler");
+ mColorLoc = glGetUniformLocation(programId, "color");
+ mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance");
+ mMaxMasteringLuminanceLoc = glGetUniformLocation(programId, "maxMasteringLuminance");
+ mMaxContentLuminanceLoc = glGetUniformLocation(programId, "maxContentLuminance");
+ mInputTransformMatrixLoc = glGetUniformLocation(programId, "inputTransformMatrix");
+ mOutputTransformMatrixLoc = glGetUniformLocation(programId, "outputTransformMatrix");
+ mCornerRadiusLoc = glGetUniformLocation(programId, "cornerRadius");
+ mCropCenterLoc = glGetUniformLocation(programId, "cropCenter");
+
+ // set-up the default values for our uniforms
+ glUseProgram(programId);
+ glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, mat4().asArray());
+ glEnableVertexAttribArray(0);
+ }
+}
+
+bool Program::isValid() const {
+ return mInitialized;
+}
+
+void Program::use() {
+ glUseProgram(mProgram);
+}
+
+GLuint Program::getAttrib(const char* name) const {
+ // TODO: maybe use a local cache
+ return glGetAttribLocation(mProgram, name);
+}
+
+GLint Program::getUniform(const char* name) const {
+ // TODO: maybe use a local cache
+ return glGetUniformLocation(mProgram, name);
+}
+
+GLuint Program::buildShader(const char* source, GLenum type) {
+ GLuint shader = glCreateShader(type);
+ glShaderSource(shader, 1, &source, 0);
+ glCompileShader(shader);
+ GLint status;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+ if (status != GL_TRUE) {
+ // Some drivers return wrong values for GL_INFO_LOG_LENGTH
+ // use a fixed size instead
+ GLchar log[512];
+ glGetShaderInfoLog(shader, sizeof(log), 0, log);
+ ALOGE("Error while compiling shader: \n%s\n%s", source, log);
+ glDeleteShader(shader);
+ return 0;
+ }
+ return shader;
+}
+
+void Program::setUniforms(const Description& desc) {
+ // TODO: we should have a mechanism here to not always reset uniforms that
+ // didn't change for this program.
+
+ if (mSamplerLoc >= 0) {
+ glUniform1i(mSamplerLoc, 0);
+ glUniformMatrix4fv(mTextureMatrixLoc, 1, GL_FALSE, desc.texture.getMatrix().asArray());
+ }
+ if (mColorLoc >= 0) {
+ const float color[4] = {desc.color.r, desc.color.g, desc.color.b, desc.color.a};
+ glUniform4fv(mColorLoc, 1, color);
+ }
+ if (mInputTransformMatrixLoc >= 0) {
+ mat4 inputTransformMatrix = desc.inputTransformMatrix;
+ glUniformMatrix4fv(mInputTransformMatrixLoc, 1, GL_FALSE, inputTransformMatrix.asArray());
+ }
+ if (mOutputTransformMatrixLoc >= 0) {
+ // The output transform matrix and color matrix can be combined as one matrix
+ // that is applied right before applying OETF.
+ mat4 outputTransformMatrix = desc.colorMatrix * desc.outputTransformMatrix;
+ glUniformMatrix4fv(mOutputTransformMatrixLoc, 1, GL_FALSE, outputTransformMatrix.asArray());
+ }
+ if (mDisplayMaxLuminanceLoc >= 0) {
+ glUniform1f(mDisplayMaxLuminanceLoc, desc.displayMaxLuminance);
+ }
+ if (mMaxMasteringLuminanceLoc >= 0) {
+ glUniform1f(mMaxMasteringLuminanceLoc, desc.maxMasteringLuminance);
+ }
+ if (mMaxContentLuminanceLoc >= 0) {
+ glUniform1f(mMaxContentLuminanceLoc, desc.maxContentLuminance);
+ }
+ if (mCornerRadiusLoc >= 0) {
+ glUniform1f(mCornerRadiusLoc, desc.cornerRadius);
+ }
+ if (mCropCenterLoc >= 0) {
+ glUniform2f(mCropCenterLoc, desc.cropSize.x / 2.0f, desc.cropSize.y / 2.0f);
+ }
+ // these uniforms are always present
+ glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, desc.projectionMatrix.asArray());
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/gl/Program.h b/media/libstagefright/renderfright/gl/Program.h
new file mode 100644
index 0000000..fc3755e
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/Program.h
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+#ifndef SF_RENDER_ENGINE_PROGRAM_H
+#define SF_RENDER_ENGINE_PROGRAM_H
+
+#include <stdint.h>
+
+#include <GLES2/gl2.h>
+#include <renderengine/private/Description.h>
+#include "ProgramCache.h"
+
+namespace android {
+
+class String8;
+
+namespace renderengine {
+namespace gl {
+
+/*
+ * Abstracts a GLSL program comprising a vertex and fragment shader
+ */
+class Program {
+public:
+ // known locations for position and texture coordinates
+ enum {
+ /* position of each vertex for vertex shader */
+ position = 0,
+
+ /* UV coordinates for texture mapping */
+ texCoords = 1,
+
+ /* Crop coordinates, in pixels */
+ cropCoords = 2,
+
+ /* Shadow color */
+ shadowColor = 3,
+
+ /* Shadow params */
+ shadowParams = 4,
+ };
+
+ Program(const ProgramCache::Key& needs, const char* vertex, const char* fragment);
+ ~Program() = default;
+
+ /* whether this object is usable */
+ bool isValid() const;
+
+ /* Binds this program to the GLES context */
+ void use();
+
+ /* Returns the location of the specified attribute */
+ GLuint getAttrib(const char* name) const;
+
+ /* Returns the location of the specified uniform */
+ GLint getUniform(const char* name) const;
+
+ /* set-up uniforms from the description */
+ void setUniforms(const Description& desc);
+
+private:
+ GLuint buildShader(const char* source, GLenum type);
+
+ // whether the initialization succeeded
+ bool mInitialized;
+
+ // Name of the OpenGL program and shaders
+ GLuint mProgram;
+ GLuint mVertexShader;
+ GLuint mFragmentShader;
+
+ /* location of the projection matrix uniform */
+ GLint mProjectionMatrixLoc;
+
+ /* location of the texture matrix uniform */
+ GLint mTextureMatrixLoc;
+
+ /* location of the sampler uniform */
+ GLint mSamplerLoc;
+
+ /* location of the color uniform */
+ GLint mColorLoc;
+
+ /* location of display luminance uniform */
+ GLint mDisplayMaxLuminanceLoc;
+ /* location of max mastering luminance uniform */
+ GLint mMaxMasteringLuminanceLoc;
+ /* location of max content luminance uniform */
+ GLint mMaxContentLuminanceLoc;
+
+ /* location of transform matrix */
+ GLint mInputTransformMatrixLoc;
+ GLint mOutputTransformMatrixLoc;
+
+ /* location of corner radius uniform */
+ GLint mCornerRadiusLoc;
+
+ /* location of surface crop origin uniform, for rounded corner clipping */
+ GLint mCropCenterLoc;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
+
+#endif /* SF_RENDER_ENGINE_PROGRAM_H */
diff --git a/media/libstagefright/renderfright/gl/ProgramCache.cpp b/media/libstagefright/renderfright/gl/ProgramCache.cpp
new file mode 100644
index 0000000..3ae35ec
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/ProgramCache.cpp
@@ -0,0 +1,800 @@
+/*
+ * 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "ProgramCache.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <log/log.h>
+#include <renderengine/private/Description.h>
+#include <utils/String8.h>
+#include <utils/Trace.h>
+#include "Program.h"
+
+ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::ProgramCache)
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+/*
+ * A simple formatter class to automatically add the endl and
+ * manage the indentation.
+ */
+
+class Formatter;
+static Formatter& indent(Formatter& f);
+static Formatter& dedent(Formatter& f);
+
+class Formatter {
+ String8 mString;
+ int mIndent;
+ typedef Formatter& (*FormaterManipFunc)(Formatter&);
+ friend Formatter& indent(Formatter& f);
+ friend Formatter& dedent(Formatter& f);
+
+public:
+ Formatter() : mIndent(0) {}
+
+ String8 getString() const { return mString; }
+
+ friend Formatter& operator<<(Formatter& out, const char* in) {
+ for (int i = 0; i < out.mIndent; i++) {
+ out.mString.append(" ");
+ }
+ out.mString.append(in);
+ out.mString.append("\n");
+ return out;
+ }
+ friend inline Formatter& operator<<(Formatter& out, const String8& in) {
+ return operator<<(out, in.string());
+ }
+ friend inline Formatter& operator<<(Formatter& to, FormaterManipFunc func) {
+ return (*func)(to);
+ }
+};
+Formatter& indent(Formatter& f) {
+ f.mIndent++;
+ return f;
+}
+Formatter& dedent(Formatter& f) {
+ f.mIndent--;
+ return f;
+}
+
+void ProgramCache::primeCache(
+ EGLContext context, bool useColorManagement, bool toneMapperShaderOnly) {
+ auto& cache = mCaches[context];
+ uint32_t shaderCount = 0;
+
+ if (toneMapperShaderOnly) {
+ Key shaderKey;
+ // base settings used by HDR->SDR tonemap only
+ shaderKey.set(Key::BLEND_MASK | Key::INPUT_TRANSFORM_MATRIX_MASK |
+ Key::OUTPUT_TRANSFORM_MATRIX_MASK | Key::OUTPUT_TF_MASK |
+ Key::OPACITY_MASK | Key::ALPHA_MASK |
+ Key::ROUNDED_CORNERS_MASK | Key::TEXTURE_MASK,
+ Key::BLEND_NORMAL | Key::INPUT_TRANSFORM_MATRIX_ON |
+ Key::OUTPUT_TRANSFORM_MATRIX_ON | Key::OUTPUT_TF_SRGB |
+ Key::OPACITY_OPAQUE | Key::ALPHA_EQ_ONE |
+ Key::ROUNDED_CORNERS_OFF | Key::TEXTURE_EXT);
+ for (int i = 0; i < 4; i++) {
+ // Cache input transfer for HLG & ST2084
+ shaderKey.set(Key::INPUT_TF_MASK, (i & 1) ?
+ Key::INPUT_TF_HLG : Key::INPUT_TF_ST2084);
+
+ // Cache Y410 input on or off
+ shaderKey.set(Key::Y410_BT2020_MASK, (i & 2) ?
+ Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF);
+ if (cache.count(shaderKey) == 0) {
+ cache.emplace(shaderKey, generateProgram(shaderKey));
+ shaderCount++;
+ }
+ }
+ return;
+ }
+
+ uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | Key::ALPHA_MASK | Key::TEXTURE_MASK
+ | Key::ROUNDED_CORNERS_MASK;
+ // Prime the cache for all combinations of the above masks,
+ // leaving off the experimental color matrix mask options.
+
+ nsecs_t timeBefore = systemTime();
+ for (uint32_t keyVal = 0; keyVal <= keyMask; keyVal++) {
+ Key shaderKey;
+ shaderKey.set(keyMask, keyVal);
+ uint32_t tex = shaderKey.getTextureTarget();
+ if (tex != Key::TEXTURE_OFF && tex != Key::TEXTURE_EXT && tex != Key::TEXTURE_2D) {
+ continue;
+ }
+ if (cache.count(shaderKey) == 0) {
+ cache.emplace(shaderKey, generateProgram(shaderKey));
+ shaderCount++;
+ }
+ }
+
+ // Prime for sRGB->P3 conversion
+ if (useColorManagement) {
+ Key shaderKey;
+ shaderKey.set(Key::BLEND_MASK | Key::OUTPUT_TRANSFORM_MATRIX_MASK | Key::INPUT_TF_MASK |
+ Key::OUTPUT_TF_MASK,
+ Key::BLEND_PREMULT | Key::OUTPUT_TRANSFORM_MATRIX_ON | Key::INPUT_TF_SRGB |
+ Key::OUTPUT_TF_SRGB);
+ for (int i = 0; i < 16; i++) {
+ shaderKey.set(Key::OPACITY_MASK,
+ (i & 1) ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT);
+ shaderKey.set(Key::ALPHA_MASK, (i & 2) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE);
+
+ // Cache rounded corners
+ shaderKey.set(Key::ROUNDED_CORNERS_MASK,
+ (i & 4) ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF);
+
+ // Cache texture off option for window transition
+ shaderKey.set(Key::TEXTURE_MASK, (i & 8) ? Key::TEXTURE_EXT : Key::TEXTURE_OFF);
+ if (cache.count(shaderKey) == 0) {
+ cache.emplace(shaderKey, generateProgram(shaderKey));
+ shaderCount++;
+ }
+ }
+ }
+
+ nsecs_t timeAfter = systemTime();
+ float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
+ ALOGD("shader cache generated - %u shaders in %f ms\n", shaderCount, compileTimeMs);
+}
+
+ProgramCache::Key ProgramCache::computeKey(const Description& description) {
+ Key needs;
+ needs.set(Key::TEXTURE_MASK,
+ !description.textureEnabled
+ ? Key::TEXTURE_OFF
+ : description.texture.getTextureTarget() == GL_TEXTURE_EXTERNAL_OES
+ ? Key::TEXTURE_EXT
+ : description.texture.getTextureTarget() == GL_TEXTURE_2D
+ ? Key::TEXTURE_2D
+ : Key::TEXTURE_OFF)
+ .set(Key::ALPHA_MASK, (description.color.a < 1) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE)
+ .set(Key::BLEND_MASK,
+ description.isPremultipliedAlpha ? Key::BLEND_PREMULT : Key::BLEND_NORMAL)
+ .set(Key::OPACITY_MASK,
+ description.isOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT)
+ .set(Key::Key::INPUT_TRANSFORM_MATRIX_MASK,
+ description.hasInputTransformMatrix() ? Key::INPUT_TRANSFORM_MATRIX_ON
+ : Key::INPUT_TRANSFORM_MATRIX_OFF)
+ .set(Key::Key::OUTPUT_TRANSFORM_MATRIX_MASK,
+ description.hasOutputTransformMatrix() || description.hasColorMatrix()
+ ? Key::OUTPUT_TRANSFORM_MATRIX_ON
+ : Key::OUTPUT_TRANSFORM_MATRIX_OFF)
+ .set(Key::ROUNDED_CORNERS_MASK,
+ description.cornerRadius > 0 ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF)
+ .set(Key::SHADOW_MASK, description.drawShadows ? Key::SHADOW_ON : Key::SHADOW_OFF);
+ needs.set(Key::Y410_BT2020_MASK,
+ description.isY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF);
+
+ if (needs.hasTransformMatrix() ||
+ (description.inputTransferFunction != description.outputTransferFunction)) {
+ switch (description.inputTransferFunction) {
+ case Description::TransferFunction::LINEAR:
+ default:
+ needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_LINEAR);
+ break;
+ case Description::TransferFunction::SRGB:
+ needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_SRGB);
+ break;
+ case Description::TransferFunction::ST2084:
+ needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_ST2084);
+ break;
+ case Description::TransferFunction::HLG:
+ needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_HLG);
+ break;
+ }
+
+ switch (description.outputTransferFunction) {
+ case Description::TransferFunction::LINEAR:
+ default:
+ needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_LINEAR);
+ break;
+ case Description::TransferFunction::SRGB:
+ needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_SRGB);
+ break;
+ case Description::TransferFunction::ST2084:
+ needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_ST2084);
+ break;
+ case Description::TransferFunction::HLG:
+ needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_HLG);
+ break;
+ }
+ }
+
+ return needs;
+}
+
+// Generate EOTF that converts signal values to relative display light,
+// both normalized to [0, 1].
+void ProgramCache::generateEOTF(Formatter& fs, const Key& needs) {
+ switch (needs.getInputTF()) {
+ case Key::INPUT_TF_SRGB:
+ fs << R"__SHADER__(
+ float EOTF_sRGB(float srgb) {
+ return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
+ }
+
+ vec3 EOTF_sRGB(const vec3 srgb) {
+ return vec3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+ }
+
+ vec3 EOTF(const vec3 srgb) {
+ return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+ }
+ )__SHADER__";
+ break;
+ case Key::INPUT_TF_ST2084:
+ fs << R"__SHADER__(
+ vec3 EOTF(const highp vec3 color) {
+ const highp float m1 = (2610.0 / 4096.0) / 4.0;
+ const highp float m2 = (2523.0 / 4096.0) * 128.0;
+ const highp float c1 = (3424.0 / 4096.0);
+ const highp float c2 = (2413.0 / 4096.0) * 32.0;
+ const highp float c3 = (2392.0 / 4096.0) * 32.0;
+
+ highp vec3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / vec3(m2));
+ tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp);
+ return pow(tmp, 1.0 / vec3(m1));
+ }
+ )__SHADER__";
+ break;
+ case Key::INPUT_TF_HLG:
+ fs << R"__SHADER__(
+ highp float EOTF_channel(const highp float channel) {
+ const highp float a = 0.17883277;
+ const highp float b = 0.28466892;
+ const highp float c = 0.55991073;
+ return channel <= 0.5 ? channel * channel / 3.0 :
+ (exp((channel - c) / a) + b) / 12.0;
+ }
+
+ vec3 EOTF(const highp vec3 color) {
+ return vec3(EOTF_channel(color.r), EOTF_channel(color.g),
+ EOTF_channel(color.b));
+ }
+ )__SHADER__";
+ break;
+ default:
+ fs << R"__SHADER__(
+ vec3 EOTF(const vec3 linear) {
+ return linear;
+ }
+ )__SHADER__";
+ break;
+ }
+}
+
+void ProgramCache::generateToneMappingProcess(Formatter& fs, const Key& needs) {
+ // Convert relative light to absolute light.
+ switch (needs.getInputTF()) {
+ case Key::INPUT_TF_ST2084:
+ fs << R"__SHADER__(
+ highp vec3 ScaleLuminance(highp vec3 color) {
+ return color * 10000.0;
+ }
+ )__SHADER__";
+ break;
+ case Key::INPUT_TF_HLG:
+ fs << R"__SHADER__(
+ highp vec3 ScaleLuminance(highp vec3 color) {
+ // The formula is:
+ // alpha * pow(Y, gamma - 1.0) * color + beta;
+ // where alpha is 1000.0, gamma is 1.2, beta is 0.0.
+ return color * 1000.0 * pow(color.y, 0.2);
+ }
+ )__SHADER__";
+ break;
+ default:
+ fs << R"__SHADER__(
+ highp vec3 ScaleLuminance(highp vec3 color) {
+ return color * displayMaxLuminance;
+ }
+ )__SHADER__";
+ break;
+ }
+
+ // Tone map absolute light to display luminance range.
+ switch (needs.getInputTF()) {
+ case Key::INPUT_TF_ST2084:
+ case Key::INPUT_TF_HLG:
+ switch (needs.getOutputTF()) {
+ case Key::OUTPUT_TF_HLG:
+ // Right now when mixed PQ and HLG contents are presented,
+ // HLG content will always be converted to PQ. However, for
+ // completeness, we simply clamp the value to [0.0, 1000.0].
+ fs << R"__SHADER__(
+ highp vec3 ToneMap(highp vec3 color) {
+ return clamp(color, 0.0, 1000.0);
+ }
+ )__SHADER__";
+ break;
+ case Key::OUTPUT_TF_ST2084:
+ fs << R"__SHADER__(
+ highp vec3 ToneMap(highp vec3 color) {
+ return color;
+ }
+ )__SHADER__";
+ break;
+ default:
+ fs << R"__SHADER__(
+ highp vec3 ToneMap(highp vec3 color) {
+ float maxMasteringLumi = maxMasteringLuminance;
+ float maxContentLumi = maxContentLuminance;
+ float maxInLumi = min(maxMasteringLumi, maxContentLumi);
+ float maxOutLumi = displayMaxLuminance;
+
+ float nits = color.y;
+
+ // clamp to max input luminance
+ nits = clamp(nits, 0.0, maxInLumi);
+
+ // scale [0.0, maxInLumi] to [0.0, maxOutLumi]
+ if (maxInLumi <= maxOutLumi) {
+ return color * (maxOutLumi / maxInLumi);
+ } else {
+ // three control points
+ const float x0 = 10.0;
+ const float y0 = 17.0;
+ float x1 = maxOutLumi * 0.75;
+ float y1 = x1;
+ float x2 = x1 + (maxInLumi - x1) / 2.0;
+ float y2 = y1 + (maxOutLumi - y1) * 0.75;
+
+ // horizontal distances between the last three control points
+ float h12 = x2 - x1;
+ float h23 = maxInLumi - x2;
+ // tangents at the last three control points
+ float m1 = (y2 - y1) / h12;
+ float m3 = (maxOutLumi - y2) / h23;
+ float m2 = (m1 + m3) / 2.0;
+
+ if (nits < x0) {
+ // scale [0.0, x0] to [0.0, y0] linearly
+ float slope = y0 / x0;
+ return color * slope;
+ } else if (nits < x1) {
+ // scale [x0, x1] to [y0, y1] linearly
+ float slope = (y1 - y0) / (x1 - x0);
+ nits = y0 + (nits - x0) * slope;
+ } else if (nits < x2) {
+ // scale [x1, x2] to [y1, y2] using Hermite interp
+ float t = (nits - x1) / h12;
+ nits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) * (1.0 - t) +
+ (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t;
+ } else {
+ // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp
+ float t = (nits - x2) / h23;
+ nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * (1.0 - t) +
+ (maxOutLumi * (3.0 - 2.0 * t) + h23 * m3 * (t - 1.0)) * t * t;
+ }
+ }
+
+ // color.y is greater than x0 and is thus non-zero
+ return color * (nits / color.y);
+ }
+ )__SHADER__";
+ break;
+ }
+ break;
+ default:
+ // inverse tone map; the output luminance can be up to maxOutLumi.
+ fs << R"__SHADER__(
+ highp vec3 ToneMap(highp vec3 color) {
+ const float maxOutLumi = 3000.0;
+
+ const float x0 = 5.0;
+ const float y0 = 2.5;
+ float x1 = displayMaxLuminance * 0.7;
+ float y1 = maxOutLumi * 0.15;
+ float x2 = displayMaxLuminance * 0.9;
+ float y2 = maxOutLumi * 0.45;
+ float x3 = displayMaxLuminance;
+ float y3 = maxOutLumi;
+
+ float c1 = y1 / 3.0;
+ float c2 = y2 / 2.0;
+ float c3 = y3 / 1.5;
+
+ float nits = color.y;
+
+ float scale;
+ if (nits <= x0) {
+ // scale [0.0, x0] to [0.0, y0] linearly
+ const float slope = y0 / x0;
+ return color * slope;
+ } else if (nits <= x1) {
+ // scale [x0, x1] to [y0, y1] using a curve
+ float t = (nits - x0) / (x1 - x0);
+ nits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 + t * t * y1;
+ } else if (nits <= x2) {
+ // scale [x1, x2] to [y1, y2] using a curve
+ float t = (nits - x1) / (x2 - x1);
+ nits = (1.0 - t) * (1.0 - t) * y1 + 2.0 * (1.0 - t) * t * c2 + t * t * y2;
+ } else {
+ // scale [x2, x3] to [y2, y3] using a curve
+ float t = (nits - x2) / (x3 - x2);
+ nits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 + t * t * y3;
+ }
+
+ // color.y is greater than x0 and is thus non-zero
+ return color * (nits / color.y);
+ }
+ )__SHADER__";
+ break;
+ }
+
+ // convert absolute light to relative light.
+ switch (needs.getOutputTF()) {
+ case Key::OUTPUT_TF_ST2084:
+ fs << R"__SHADER__(
+ highp vec3 NormalizeLuminance(highp vec3 color) {
+ return color / 10000.0;
+ }
+ )__SHADER__";
+ break;
+ case Key::OUTPUT_TF_HLG:
+ fs << R"__SHADER__(
+ highp vec3 NormalizeLuminance(highp vec3 color) {
+ return color / 1000.0 * pow(color.y / 1000.0, -0.2 / 1.2);
+ }
+ )__SHADER__";
+ break;
+ default:
+ fs << R"__SHADER__(
+ highp vec3 NormalizeLuminance(highp vec3 color) {
+ return color / displayMaxLuminance;
+ }
+ )__SHADER__";
+ break;
+ }
+}
+
+// Generate OOTF that modifies the relative scence light to relative display light.
+void ProgramCache::generateOOTF(Formatter& fs, const ProgramCache::Key& needs) {
+ if (!needs.needsToneMapping()) {
+ fs << R"__SHADER__(
+ highp vec3 OOTF(const highp vec3 color) {
+ return color;
+ }
+ )__SHADER__";
+ } else {
+ generateToneMappingProcess(fs, needs);
+ fs << R"__SHADER__(
+ highp vec3 OOTF(const highp vec3 color) {
+ return NormalizeLuminance(ToneMap(ScaleLuminance(color)));
+ }
+ )__SHADER__";
+ }
+}
+
+// Generate OETF that converts relative display light to signal values,
+// both normalized to [0, 1]
+void ProgramCache::generateOETF(Formatter& fs, const Key& needs) {
+ switch (needs.getOutputTF()) {
+ case Key::OUTPUT_TF_SRGB:
+ fs << R"__SHADER__(
+ float OETF_sRGB(const float linear) {
+ return linear <= 0.0031308 ?
+ linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
+ }
+
+ vec3 OETF_sRGB(const vec3 linear) {
+ return vec3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+ }
+
+ vec3 OETF(const vec3 linear) {
+ return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+ }
+ )__SHADER__";
+ break;
+ case Key::OUTPUT_TF_ST2084:
+ fs << R"__SHADER__(
+ vec3 OETF(const vec3 linear) {
+ const highp float m1 = (2610.0 / 4096.0) / 4.0;
+ const highp float m2 = (2523.0 / 4096.0) * 128.0;
+ const highp float c1 = (3424.0 / 4096.0);
+ const highp float c2 = (2413.0 / 4096.0) * 32.0;
+ const highp float c3 = (2392.0 / 4096.0) * 32.0;
+
+ highp vec3 tmp = pow(linear, vec3(m1));
+ tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
+ return pow(tmp, vec3(m2));
+ }
+ )__SHADER__";
+ break;
+ case Key::OUTPUT_TF_HLG:
+ fs << R"__SHADER__(
+ highp float OETF_channel(const highp float channel) {
+ const highp float a = 0.17883277;
+ const highp float b = 0.28466892;
+ const highp float c = 0.55991073;
+ return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) :
+ a * log(12.0 * channel - b) + c;
+ }
+
+ vec3 OETF(const highp vec3 color) {
+ return vec3(OETF_channel(color.r), OETF_channel(color.g),
+ OETF_channel(color.b));
+ }
+ )__SHADER__";
+ break;
+ default:
+ fs << R"__SHADER__(
+ vec3 OETF(const vec3 linear) {
+ return linear;
+ }
+ )__SHADER__";
+ break;
+ }
+}
+
+String8 ProgramCache::generateVertexShader(const Key& needs) {
+ Formatter vs;
+ if (needs.hasTextureCoords()) {
+ vs << "attribute vec4 texCoords;"
+ << "varying vec2 outTexCoords;";
+ }
+ if (needs.hasRoundedCorners()) {
+ vs << "attribute lowp vec4 cropCoords;";
+ vs << "varying lowp vec2 outCropCoords;";
+ }
+ if (needs.drawShadows()) {
+ vs << "attribute lowp vec4 shadowColor;";
+ vs << "varying lowp vec4 outShadowColor;";
+ vs << "attribute lowp vec4 shadowParams;";
+ vs << "varying lowp vec3 outShadowParams;";
+ }
+ vs << "attribute vec4 position;"
+ << "uniform mat4 projection;"
+ << "uniform mat4 texture;"
+ << "void main(void) {" << indent << "gl_Position = projection * position;";
+ if (needs.hasTextureCoords()) {
+ vs << "outTexCoords = (texture * texCoords).st;";
+ }
+ if (needs.hasRoundedCorners()) {
+ vs << "outCropCoords = cropCoords.st;";
+ }
+ if (needs.drawShadows()) {
+ vs << "outShadowColor = shadowColor;";
+ vs << "outShadowParams = shadowParams.xyz;";
+ }
+ vs << dedent << "}";
+ return vs.getString();
+}
+
+String8 ProgramCache::generateFragmentShader(const Key& needs) {
+ Formatter fs;
+ if (needs.getTextureTarget() == Key::TEXTURE_EXT) {
+ fs << "#extension GL_OES_EGL_image_external : require";
+ }
+
+ // default precision is required-ish in fragment shaders
+ fs << "precision mediump float;";
+
+ if (needs.getTextureTarget() == Key::TEXTURE_EXT) {
+ fs << "uniform samplerExternalOES sampler;";
+ } else if (needs.getTextureTarget() == Key::TEXTURE_2D) {
+ fs << "uniform sampler2D sampler;";
+ }
+
+ if (needs.hasTextureCoords()) {
+ fs << "varying vec2 outTexCoords;";
+ }
+
+ if (needs.hasRoundedCorners()) {
+ // Rounded corners implementation using a signed distance function.
+ fs << R"__SHADER__(
+ uniform float cornerRadius;
+ uniform vec2 cropCenter;
+ varying vec2 outCropCoords;
+
+ /**
+ * This function takes the current crop coordinates and calculates an alpha value based
+ * on the corner radius and distance from the crop center.
+ */
+ float applyCornerRadius(vec2 cropCoords)
+ {
+ vec2 position = cropCoords - cropCenter;
+ // Scale down the dist vector here, as otherwise large corner
+ // radii can cause floating point issues when computing the norm
+ vec2 dist = (abs(position) - cropCenter + vec2(cornerRadius)) / 16.0;
+ // Once we've found the norm, then scale back up.
+ float plane = length(max(dist, vec2(0.0))) * 16.0;
+ return 1.0 - clamp(plane - cornerRadius, 0.0, 1.0);
+ }
+ )__SHADER__";
+ }
+
+ if (needs.drawShadows()) {
+ fs << R"__SHADER__(
+ varying lowp vec4 outShadowColor;
+ varying lowp vec3 outShadowParams;
+
+ /**
+ * Returns the shadow color.
+ */
+ vec4 getShadowColor()
+ {
+ lowp float d = length(outShadowParams.xy);
+ vec2 uv = vec2(outShadowParams.z * (1.0 - d), 0.5);
+ lowp float factor = texture2D(sampler, uv).a;
+ return outShadowColor * factor;
+ }
+ )__SHADER__";
+ }
+
+ if (needs.getTextureTarget() == Key::TEXTURE_OFF || needs.hasAlpha()) {
+ fs << "uniform vec4 color;";
+ }
+
+ if (needs.isY410BT2020()) {
+ fs << R"__SHADER__(
+ vec3 convertY410BT2020(const vec3 color) {
+ const vec3 offset = vec3(0.0625, 0.5, 0.5);
+ const mat3 transform = mat3(
+ vec3(1.1678, 1.1678, 1.1678),
+ vec3( 0.0, -0.1878, 2.1481),
+ vec3(1.6836, -0.6523, 0.0));
+ // Y is in G, U is in R, and V is in B
+ return clamp(transform * (color.grb - offset), 0.0, 1.0);
+ }
+ )__SHADER__";
+ }
+
+ if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
+ if (needs.needsToneMapping()) {
+ fs << "uniform float displayMaxLuminance;";
+ fs << "uniform float maxMasteringLuminance;";
+ fs << "uniform float maxContentLuminance;";
+ }
+
+ if (needs.hasInputTransformMatrix()) {
+ fs << "uniform mat4 inputTransformMatrix;";
+ fs << R"__SHADER__(
+ highp vec3 InputTransform(const highp vec3 color) {
+ return clamp(vec3(inputTransformMatrix * vec4(color, 1.0)), 0.0, 1.0);
+ }
+ )__SHADER__";
+ } else {
+ fs << R"__SHADER__(
+ highp vec3 InputTransform(const highp vec3 color) {
+ return color;
+ }
+ )__SHADER__";
+ }
+
+ // the transformation from a wider colorspace to a narrower one can
+ // result in >1.0 or <0.0 pixel values
+ if (needs.hasOutputTransformMatrix()) {
+ fs << "uniform mat4 outputTransformMatrix;";
+ fs << R"__SHADER__(
+ highp vec3 OutputTransform(const highp vec3 color) {
+ return clamp(vec3(outputTransformMatrix * vec4(color, 1.0)), 0.0, 1.0);
+ }
+ )__SHADER__";
+ } else {
+ fs << R"__SHADER__(
+ highp vec3 OutputTransform(const highp vec3 color) {
+ return clamp(color, 0.0, 1.0);
+ }
+ )__SHADER__";
+ }
+
+ generateEOTF(fs, needs);
+ generateOOTF(fs, needs);
+ generateOETF(fs, needs);
+ }
+
+ fs << "void main(void) {" << indent;
+ if (needs.drawShadows()) {
+ fs << "gl_FragColor = getShadowColor();";
+ } else {
+ if (needs.isTexturing()) {
+ fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
+ if (needs.isY410BT2020()) {
+ fs << "gl_FragColor.rgb = convertY410BT2020(gl_FragColor.rgb);";
+ }
+ } else {
+ fs << "gl_FragColor.rgb = color.rgb;";
+ fs << "gl_FragColor.a = 1.0;";
+ }
+ if (needs.isOpaque()) {
+ fs << "gl_FragColor.a = 1.0;";
+ }
+ if (needs.hasAlpha()) {
+ // modulate the current alpha value with alpha set
+ if (needs.isPremultiplied()) {
+ // ... and the color too if we're premultiplied
+ fs << "gl_FragColor *= color.a;";
+ } else {
+ fs << "gl_FragColor.a *= color.a;";
+ }
+ }
+ }
+
+ if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
+ if (!needs.isOpaque() && needs.isPremultiplied()) {
+ // un-premultiply if needed before linearization
+ // avoid divide by 0 by adding 0.5/256 to the alpha channel
+ fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);";
+ }
+ fs << "gl_FragColor.rgb = "
+ "OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))));";
+ if (!needs.isOpaque() && needs.isPremultiplied()) {
+ // and re-premultiply if needed after gamma correction
+ fs << "gl_FragColor.rgb = gl_FragColor.rgb * (gl_FragColor.a + 0.0019);";
+ }
+ }
+
+ if (needs.hasRoundedCorners()) {
+ if (needs.isPremultiplied()) {
+ fs << "gl_FragColor *= vec4(applyCornerRadius(outCropCoords));";
+ } else {
+ fs << "gl_FragColor.a *= applyCornerRadius(outCropCoords);";
+ }
+ }
+
+ fs << dedent << "}";
+ return fs.getString();
+}
+
+std::unique_ptr<Program> ProgramCache::generateProgram(const Key& needs) {
+ ATRACE_CALL();
+
+ // vertex shader
+ String8 vs = generateVertexShader(needs);
+
+ // fragment shader
+ String8 fs = generateFragmentShader(needs);
+
+ return std::make_unique<Program>(needs, vs.string(), fs.string());
+}
+
+void ProgramCache::useProgram(EGLContext context, const Description& description) {
+ // generate the key for the shader based on the description
+ Key needs(computeKey(description));
+
+ // look-up the program in the cache
+ auto& cache = mCaches[context];
+ auto it = cache.find(needs);
+ if (it == cache.end()) {
+ // we didn't find our program, so generate one...
+ nsecs_t time = systemTime();
+ it = cache.emplace(needs, generateProgram(needs)).first;
+ time = systemTime() - time;
+
+ ALOGV(">>> generated new program for context %p: needs=%08X, time=%u ms (%zu programs)",
+ context, needs.mKey, uint32_t(ns2ms(time)), cache.size());
+ }
+
+ // here we have a suitable program for this description
+ std::unique_ptr<Program>& program = it->second;
+ if (program->isValid()) {
+ program->use();
+ program->setUniforms(description);
+ }
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/gl/ProgramCache.h b/media/libstagefright/renderfright/gl/ProgramCache.h
new file mode 100644
index 0000000..901e631
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/ProgramCache.h
@@ -0,0 +1,228 @@
+/*
+ * 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.
+ */
+
+#ifndef SF_RENDER_ENGINE_PROGRAMCACHE_H
+#define SF_RENDER_ENGINE_PROGRAMCACHE_H
+
+#include <memory>
+#include <unordered_map>
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <renderengine/private/Description.h>
+#include <utils/Singleton.h>
+#include <utils/TypeHelpers.h>
+
+namespace android {
+
+class String8;
+
+namespace renderengine {
+
+struct Description;
+
+namespace gl {
+
+class Formatter;
+class Program;
+
+/*
+ * This class generates GLSL programs suitable to handle a given
+ * Description. It's responsible for figuring out what to
+ * generate from a Description.
+ * It also maintains a cache of these Programs.
+ */
+class ProgramCache : public Singleton<ProgramCache> {
+public:
+ /*
+ * Key is used to retrieve a Program in the cache.
+ * A Key is generated from a Description.
+ */
+ class Key {
+ friend class ProgramCache;
+ typedef uint32_t key_t;
+ key_t mKey;
+
+ public:
+ enum {
+ BLEND_SHIFT = 0,
+ BLEND_MASK = 1 << BLEND_SHIFT,
+ BLEND_PREMULT = 1 << BLEND_SHIFT,
+ BLEND_NORMAL = 0 << BLEND_SHIFT,
+
+ OPACITY_SHIFT = 1,
+ OPACITY_MASK = 1 << OPACITY_SHIFT,
+ OPACITY_OPAQUE = 1 << OPACITY_SHIFT,
+ OPACITY_TRANSLUCENT = 0 << OPACITY_SHIFT,
+
+ ALPHA_SHIFT = 2,
+ ALPHA_MASK = 1 << ALPHA_SHIFT,
+ ALPHA_LT_ONE = 1 << ALPHA_SHIFT,
+ ALPHA_EQ_ONE = 0 << ALPHA_SHIFT,
+
+ TEXTURE_SHIFT = 3,
+ TEXTURE_MASK = 3 << TEXTURE_SHIFT,
+ TEXTURE_OFF = 0 << TEXTURE_SHIFT,
+ TEXTURE_EXT = 1 << TEXTURE_SHIFT,
+ TEXTURE_2D = 2 << TEXTURE_SHIFT,
+
+ ROUNDED_CORNERS_SHIFT = 5,
+ ROUNDED_CORNERS_MASK = 1 << ROUNDED_CORNERS_SHIFT,
+ ROUNDED_CORNERS_OFF = 0 << ROUNDED_CORNERS_SHIFT,
+ ROUNDED_CORNERS_ON = 1 << ROUNDED_CORNERS_SHIFT,
+
+ INPUT_TRANSFORM_MATRIX_SHIFT = 6,
+ INPUT_TRANSFORM_MATRIX_MASK = 1 << INPUT_TRANSFORM_MATRIX_SHIFT,
+ INPUT_TRANSFORM_MATRIX_OFF = 0 << INPUT_TRANSFORM_MATRIX_SHIFT,
+ INPUT_TRANSFORM_MATRIX_ON = 1 << INPUT_TRANSFORM_MATRIX_SHIFT,
+
+ OUTPUT_TRANSFORM_MATRIX_SHIFT = 7,
+ OUTPUT_TRANSFORM_MATRIX_MASK = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
+ OUTPUT_TRANSFORM_MATRIX_OFF = 0 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
+ OUTPUT_TRANSFORM_MATRIX_ON = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
+
+ INPUT_TF_SHIFT = 8,
+ INPUT_TF_MASK = 3 << INPUT_TF_SHIFT,
+ INPUT_TF_LINEAR = 0 << INPUT_TF_SHIFT,
+ INPUT_TF_SRGB = 1 << INPUT_TF_SHIFT,
+ INPUT_TF_ST2084 = 2 << INPUT_TF_SHIFT,
+ INPUT_TF_HLG = 3 << INPUT_TF_SHIFT,
+
+ OUTPUT_TF_SHIFT = 10,
+ OUTPUT_TF_MASK = 3 << OUTPUT_TF_SHIFT,
+ OUTPUT_TF_LINEAR = 0 << OUTPUT_TF_SHIFT,
+ OUTPUT_TF_SRGB = 1 << OUTPUT_TF_SHIFT,
+ OUTPUT_TF_ST2084 = 2 << OUTPUT_TF_SHIFT,
+ OUTPUT_TF_HLG = 3 << OUTPUT_TF_SHIFT,
+
+ Y410_BT2020_SHIFT = 12,
+ Y410_BT2020_MASK = 1 << Y410_BT2020_SHIFT,
+ Y410_BT2020_OFF = 0 << Y410_BT2020_SHIFT,
+ Y410_BT2020_ON = 1 << Y410_BT2020_SHIFT,
+
+ SHADOW_SHIFT = 13,
+ SHADOW_MASK = 1 << SHADOW_SHIFT,
+ SHADOW_OFF = 0 << SHADOW_SHIFT,
+ SHADOW_ON = 1 << SHADOW_SHIFT,
+ };
+
+ inline Key() : mKey(0) {}
+ inline Key(const Key& rhs) : mKey(rhs.mKey) {}
+
+ inline Key& set(key_t mask, key_t value) {
+ mKey = (mKey & ~mask) | value;
+ return *this;
+ }
+
+ inline bool isTexturing() const { return (mKey & TEXTURE_MASK) != TEXTURE_OFF; }
+ inline bool hasTextureCoords() const { return isTexturing() && !drawShadows(); }
+ inline int getTextureTarget() const { return (mKey & TEXTURE_MASK); }
+ inline bool isPremultiplied() const { return (mKey & BLEND_MASK) == BLEND_PREMULT; }
+ inline bool isOpaque() const { return (mKey & OPACITY_MASK) == OPACITY_OPAQUE; }
+ inline bool hasAlpha() const { return (mKey & ALPHA_MASK) == ALPHA_LT_ONE; }
+ inline bool hasRoundedCorners() const {
+ return (mKey & ROUNDED_CORNERS_MASK) == ROUNDED_CORNERS_ON;
+ }
+ inline bool drawShadows() const { return (mKey & SHADOW_MASK) == SHADOW_ON; }
+ inline bool hasInputTransformMatrix() const {
+ return (mKey & INPUT_TRANSFORM_MATRIX_MASK) == INPUT_TRANSFORM_MATRIX_ON;
+ }
+ inline bool hasOutputTransformMatrix() const {
+ return (mKey & OUTPUT_TRANSFORM_MATRIX_MASK) == OUTPUT_TRANSFORM_MATRIX_ON;
+ }
+ inline bool hasTransformMatrix() const {
+ return hasInputTransformMatrix() || hasOutputTransformMatrix();
+ }
+ inline int getInputTF() const { return (mKey & INPUT_TF_MASK); }
+ inline int getOutputTF() const { return (mKey & OUTPUT_TF_MASK); }
+
+ // When HDR and non-HDR contents are mixed, or different types of HDR contents are
+ // mixed, we will do a tone mapping process to tone map the input content to output
+ // content. Currently, the following conversions handled, they are:
+ // * SDR -> HLG
+ // * SDR -> PQ
+ // * HLG -> PQ
+ inline bool needsToneMapping() const {
+ int inputTF = getInputTF();
+ int outputTF = getOutputTF();
+
+ // Return false when converting from SDR to SDR.
+ if (inputTF == Key::INPUT_TF_SRGB && outputTF == Key::OUTPUT_TF_LINEAR) {
+ return false;
+ }
+ if (inputTF == Key::INPUT_TF_LINEAR && outputTF == Key::OUTPUT_TF_SRGB) {
+ return false;
+ }
+
+ inputTF >>= Key::INPUT_TF_SHIFT;
+ outputTF >>= Key::OUTPUT_TF_SHIFT;
+ return inputTF != outputTF;
+ }
+ inline bool isY410BT2020() const { return (mKey & Y410_BT2020_MASK) == Y410_BT2020_ON; }
+
+ // for use by std::unordered_map
+
+ bool operator==(const Key& other) const { return mKey == other.mKey; }
+
+ struct Hash {
+ size_t operator()(const Key& key) const { return static_cast<size_t>(key.mKey); }
+ };
+ };
+
+ ProgramCache() = default;
+ ~ProgramCache() = default;
+
+ // Generate shaders to populate the cache
+ void primeCache(const EGLContext context, bool useColorManagement, bool toneMapperShaderOnly);
+
+ size_t getSize(const EGLContext context) { return mCaches[context].size(); }
+
+ // useProgram lookup a suitable program in the cache or generates one
+ // if none can be found.
+ void useProgram(const EGLContext context, const Description& description);
+
+private:
+ // compute a cache Key from a Description
+ static Key computeKey(const Description& description);
+ // Generate EOTF based from Key.
+ static void generateEOTF(Formatter& fs, const Key& needs);
+ // Generate necessary tone mapping methods for OOTF.
+ static void generateToneMappingProcess(Formatter& fs, const Key& needs);
+ // Generate OOTF based from Key.
+ static void generateOOTF(Formatter& fs, const Key& needs);
+ // Generate OETF based from Key.
+ static void generateOETF(Formatter& fs, const Key& needs);
+ // generates a program from the Key
+ static std::unique_ptr<Program> generateProgram(const Key& needs);
+ // generates the vertex shader from the Key
+ static String8 generateVertexShader(const Key& needs);
+ // generates the fragment shader from the Key
+ static String8 generateFragmentShader(const Key& needs);
+
+ // Key/Value map used for caching Programs. Currently the cache
+ // is never shrunk (and the GL program objects are never deleted).
+ std::unordered_map<EGLContext, std::unordered_map<Key, std::unique_ptr<Program>, Key::Hash>>
+ mCaches;
+};
+
+} // namespace gl
+} // namespace renderengine
+
+ANDROID_BASIC_TYPES_TRAITS(renderengine::gl::ProgramCache::Key)
+
+} // namespace android
+
+#endif /* SF_RENDER_ENGINE_PROGRAMCACHE_H */
diff --git a/media/libstagefright/renderfright/gl/filters/BlurFilter.cpp b/media/libstagefright/renderfright/gl/filters/BlurFilter.cpp
new file mode 100644
index 0000000..19f18c0
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/filters/BlurFilter.cpp
@@ -0,0 +1,268 @@
+/*
+ * 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "BlurFilter.h"
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES3/gl3.h>
+#include <GLES3/gl3ext.h>
+#include <ui/GraphicTypes.h>
+#include <cstdint>
+
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+BlurFilter::BlurFilter(GLESRenderEngine& engine)
+ : mEngine(engine),
+ mCompositionFbo(engine),
+ mPingFbo(engine),
+ mPongFbo(engine),
+ mMixProgram(engine),
+ mBlurProgram(engine) {
+ mMixProgram.compile(getVertexShader(), getMixFragShader());
+ mMPosLoc = mMixProgram.getAttributeLocation("aPosition");
+ mMUvLoc = mMixProgram.getAttributeLocation("aUV");
+ mMTextureLoc = mMixProgram.getUniformLocation("uTexture");
+ mMCompositionTextureLoc = mMixProgram.getUniformLocation("uCompositionTexture");
+ mMMixLoc = mMixProgram.getUniformLocation("uMix");
+
+ mBlurProgram.compile(getVertexShader(), getFragmentShader());
+ mBPosLoc = mBlurProgram.getAttributeLocation("aPosition");
+ mBUvLoc = mBlurProgram.getAttributeLocation("aUV");
+ mBTextureLoc = mBlurProgram.getUniformLocation("uTexture");
+ mBOffsetLoc = mBlurProgram.getUniformLocation("uOffset");
+
+ static constexpr auto size = 2.0f;
+ static constexpr auto translation = 1.0f;
+ const GLfloat vboData[] = {
+ // Vertex data
+ translation - size, -translation - size,
+ translation - size, -translation + size,
+ translation + size, -translation + size,
+ // UV data
+ 0.0f, 0.0f - translation,
+ 0.0f, size - translation,
+ size, size - translation
+ };
+ mMeshBuffer.allocateBuffers(vboData, 12 /* size */);
+}
+
+status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display, uint32_t radius) {
+ ATRACE_NAME("BlurFilter::setAsDrawTarget");
+ mRadius = radius;
+ mDisplayX = display.physicalDisplay.left;
+ mDisplayY = display.physicalDisplay.top;
+
+ if (mDisplayWidth < display.physicalDisplay.width() ||
+ mDisplayHeight < display.physicalDisplay.height()) {
+ ATRACE_NAME("BlurFilter::allocatingTextures");
+
+ mDisplayWidth = display.physicalDisplay.width();
+ mDisplayHeight = display.physicalDisplay.height();
+ mCompositionFbo.allocateBuffers(mDisplayWidth, mDisplayHeight);
+
+ const uint32_t fboWidth = floorf(mDisplayWidth * kFboScale);
+ const uint32_t fboHeight = floorf(mDisplayHeight * kFboScale);
+ mPingFbo.allocateBuffers(fboWidth, fboHeight);
+ mPongFbo.allocateBuffers(fboWidth, fboHeight);
+
+ if (mPingFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Invalid ping buffer");
+ return mPingFbo.getStatus();
+ }
+ if (mPongFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Invalid pong buffer");
+ return mPongFbo.getStatus();
+ }
+ if (mCompositionFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Invalid composition buffer");
+ return mCompositionFbo.getStatus();
+ }
+ if (!mBlurProgram.isValid()) {
+ ALOGE("Invalid shader");
+ return GL_INVALID_OPERATION;
+ }
+ }
+
+ mCompositionFbo.bind();
+ glViewport(0, 0, mCompositionFbo.getBufferWidth(), mCompositionFbo.getBufferHeight());
+ return NO_ERROR;
+}
+
+void BlurFilter::drawMesh(GLuint uv, GLuint position) {
+
+ glEnableVertexAttribArray(uv);
+ glEnableVertexAttribArray(position);
+ mMeshBuffer.bind();
+ glVertexAttribPointer(position, 2 /* size */, GL_FLOAT, GL_FALSE,
+ 2 * sizeof(GLfloat) /* stride */, 0 /* offset */);
+ glVertexAttribPointer(uv, 2 /* size */, GL_FLOAT, GL_FALSE, 0 /* stride */,
+ (GLvoid*)(6 * sizeof(GLfloat)) /* offset */);
+ mMeshBuffer.unbind();
+
+ // draw mesh
+ glDrawArrays(GL_TRIANGLES, 0 /* first */, 3 /* count */);
+}
+
+status_t BlurFilter::prepare() {
+ ATRACE_NAME("BlurFilter::prepare");
+
+ // Kawase is an approximation of Gaussian, but it behaves differently from it.
+ // A radius transformation is required for approximating them, and also to introduce
+ // non-integer steps, necessary to smoothly interpolate large radii.
+ const auto radius = mRadius / 6.0f;
+
+ // Calculate how many passes we'll do, based on the radius.
+ // Too many passes will make the operation expensive.
+ const auto passes = min(kMaxPasses, (uint32_t)ceil(radius));
+
+ const float radiusByPasses = radius / (float)passes;
+ const float stepX = radiusByPasses / (float)mCompositionFbo.getBufferWidth();
+ const float stepY = radiusByPasses / (float)mCompositionFbo.getBufferHeight();
+
+ // Let's start by downsampling and blurring the composited frame simultaneously.
+ mBlurProgram.useProgram();
+ glActiveTexture(GL_TEXTURE0);
+ glUniform1i(mBTextureLoc, 0);
+ glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
+ glUniform2f(mBOffsetLoc, stepX, stepY);
+ glViewport(0, 0, mPingFbo.getBufferWidth(), mPingFbo.getBufferHeight());
+ mPingFbo.bind();
+ drawMesh(mBUvLoc, mBPosLoc);
+
+ // And now we'll ping pong between our textures, to accumulate the result of various offsets.
+ GLFramebuffer* read = &mPingFbo;
+ GLFramebuffer* draw = &mPongFbo;
+ glViewport(0, 0, draw->getBufferWidth(), draw->getBufferHeight());
+ for (auto i = 1; i < passes; i++) {
+ ATRACE_NAME("BlurFilter::renderPass");
+ draw->bind();
+
+ glBindTexture(GL_TEXTURE_2D, read->getTextureName());
+ glUniform2f(mBOffsetLoc, stepX * i, stepY * i);
+
+ drawMesh(mBUvLoc, mBPosLoc);
+
+ // Swap buffers for next iteration
+ auto tmp = draw;
+ draw = read;
+ read = tmp;
+ }
+ mLastDrawTarget = read;
+
+ return NO_ERROR;
+}
+
+status_t BlurFilter::render(bool multiPass) {
+ ATRACE_NAME("BlurFilter::render");
+
+ // Now let's scale our blur up. It will be interpolated with the larger composited
+ // texture for the first frames, to hide downscaling artifacts.
+ GLfloat mix = fmin(1.0, mRadius / kMaxCrossFadeRadius);
+
+ // When doing multiple passes, we cannot try to read mCompositionFbo, given that we'll
+ // be writing onto it. Let's disable the crossfade, otherwise we'd need 1 extra frame buffer,
+ // as large as the screen size.
+ if (mix >= 1 || multiPass) {
+ mLastDrawTarget->bindAsReadBuffer();
+ glBlitFramebuffer(0, 0, mLastDrawTarget->getBufferWidth(),
+ mLastDrawTarget->getBufferHeight(), mDisplayX, mDisplayY, mDisplayWidth,
+ mDisplayHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+ return NO_ERROR;
+ }
+
+ mMixProgram.useProgram();
+ glUniform1f(mMMixLoc, mix);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, mLastDrawTarget->getTextureName());
+ glUniform1i(mMTextureLoc, 0);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
+ glUniform1i(mMCompositionTextureLoc, 1);
+
+ drawMesh(mMUvLoc, mMPosLoc);
+
+ glUseProgram(0);
+ glActiveTexture(GL_TEXTURE0);
+ mEngine.checkErrors("Drawing blur mesh");
+ return NO_ERROR;
+}
+
+string BlurFilter::getVertexShader() const {
+ return R"SHADER(#version 310 es
+ precision mediump float;
+
+ in vec2 aPosition;
+ in highp vec2 aUV;
+ out highp vec2 vUV;
+
+ void main() {
+ vUV = aUV;
+ gl_Position = vec4(aPosition, 0.0, 1.0);
+ }
+ )SHADER";
+}
+
+string BlurFilter::getFragmentShader() const {
+ return R"SHADER(#version 310 es
+ precision mediump float;
+
+ uniform sampler2D uTexture;
+ uniform vec2 uOffset;
+
+ in highp vec2 vUV;
+ out vec4 fragColor;
+
+ void main() {
+ fragColor = texture(uTexture, vUV, 0.0);
+ fragColor += texture(uTexture, vUV + vec2( uOffset.x, uOffset.y), 0.0);
+ fragColor += texture(uTexture, vUV + vec2( uOffset.x, -uOffset.y), 0.0);
+ fragColor += texture(uTexture, vUV + vec2(-uOffset.x, uOffset.y), 0.0);
+ fragColor += texture(uTexture, vUV + vec2(-uOffset.x, -uOffset.y), 0.0);
+
+ fragColor = vec4(fragColor.rgb * 0.2, 1.0);
+ }
+ )SHADER";
+}
+
+string BlurFilter::getMixFragShader() const {
+ string shader = R"SHADER(#version 310 es
+ precision mediump float;
+
+ in highp vec2 vUV;
+ out vec4 fragColor;
+
+ uniform sampler2D uCompositionTexture;
+ uniform sampler2D uTexture;
+ uniform float uMix;
+
+ void main() {
+ vec4 blurred = texture(uTexture, vUV);
+ vec4 composition = texture(uCompositionTexture, vUV);
+ fragColor = mix(composition, blurred, uMix);
+ }
+ )SHADER";
+ return shader;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/gl/filters/BlurFilter.h b/media/libstagefright/renderfright/gl/filters/BlurFilter.h
new file mode 100644
index 0000000..593a8fd
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/filters/BlurFilter.h
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <ui/GraphicTypes.h>
+#include "../GLESRenderEngine.h"
+#include "../GLFramebuffer.h"
+#include "../GLVertexBuffer.h"
+#include "GenericProgram.h"
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+/**
+ * This is an implementation of a Kawase blur, as described in here:
+ * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/
+ * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
+ */
+class BlurFilter {
+public:
+ // Downsample FBO to improve performance
+ static constexpr float kFboScale = 0.25f;
+ // Maximum number of render passes
+ static constexpr uint32_t kMaxPasses = 4;
+ // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
+ // image, up to this radius.
+ static constexpr float kMaxCrossFadeRadius = 30.0f;
+
+ explicit BlurFilter(GLESRenderEngine& engine);
+ virtual ~BlurFilter(){};
+
+ // Set up render targets, redirecting output to offscreen texture.
+ status_t setAsDrawTarget(const DisplaySettings&, uint32_t radius);
+ // Execute blur passes, rendering to offscreen texture.
+ status_t prepare();
+ // Render blur to the bound framebuffer (screen).
+ status_t render(bool multiPass);
+
+private:
+ uint32_t mRadius;
+ void drawMesh(GLuint uv, GLuint position);
+ string getVertexShader() const;
+ string getFragmentShader() const;
+ string getMixFragShader() const;
+
+ GLESRenderEngine& mEngine;
+ // Frame buffer holding the composited background.
+ GLFramebuffer mCompositionFbo;
+ // Frame buffers holding the blur passes.
+ GLFramebuffer mPingFbo;
+ GLFramebuffer mPongFbo;
+ uint32_t mDisplayWidth = 0;
+ uint32_t mDisplayHeight = 0;
+ uint32_t mDisplayX = 0;
+ uint32_t mDisplayY = 0;
+ // Buffer holding the final blur pass.
+ GLFramebuffer* mLastDrawTarget;
+
+ // VBO containing vertex and uv data of a fullscreen triangle.
+ GLVertexBuffer mMeshBuffer;
+
+ GenericProgram mMixProgram;
+ GLuint mMPosLoc;
+ GLuint mMUvLoc;
+ GLuint mMMixLoc;
+ GLuint mMTextureLoc;
+ GLuint mMCompositionTextureLoc;
+
+ GenericProgram mBlurProgram;
+ GLuint mBPosLoc;
+ GLuint mBUvLoc;
+ GLuint mBTextureLoc;
+ GLuint mBOffsetLoc;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/gl/filters/GenericProgram.cpp b/media/libstagefright/renderfright/gl/filters/GenericProgram.cpp
new file mode 100644
index 0000000..bb35889
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/filters/GenericProgram.cpp
@@ -0,0 +1,122 @@
+/*
+ * 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 "GenericProgram.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GenericProgram::GenericProgram(GLESRenderEngine& engine) : mEngine(engine) {}
+
+GenericProgram::~GenericProgram() {
+ if (mVertexShaderHandle != 0) {
+ if (mProgramHandle != 0) {
+ glDetachShader(mProgramHandle, mVertexShaderHandle);
+ }
+ glDeleteShader(mVertexShaderHandle);
+ }
+
+ if (mFragmentShaderHandle != 0) {
+ if (mProgramHandle != 0) {
+ glDetachShader(mProgramHandle, mFragmentShaderHandle);
+ }
+ glDeleteShader(mFragmentShaderHandle);
+ }
+
+ if (mProgramHandle != 0) {
+ glDeleteProgram(mProgramHandle);
+ }
+}
+
+void GenericProgram::compile(string vertexShader, string fragmentShader) {
+ mVertexShaderHandle = compileShader(GL_VERTEX_SHADER, vertexShader);
+ mFragmentShaderHandle = compileShader(GL_FRAGMENT_SHADER, fragmentShader);
+ if (mVertexShaderHandle == 0 || mFragmentShaderHandle == 0) {
+ ALOGE("Aborting program creation.");
+ return;
+ }
+ mProgramHandle = createAndLink(mVertexShaderHandle, mFragmentShaderHandle);
+ mEngine.checkErrors("Linking program");
+}
+
+void GenericProgram::useProgram() const {
+ glUseProgram(mProgramHandle);
+}
+
+GLuint GenericProgram::compileShader(GLuint type, string src) const {
+ const GLuint shader = glCreateShader(type);
+ if (shader == 0) {
+ mEngine.checkErrors("Creating shader");
+ return 0;
+ }
+ const GLchar* charSrc = (const GLchar*)src.c_str();
+ glShaderSource(shader, 1, &charSrc, nullptr);
+ glCompileShader(shader);
+
+ GLint isCompiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
+ if (isCompiled == GL_FALSE) {
+ GLint maxLength = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
+ string errorLog;
+ errorLog.reserve(maxLength);
+ glGetShaderInfoLog(shader, maxLength, &maxLength, errorLog.data());
+ glDeleteShader(shader);
+ ALOGE("Error compiling shader: %s", errorLog.c_str());
+ return 0;
+ }
+ return shader;
+}
+GLuint GenericProgram::createAndLink(GLuint vertexShader, GLuint fragmentShader) const {
+ const GLuint program = glCreateProgram();
+ mEngine.checkErrors("Creating program");
+
+ glAttachShader(program, vertexShader);
+ glAttachShader(program, fragmentShader);
+ glLinkProgram(program);
+ mEngine.checkErrors("Linking program");
+ return program;
+}
+
+GLuint GenericProgram::getUniformLocation(const string name) const {
+ if (mProgramHandle == 0) {
+ ALOGE("Can't get location of %s on an invalid program.", name.c_str());
+ return -1;
+ }
+ return glGetUniformLocation(mProgramHandle, (const GLchar*)name.c_str());
+}
+
+GLuint GenericProgram::getAttributeLocation(const string name) const {
+ if (mProgramHandle == 0) {
+ ALOGE("Can't get location of %s on an invalid program.", name.c_str());
+ return -1;
+ }
+ return glGetAttribLocation(mProgramHandle, (const GLchar*)name.c_str());
+}
+
+bool GenericProgram::isValid() const {
+ return mProgramHandle != 0;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/gl/filters/GenericProgram.h b/media/libstagefright/renderfright/gl/filters/GenericProgram.h
new file mode 100644
index 0000000..6da2a5a
--- /dev/null
+++ b/media/libstagefright/renderfright/gl/filters/GenericProgram.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <ui/GraphicTypes.h>
+#include "../GLESRenderEngine.h"
+#include "../GLFramebuffer.h"
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GenericProgram {
+public:
+ explicit GenericProgram(GLESRenderEngine& renderEngine);
+ ~GenericProgram();
+ void compile(string vertexShader, string fragmentShader);
+ bool isValid() const;
+ void useProgram() const;
+ GLuint getAttributeLocation(const string name) const;
+ GLuint getUniformLocation(const string name) const;
+
+private:
+ GLuint compileShader(GLuint type, const string src) const;
+ GLuint createAndLink(GLuint vertexShader, GLuint fragmentShader) const;
+
+ GLESRenderEngine& mEngine;
+ GLuint mVertexShaderHandle = 0;
+ GLuint mFragmentShaderHandle = 0;
+ GLuint mProgramHandle = 0;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/include/renderengine/DisplaySettings.h b/media/libstagefright/renderfright/include/renderengine/DisplaySettings.h
new file mode 100644
index 0000000..ca16d2c
--- /dev/null
+++ b/media/libstagefright/renderfright/include/renderengine/DisplaySettings.h
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <iosfwd>
+
+#include <math/mat4.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+
+namespace android {
+namespace renderengine {
+
+// DisplaySettings contains the settings that are applicable when drawing all
+// layers for a given display.
+struct DisplaySettings {
+ // Rectangle describing the physical display. We will project from the
+ // logical clip onto this rectangle.
+ Rect physicalDisplay = Rect::INVALID_RECT;
+
+ // Rectangle bounded by the x,y- clipping planes in the logical display, so
+ // that the orthographic projection matrix can be computed. When
+ // constructing this matrix, z-coordinate bound are assumed to be at z=0 and
+ // z=1.
+ Rect clip = Rect::INVALID_RECT;
+
+ // Maximum luminance pulled from the display's HDR capabilities.
+ float maxLuminance = 1.0f;
+
+ // Output dataspace that will be populated if wide color gamut is used, or
+ // DataSpace::UNKNOWN otherwise.
+ ui::Dataspace outputDataspace = ui::Dataspace::UNKNOWN;
+
+ // Additional color transform to apply in linear space after transforming
+ // to the output dataspace.
+ mat4 colorTransform = mat4();
+
+ // Region that will be cleared to (0, 0, 0, 1) prior to rendering.
+ // This is specified in layer-stack space.
+ Region clearRegion = Region::INVALID_REGION;
+
+ // An additional orientation flag to be applied after clipping the output.
+ // By way of example, this may be used for supporting fullscreen screenshot
+ // capture of a device in landscape while the buffer is in portrait
+ // orientation.
+ uint32_t orientation = ui::Transform::ROT_0;
+};
+
+static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {
+ return lhs.physicalDisplay == rhs.physicalDisplay && lhs.clip == rhs.clip &&
+ lhs.maxLuminance == rhs.maxLuminance && lhs.outputDataspace == rhs.outputDataspace &&
+ lhs.colorTransform == rhs.colorTransform &&
+ lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.orientation == rhs.orientation;
+}
+
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const DisplaySettings& settings, ::std::ostream* os) {
+ *os << "DisplaySettings {";
+ *os << "\n .physicalDisplay = ";
+ PrintTo(settings.physicalDisplay, os);
+ *os << "\n .clip = ";
+ PrintTo(settings.clip, os);
+ *os << "\n .maxLuminance = " << settings.maxLuminance;
+ *os << "\n .outputDataspace = ";
+ PrintTo(settings.outputDataspace, os);
+ *os << "\n .colorTransform = " << settings.colorTransform;
+ *os << "\n .clearRegion = ";
+ PrintTo(settings.clearRegion, os);
+ *os << "\n .orientation = " << settings.orientation;
+ *os << "\n}";
+}
+
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/include/renderengine/Framebuffer.h b/media/libstagefright/renderfright/include/renderengine/Framebuffer.h
new file mode 100644
index 0000000..6511127
--- /dev/null
+++ b/media/libstagefright/renderfright/include/renderengine/Framebuffer.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace renderengine {
+
+class Framebuffer {
+public:
+ virtual ~Framebuffer() = default;
+
+ virtual bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
+ const bool useFramebufferCache) = 0;
+};
+
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/include/renderengine/Image.h b/media/libstagefright/renderfright/include/renderengine/Image.h
new file mode 100644
index 0000000..3bb4731
--- /dev/null
+++ b/media/libstagefright/renderfright/include/renderengine/Image.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 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
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace renderengine {
+
+class Image {
+public:
+ virtual ~Image() = default;
+ virtual bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) = 0;
+};
+
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/include/renderengine/LayerSettings.h b/media/libstagefright/renderfright/include/renderengine/LayerSettings.h
new file mode 100644
index 0000000..95e9367
--- /dev/null
+++ b/media/libstagefright/renderfright/include/renderengine/LayerSettings.h
@@ -0,0 +1,258 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <iosfwd>
+
+#include <math/mat4.h>
+#include <math/vec3.h>
+#include <renderengine/Texture.h>
+#include <ui/Fence.h>
+#include <ui/FloatRect.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+
+namespace android {
+namespace renderengine {
+
+// Metadata describing the input buffer to render from.
+struct Buffer {
+ // Buffer containing the image that we will render.
+ // If buffer == nullptr, then the rest of the fields in this struct will be
+ // ignored.
+ sp<GraphicBuffer> buffer = nullptr;
+
+ // Fence that will fire when the buffer is ready to be bound.
+ sp<Fence> fence = nullptr;
+
+ // Texture identifier to bind the external texture to.
+ // TODO(alecmouri): This is GL-specific...make the type backend-agnostic.
+ uint32_t textureName = 0;
+
+ // Whether to use filtering when rendering the texture.
+ bool useTextureFiltering = false;
+
+ // Transform matrix to apply to texture coordinates.
+ mat4 textureTransform = mat4();
+
+ // Whether to use pre-multiplied alpha.
+ bool usePremultipliedAlpha = true;
+
+ // Override flag that alpha for each pixel in the buffer *must* be 1.0.
+ // LayerSettings::alpha is still used if isOpaque==true - this flag only
+ // overrides the alpha channel of the buffer.
+ bool isOpaque = false;
+
+ // HDR color-space setting for Y410.
+ bool isY410BT2020 = false;
+ float maxMasteringLuminance = 0.0;
+ float maxContentLuminance = 0.0;
+};
+
+// Metadata describing the layer geometry.
+struct Geometry {
+ // Boundaries of the layer.
+ FloatRect boundaries = FloatRect();
+
+ // Transform matrix to apply to mesh coordinates.
+ mat4 positionTransform = mat4();
+
+ // Radius of rounded corners, if greater than 0. Otherwise, this layer's
+ // corners are not rounded.
+ // Having corner radius will force GPU composition on the layer and its children, drawing it
+ // with a special shader. The shader will receive the radius and the crop rectangle as input,
+ // modifying the opacity of the destination texture, multiplying it by a number between 0 and 1.
+ // We query Layer#getRoundedCornerState() to retrieve the radius as well as the rounded crop
+ // rectangle to figure out how to apply the radius for this layer. The crop rectangle will be
+ // in local layer coordinate space, so we have to take the layer transform into account when
+ // walking up the tree.
+ float roundedCornersRadius = 0.0;
+
+ // Rectangle within which corners will be rounded.
+ FloatRect roundedCornersCrop = FloatRect();
+};
+
+// Descriptor of the source pixels for this layer.
+struct PixelSource {
+ // Source buffer
+ Buffer buffer = Buffer();
+
+ // The solid color with which to fill the layer.
+ // This should only be populated if we don't render from an application
+ // buffer.
+ half3 solidColor = half3(0.0f, 0.0f, 0.0f);
+};
+
+/*
+ * Contains the configuration for the shadows drawn by single layer. Shadow follows
+ * material design guidelines.
+ */
+struct ShadowSettings {
+ // Color to the ambient shadow. The alpha is premultiplied.
+ vec4 ambientColor = vec4();
+
+ // Color to the spot shadow. The alpha is premultiplied. The position of the spot shadow
+ // depends on the light position.
+ vec4 spotColor = vec4();
+
+ // Position of the light source used to cast the spot shadow.
+ vec3 lightPos = vec3();
+
+ // Radius of the spot light source. Smaller radius will have sharper edges,
+ // larger radius will have softer shadows
+ float lightRadius = 0.f;
+
+ // Length of the cast shadow. If length is <= 0.f no shadows will be drawn.
+ float length = 0.f;
+
+ // If true fill in the casting layer is translucent and the shadow needs to fill the bounds.
+ // Otherwise the shadow will only be drawn around the edges of the casting layer.
+ bool casterIsTranslucent = false;
+};
+
+// The settings that RenderEngine requires for correctly rendering a Layer.
+struct LayerSettings {
+ // Geometry information
+ Geometry geometry = Geometry();
+
+ // Source pixels for this layer.
+ PixelSource source = PixelSource();
+
+ // Alpha option to blend with the source pixels
+ half alpha = half(0.0);
+
+ // Color space describing how the source pixels should be interpreted.
+ ui::Dataspace sourceDataspace = ui::Dataspace::UNKNOWN;
+
+ // Additional layer-specific color transform to be applied before the global
+ // transform.
+ mat4 colorTransform = mat4();
+
+ // True if blending will be forced to be disabled.
+ bool disableBlending = false;
+
+ ShadowSettings shadow;
+
+ int backgroundBlurRadius = 0;
+};
+
+// Keep in sync with custom comparison function in
+// compositionengine/impl/ClientCompositionRequestCache.cpp
+static inline bool operator==(const Buffer& lhs, const Buffer& rhs) {
+ return lhs.buffer == rhs.buffer && lhs.fence == rhs.fence &&
+ lhs.textureName == rhs.textureName &&
+ lhs.useTextureFiltering == rhs.useTextureFiltering &&
+ lhs.textureTransform == rhs.textureTransform &&
+ lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha &&
+ lhs.isOpaque == rhs.isOpaque && lhs.isY410BT2020 == rhs.isY410BT2020 &&
+ lhs.maxMasteringLuminance == rhs.maxMasteringLuminance &&
+ lhs.maxContentLuminance == rhs.maxContentLuminance;
+}
+
+static inline bool operator==(const Geometry& lhs, const Geometry& rhs) {
+ return lhs.boundaries == rhs.boundaries && lhs.positionTransform == rhs.positionTransform &&
+ lhs.roundedCornersRadius == rhs.roundedCornersRadius &&
+ lhs.roundedCornersCrop == rhs.roundedCornersCrop;
+}
+
+static inline bool operator==(const PixelSource& lhs, const PixelSource& rhs) {
+ return lhs.buffer == rhs.buffer && lhs.solidColor == rhs.solidColor;
+}
+
+static inline bool operator==(const ShadowSettings& lhs, const ShadowSettings& rhs) {
+ return lhs.ambientColor == rhs.ambientColor && lhs.spotColor == rhs.spotColor &&
+ lhs.lightPos == rhs.lightPos && lhs.lightRadius == rhs.lightRadius &&
+ lhs.length == rhs.length && lhs.casterIsTranslucent == rhs.casterIsTranslucent;
+}
+
+static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs) {
+ return lhs.geometry == rhs.geometry && lhs.source == rhs.source && lhs.alpha == rhs.alpha &&
+ lhs.sourceDataspace == rhs.sourceDataspace &&
+ lhs.colorTransform == rhs.colorTransform &&
+ lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
+ lhs.backgroundBlurRadius == rhs.backgroundBlurRadius;
+}
+
+// Defining PrintTo helps with Google Tests.
+
+static inline void PrintTo(const Buffer& settings, ::std::ostream* os) {
+ *os << "Buffer {";
+ *os << "\n .buffer = " << settings.buffer.get();
+ *os << "\n .fence = " << settings.fence.get();
+ *os << "\n .textureName = " << settings.textureName;
+ *os << "\n .useTextureFiltering = " << settings.useTextureFiltering;
+ *os << "\n .textureTransform = " << settings.textureTransform;
+ *os << "\n .usePremultipliedAlpha = " << settings.usePremultipliedAlpha;
+ *os << "\n .isOpaque = " << settings.isOpaque;
+ *os << "\n .isY410BT2020 = " << settings.isY410BT2020;
+ *os << "\n .maxMasteringLuminance = " << settings.maxMasteringLuminance;
+ *os << "\n .maxContentLuminance = " << settings.maxContentLuminance;
+ *os << "\n}";
+}
+
+static inline void PrintTo(const Geometry& settings, ::std::ostream* os) {
+ *os << "Geometry {";
+ *os << "\n .boundaries = ";
+ PrintTo(settings.boundaries, os);
+ *os << "\n .positionTransform = " << settings.positionTransform;
+ *os << "\n .roundedCornersRadius = " << settings.roundedCornersRadius;
+ *os << "\n .roundedCornersCrop = ";
+ PrintTo(settings.roundedCornersCrop, os);
+ *os << "\n}";
+}
+
+static inline void PrintTo(const PixelSource& settings, ::std::ostream* os) {
+ *os << "PixelSource {";
+ *os << "\n .buffer = ";
+ PrintTo(settings.buffer, os);
+ *os << "\n .solidColor = " << settings.solidColor;
+ *os << "\n}";
+}
+
+static inline void PrintTo(const ShadowSettings& settings, ::std::ostream* os) {
+ *os << "ShadowSettings {";
+ *os << "\n .ambientColor = " << settings.ambientColor;
+ *os << "\n .spotColor = " << settings.spotColor;
+ *os << "\n .lightPos = " << settings.lightPos;
+ *os << "\n .lightRadius = " << settings.lightRadius;
+ *os << "\n .length = " << settings.length;
+ *os << "\n .casterIsTranslucent = " << settings.casterIsTranslucent;
+ *os << "\n}";
+}
+
+static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) {
+ *os << "LayerSettings {";
+ *os << "\n .geometry = ";
+ PrintTo(settings.geometry, os);
+ *os << "\n .source = ";
+ PrintTo(settings.source, os);
+ *os << "\n .alpha = " << settings.alpha;
+ *os << "\n .sourceDataspace = ";
+ PrintTo(settings.sourceDataspace, os);
+ *os << "\n .colorTransform = " << settings.colorTransform;
+ *os << "\n .disableBlending = " << settings.disableBlending;
+ *os << "\n .backgroundBlurRadius = " << settings.backgroundBlurRadius;
+ *os << "\n .shadow = ";
+ PrintTo(settings.shadow, os);
+ *os << "\n}";
+}
+
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/include/renderengine/Mesh.h b/media/libstagefright/renderfright/include/renderengine/Mesh.h
new file mode 100644
index 0000000..167f13f
--- /dev/null
+++ b/media/libstagefright/renderfright/include/renderengine/Mesh.h
@@ -0,0 +1,205 @@
+/*
+ * 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.
+ */
+
+#ifndef SF_RENDER_ENGINE_MESH_H
+#define SF_RENDER_ENGINE_MESH_H
+
+#include <vector>
+
+#include <stdint.h>
+
+namespace android {
+namespace renderengine {
+
+class Mesh {
+public:
+ class Builder;
+
+ enum Primitive {
+ TRIANGLES = 0x0004, // GL_TRIANGLES
+ TRIANGLE_STRIP = 0x0005, // GL_TRIANGLE_STRIP
+ TRIANGLE_FAN = 0x0006 // GL_TRIANGLE_FAN
+ };
+
+ ~Mesh() = default;
+
+ /*
+ * VertexArray handles the stride automatically.
+ */
+ template <typename TYPE>
+ class VertexArray {
+ friend class Mesh;
+ float* mData;
+ size_t mStride;
+ size_t mOffset = 0;
+ VertexArray(float* data, size_t stride) : mData(data), mStride(stride) {}
+
+ public:
+ // Returns a vertex array at an offset so its easier to append attributes from
+ // multiple sources.
+ VertexArray(VertexArray<TYPE>& other, size_t offset)
+ : mData(other.mData), mStride(other.mStride), mOffset(offset) {}
+
+ TYPE& operator[](size_t index) {
+ return *reinterpret_cast<TYPE*>(&mData[(index + mOffset) * mStride]);
+ }
+ TYPE const& operator[](size_t index) const {
+ return *reinterpret_cast<TYPE const*>(&mData[(index + mOffset) * mStride]);
+ }
+ };
+
+ template <typename TYPE>
+ VertexArray<TYPE> getPositionArray() {
+ return VertexArray<TYPE>(getPositions(), mStride);
+ }
+
+ template <typename TYPE>
+ VertexArray<TYPE> getTexCoordArray() {
+ return VertexArray<TYPE>(getTexCoords(), mStride);
+ }
+
+ template <typename TYPE>
+ VertexArray<TYPE> getCropCoordArray() {
+ return VertexArray<TYPE>(getCropCoords(), mStride);
+ }
+
+ template <typename TYPE>
+ VertexArray<TYPE> getShadowColorArray() {
+ return VertexArray<TYPE>(getShadowColor(), mStride);
+ }
+
+ template <typename TYPE>
+ VertexArray<TYPE> getShadowParamsArray() {
+ return VertexArray<TYPE>(getShadowParams(), mStride);
+ }
+
+ uint16_t* getIndicesArray() { return getIndices(); }
+
+ Primitive getPrimitive() const;
+
+ // returns a pointer to the vertices positions
+ float const* getPositions() const;
+
+ // returns a pointer to the vertices texture coordinates
+ float const* getTexCoords() const;
+
+ // returns a pointer to the vertices crop coordinates
+ float const* getCropCoords() const;
+
+ // returns a pointer to colors
+ float const* getShadowColor() const;
+
+ // returns a pointer to the shadow params
+ float const* getShadowParams() const;
+
+ // returns a pointer to indices
+ uint16_t const* getIndices() const;
+
+ // number of vertices in this mesh
+ size_t getVertexCount() const;
+
+ // dimension of vertices
+ size_t getVertexSize() const;
+
+ // dimension of texture coordinates
+ size_t getTexCoordsSize() const;
+
+ size_t getShadowParamsSize() const;
+
+ size_t getShadowColorSize() const;
+
+ size_t getIndexCount() const;
+
+ // return stride in bytes
+ size_t getByteStride() const;
+
+ // return stride in floats
+ size_t getStride() const;
+
+private:
+ Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize,
+ size_t cropCoordsSize, size_t shadowColorSize, size_t shadowParamsSize, size_t indexCount);
+ Mesh(const Mesh&);
+ Mesh& operator=(const Mesh&);
+ Mesh const& operator=(const Mesh&) const;
+
+ float* getPositions();
+ float* getTexCoords();
+ float* getCropCoords();
+ float* getShadowColor();
+ float* getShadowParams();
+ uint16_t* getIndices();
+
+ std::vector<float> mVertices;
+ size_t mVertexCount;
+ size_t mVertexSize;
+ size_t mTexCoordsSize;
+ size_t mCropCoordsSize;
+ size_t mShadowColorSize;
+ size_t mShadowParamsSize;
+ size_t mStride;
+ Primitive mPrimitive;
+ std::vector<uint16_t> mIndices;
+ size_t mIndexCount;
+};
+
+class Mesh::Builder {
+public:
+ Builder& setPrimitive(Primitive primitive) {
+ mPrimitive = primitive;
+ return *this;
+ };
+ Builder& setVertices(size_t vertexCount, size_t vertexSize) {
+ mVertexCount = vertexCount;
+ mVertexSize = vertexSize;
+ return *this;
+ };
+ Builder& setTexCoords(size_t texCoordsSize) {
+ mTexCoordsSize = texCoordsSize;
+ return *this;
+ };
+ Builder& setCropCoords(size_t cropCoordsSize) {
+ mCropCoordsSize = cropCoordsSize;
+ return *this;
+ };
+ Builder& setShadowAttrs() {
+ mShadowParamsSize = 3;
+ mShadowColorSize = 4;
+ return *this;
+ };
+ Builder& setIndices(size_t indexCount) {
+ mIndexCount = indexCount;
+ return *this;
+ };
+ Mesh build() const {
+ return Mesh{mPrimitive, mVertexCount, mVertexSize, mTexCoordsSize,
+ mCropCoordsSize, mShadowColorSize, mShadowParamsSize, mIndexCount};
+ }
+
+private:
+ size_t mVertexCount = 0;
+ size_t mVertexSize = 0;
+ size_t mTexCoordsSize = 0;
+ size_t mCropCoordsSize = 0;
+ size_t mShadowColorSize = 0;
+ size_t mShadowParamsSize = 0;
+ size_t mIndexCount = 0;
+ Primitive mPrimitive;
+};
+
+} // namespace renderengine
+} // namespace android
+#endif /* SF_RENDER_ENGINE_MESH_H */
diff --git a/media/libstagefright/renderfright/include/renderengine/RenderEngine.h b/media/libstagefright/renderfright/include/renderengine/RenderEngine.h
new file mode 100644
index 0000000..373d07b
--- /dev/null
+++ b/media/libstagefright/renderfright/include/renderengine/RenderEngine.h
@@ -0,0 +1,347 @@
+/*
+ * 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.
+ */
+
+#ifndef SF_RENDERENGINE_H_
+#define SF_RENDERENGINE_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <memory>
+
+#include <android-base/unique_fd.h>
+#include <math/mat4.h>
+#include <renderengine/DisplaySettings.h>
+#include <renderengine/Framebuffer.h>
+#include <renderengine/Image.h>
+#include <renderengine/LayerSettings.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Transform.h>
+
+/**
+ * Allows to set RenderEngine backend to GLES (default) or Vulkan (NOT yet supported).
+ */
+#define PROPERTY_DEBUG_RENDERENGINE_BACKEND "debug.stagefright.renderengine.backend"
+
+struct ANativeWindowBuffer;
+
+namespace android {
+
+class Rect;
+class Region;
+
+namespace renderengine {
+
+class BindNativeBufferAsFramebuffer;
+class Image;
+class Mesh;
+class Texture;
+struct RenderEngineCreationArgs;
+
+namespace threaded {
+class RenderEngineThreaded;
+}
+
+namespace impl {
+class RenderEngine;
+}
+
+enum class Protection {
+ UNPROTECTED = 1,
+ PROTECTED = 2,
+};
+
+class RenderEngine {
+public:
+ enum class ContextPriority {
+ LOW = 1,
+ MEDIUM = 2,
+ HIGH = 3,
+ };
+
+ enum class RenderEngineType {
+ GLES = 1,
+ THREADED = 2,
+ };
+
+ static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
+
+ virtual ~RenderEngine() = 0;
+
+ // ----- BEGIN DEPRECATED INTERFACE -----
+ // This interface, while still in use until a suitable replacement is built,
+ // should be considered deprecated, minus some methods which still may be
+ // used to support legacy behavior.
+ virtual void primeCache() const = 0;
+
+ // dump the extension strings. always call the base class.
+ virtual void dump(std::string& result) = 0;
+
+ virtual bool useNativeFenceSync() const = 0;
+ virtual bool useWaitSync() const = 0;
+ virtual void genTextures(size_t count, uint32_t* names) = 0;
+ virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
+ virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0;
+ // Legacy public method used by devices that don't support native fence
+ // synchronization in their GPU driver, as this method provides implicit
+ // synchronization for latching buffers.
+ virtual status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& fence) = 0;
+ // Caches Image resources for this buffer, but does not bind the buffer to
+ // a particular texture.
+ // Note that work is deferred to an additional thread, i.e. this call
+ // is made asynchronously, but the caller can expect that cache/unbind calls
+ // are performed in a manner that's conflict serializable, i.e. unbinding
+ // a buffer should never occur before binding the buffer if the caller
+ // called {bind, cache}ExternalTextureBuffer before calling unbind.
+ virtual void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 0;
+ // Removes internal resources referenced by the bufferId. This method should be
+ // invoked when the caller will no longer hold a reference to a GraphicBuffer
+ // and needs to clean up its resources.
+ // Note that work is deferred to an additional thread, i.e. this call
+ // is made asynchronously, but the caller can expect that cache/unbind calls
+ // are performed in a manner that's conflict serializable, i.e. unbinding
+ // a buffer should never occur before binding the buffer if the caller
+ // called {bind, cache}ExternalTextureBuffer before calling unbind.
+ virtual void unbindExternalTextureBuffer(uint64_t bufferId) = 0;
+ // When binding a native buffer, it must be done before setViewportAndProjection
+ // Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation.
+ virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0;
+ virtual void unbindFrameBuffer(Framebuffer* framebuffer) = 0;
+
+ enum class CleanupMode {
+ CLEAN_OUTPUT_RESOURCES,
+ CLEAN_ALL,
+ };
+ // Clean-up method that should be called on the main thread after the
+ // drawFence returned by drawLayers fires. This method will free up
+ // resources used by the most recently drawn frame. If the frame is still
+ // being drawn, then this call is silently ignored.
+ //
+ // If mode is CLEAN_OUTPUT_RESOURCES, then only resources related to the
+ // output framebuffer are cleaned up, including the sibling texture.
+ //
+ // If mode is CLEAN_ALL, then we also cleanup resources related to any input
+ // buffers.
+ //
+ // Returns true if resources were cleaned up, and false if we didn't need to
+ // do any work.
+ virtual bool cleanupPostRender(CleanupMode mode = CleanupMode::CLEAN_OUTPUT_RESOURCES) = 0;
+
+ // queries
+ virtual size_t getMaxTextureSize() const = 0;
+ virtual size_t getMaxViewportDims() const = 0;
+
+ // ----- END DEPRECATED INTERFACE -----
+
+ // ----- BEGIN NEW INTERFACE -----
+
+ virtual bool isProtected() const = 0;
+ virtual bool supportsProtectedContent() const = 0;
+ virtual bool useProtectedContext(bool useProtectedContext) = 0;
+
+ // Renders layers for a particular display via GPU composition. This method
+ // should be called for every display that needs to be rendered via the GPU.
+ // @param display The display-wide settings that should be applied prior to
+ // drawing any layers.
+ //
+ // Assumptions when calling this method:
+ // 1. There is exactly one caller - i.e. multi-threading is not supported.
+ // 2. Additional threads may be calling the {bind,cache}ExternalTexture
+ // methods above. But the main thread is responsible for holding resources
+ // such that Image destruction does not occur while this method is called.
+ //
+ // TODO(b/136806342): This should behavior should ideally be fixed since
+ // the above two assumptions are brittle, as conditional thread safetyness
+ // may be insufficient when maximizing rendering performance in the future.
+ //
+ // @param layers The layers to draw onto the display, in Z-order.
+ // @param buffer The buffer which will be drawn to. This buffer will be
+ // ready once drawFence fires.
+ // @param useFramebufferCache True if the framebuffer cache should be used.
+ // If an implementation does not cache output framebuffers, then this
+ // parameter does nothing.
+ // @param bufferFence Fence signalling that the buffer is ready to be drawn
+ // to.
+ // @param drawFence A pointer to a fence, which will fire when the buffer
+ // has been drawn to and is ready to be examined. The fence will be
+ // initialized by this method. The caller will be responsible for owning the
+ // fence.
+ // @return An error code indicating whether drawing was successful. For
+ // now, this always returns NO_ERROR.
+ virtual status_t drawLayers(const DisplaySettings& display,
+ const std::vector<const LayerSettings*>& layers,
+ const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
+ base::unique_fd&& bufferFence, base::unique_fd* drawFence) = 0;
+
+protected:
+ // Gets a framebuffer to render to. This framebuffer may or may not be
+ // cached depending on the implementation.
+ //
+ // Note that this method does not transfer ownership, so the caller most not
+ // live longer than RenderEngine.
+ virtual Framebuffer* getFramebufferForDrawing() = 0;
+ friend class BindNativeBufferAsFramebuffer;
+ friend class threaded::RenderEngineThreaded;
+};
+
+struct RenderEngineCreationArgs {
+ int pixelFormat;
+ uint32_t imageCacheSize;
+ bool useColorManagement;
+ bool enableProtectedContext;
+ bool precacheToneMapperShaderOnly;
+ bool supportsBackgroundBlur;
+ RenderEngine::ContextPriority contextPriority;
+ RenderEngine::RenderEngineType renderEngineType;
+ bool realtime;
+
+ struct Builder;
+
+private:
+ // must be created by Builder via constructor with full argument list
+ RenderEngineCreationArgs(int _pixelFormat, uint32_t _imageCacheSize, bool _useColorManagement,
+ bool _enableProtectedContext, bool _precacheToneMapperShaderOnly,
+ bool _supportsBackgroundBlur,
+ RenderEngine::ContextPriority _contextPriority,
+ RenderEngine::RenderEngineType _renderEngineType,
+ bool _realtime)
+ : pixelFormat(_pixelFormat),
+ imageCacheSize(_imageCacheSize),
+ useColorManagement(_useColorManagement),
+ enableProtectedContext(_enableProtectedContext),
+ precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly),
+ supportsBackgroundBlur(_supportsBackgroundBlur),
+ contextPriority(_contextPriority),
+ renderEngineType(_renderEngineType),
+ realtime(_realtime) {}
+ RenderEngineCreationArgs() = delete;
+};
+
+struct RenderEngineCreationArgs::Builder {
+ Builder() {}
+
+ Builder& setPixelFormat(int pixelFormat) {
+ this->pixelFormat = pixelFormat;
+ return *this;
+ }
+ Builder& setImageCacheSize(uint32_t imageCacheSize) {
+ this->imageCacheSize = imageCacheSize;
+ return *this;
+ }
+ Builder& setUseColorManagerment(bool useColorManagement) {
+ this->useColorManagement = useColorManagement;
+ return *this;
+ }
+ Builder& setEnableProtectedContext(bool enableProtectedContext) {
+ this->enableProtectedContext = enableProtectedContext;
+ return *this;
+ }
+ Builder& setPrecacheToneMapperShaderOnly(bool precacheToneMapperShaderOnly) {
+ this->precacheToneMapperShaderOnly = precacheToneMapperShaderOnly;
+ return *this;
+ }
+ Builder& setSupportsBackgroundBlur(bool supportsBackgroundBlur) {
+ this->supportsBackgroundBlur = supportsBackgroundBlur;
+ return *this;
+ }
+ Builder& setContextPriority(RenderEngine::ContextPriority contextPriority) {
+ this->contextPriority = contextPriority;
+ return *this;
+ }
+ Builder& setRenderEngineType(RenderEngine::RenderEngineType renderEngineType) {
+ this->renderEngineType = renderEngineType;
+ return *this;
+ }
+ Builder& setRealtime(bool realtime) {
+ this->realtime = realtime;
+ return *this;
+ }
+ RenderEngineCreationArgs build() const {
+ return RenderEngineCreationArgs(pixelFormat, imageCacheSize, useColorManagement,
+ enableProtectedContext, precacheToneMapperShaderOnly,
+ supportsBackgroundBlur, contextPriority, renderEngineType,
+ realtime);
+ }
+
+private:
+ // 1 means RGBA_8888
+ int pixelFormat = 1;
+ uint32_t imageCacheSize = 0;
+ bool useColorManagement = true;
+ bool enableProtectedContext = false;
+ bool precacheToneMapperShaderOnly = false;
+ bool supportsBackgroundBlur = false;
+ RenderEngine::ContextPriority contextPriority = RenderEngine::ContextPriority::MEDIUM;
+ RenderEngine::RenderEngineType renderEngineType = RenderEngine::RenderEngineType::GLES;
+ bool realtime = true;
+};
+
+class BindNativeBufferAsFramebuffer {
+public:
+ BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer,
+ const bool useFramebufferCache)
+ : mEngine(engine), mFramebuffer(mEngine.getFramebufferForDrawing()), mStatus(NO_ERROR) {
+ mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected(),
+ useFramebufferCache)
+ ? mEngine.bindFrameBuffer(mFramebuffer)
+ : NO_MEMORY;
+ }
+ ~BindNativeBufferAsFramebuffer() {
+ mFramebuffer->setNativeWindowBuffer(nullptr, false, /*arbitrary*/ true);
+ mEngine.unbindFrameBuffer(mFramebuffer);
+ }
+ status_t getStatus() const { return mStatus; }
+
+private:
+ RenderEngine& mEngine;
+ Framebuffer* mFramebuffer;
+ status_t mStatus;
+};
+
+class SyncFeatures {
+public:
+ static SyncFeatures &GetInstance();
+ bool useNativeFenceSync() const;
+ bool useFenceSync() const;
+ bool useWaitSync() const;
+
+private:
+ SyncFeatures();
+ bool mHasNativeFenceSync;
+ bool mHasFenceSync;
+ bool mHasWaitSync;
+};
+
+namespace impl {
+
+// impl::RenderEngine contains common implementation that is graphics back-end agnostic.
+class RenderEngine : public renderengine::RenderEngine {
+public:
+ virtual ~RenderEngine() = 0;
+
+ bool useNativeFenceSync() const override;
+ bool useWaitSync() const override;
+
+protected:
+ RenderEngine(const RenderEngineCreationArgs& args);
+ const RenderEngineCreationArgs mArgs;
+};
+
+} // namespace impl
+} // namespace renderengine
+} // namespace android
+
+#endif /* SF_RENDERENGINE_H_ */
diff --git a/media/libstagefright/renderfright/include/renderengine/Texture.h b/media/libstagefright/renderfright/include/renderengine/Texture.h
new file mode 100644
index 0000000..c69ace0
--- /dev/null
+++ b/media/libstagefright/renderfright/include/renderengine/Texture.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#ifndef SF_RENDER_ENGINE_TEXTURE_H
+#define SF_RENDER_ENGINE_TEXTURE_H
+
+#include <stdint.h>
+
+#include <math/mat4.h>
+
+namespace android {
+namespace renderengine {
+
+class Texture {
+public:
+ enum Target { TEXTURE_2D = 0x0DE1, TEXTURE_EXTERNAL = 0x8D65 };
+
+ Texture();
+ Texture(Target textureTarget, uint32_t textureName);
+ ~Texture();
+
+ void init(Target textureTarget, uint32_t textureName);
+
+ void setMatrix(float const* matrix);
+ void setFiltering(bool enabled);
+ void setDimensions(size_t width, size_t height);
+
+ uint32_t getTextureName() const;
+ uint32_t getTextureTarget() const;
+
+ const mat4& getMatrix() const;
+ bool getFiltering() const;
+ size_t getWidth() const;
+ size_t getHeight() const;
+
+private:
+ uint32_t mTextureName;
+ uint32_t mTextureTarget;
+ size_t mWidth;
+ size_t mHeight;
+ bool mFiltering;
+ mat4 mTextureMatrix;
+};
+
+} // namespace renderengine
+} // namespace android
+#endif /* SF_RENDER_ENGINE_TEXTURE_H */
diff --git a/media/libstagefright/renderfright/include/renderengine/mock/Framebuffer.h b/media/libstagefright/renderfright/include/renderengine/mock/Framebuffer.h
new file mode 100644
index 0000000..dfb6a4e
--- /dev/null
+++ b/media/libstagefright/renderfright/include/renderengine/mock/Framebuffer.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+#include <renderengine/Framebuffer.h>
+
+namespace android {
+namespace renderengine {
+namespace mock {
+
+class Framebuffer : public renderengine::Framebuffer {
+public:
+ Framebuffer();
+ ~Framebuffer() override;
+
+ MOCK_METHOD3(setNativeWindowBuffer, bool(ANativeWindowBuffer*, bool, const bool));
+};
+
+} // namespace mock
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/include/renderengine/mock/Image.h b/media/libstagefright/renderfright/include/renderengine/mock/Image.h
new file mode 100644
index 0000000..2b0eed1
--- /dev/null
+++ b/media/libstagefright/renderfright/include/renderengine/mock/Image.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+#include <renderengine/Image.h>
+
+namespace android {
+namespace renderengine {
+namespace mock {
+
+class Image : public renderengine::Image {
+public:
+ Image();
+ ~Image() override;
+
+ MOCK_METHOD2(setNativeWindowBuffer, bool(ANativeWindowBuffer* buffer, bool isProtected));
+};
+
+} // namespace mock
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/include/renderengine/mock/RenderEngine.h b/media/libstagefright/renderfright/include/renderengine/mock/RenderEngine.h
new file mode 100644
index 0000000..e03dd58
--- /dev/null
+++ b/media/libstagefright/renderfright/include/renderengine/mock/RenderEngine.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+#include <renderengine/DisplaySettings.h>
+#include <renderengine/LayerSettings.h>
+#include <renderengine/Mesh.h>
+#include <renderengine/RenderEngine.h>
+#include <renderengine/Texture.h>
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Region.h>
+
+namespace android {
+namespace renderengine {
+namespace mock {
+
+class RenderEngine : public renderengine::RenderEngine {
+public:
+ RenderEngine();
+ ~RenderEngine() override;
+
+ MOCK_METHOD0(getFramebufferForDrawing, Framebuffer*());
+ MOCK_CONST_METHOD0(primeCache, void());
+ MOCK_METHOD1(dump, void(std::string&));
+ MOCK_CONST_METHOD0(useNativeFenceSync, bool());
+ MOCK_CONST_METHOD0(useWaitSync, bool());
+ MOCK_CONST_METHOD0(isCurrent, bool());
+ MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
+ MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
+ MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&));
+ MOCK_METHOD1(cacheExternalTextureBuffer, void(const sp<GraphicBuffer>&));
+ MOCK_METHOD3(bindExternalTextureBuffer,
+ status_t(uint32_t, const sp<GraphicBuffer>&, const sp<Fence>&));
+ MOCK_METHOD1(unbindExternalTextureBuffer, void(uint64_t));
+ MOCK_METHOD1(bindFrameBuffer, status_t(renderengine::Framebuffer*));
+ MOCK_METHOD1(unbindFrameBuffer, void(renderengine::Framebuffer*));
+ MOCK_METHOD1(drawMesh, void(const renderengine::Mesh&));
+ MOCK_CONST_METHOD0(getMaxTextureSize, size_t());
+ MOCK_CONST_METHOD0(getMaxViewportDims, size_t());
+ MOCK_CONST_METHOD0(isProtected, bool());
+ MOCK_CONST_METHOD0(supportsProtectedContent, bool());
+ MOCK_METHOD1(useProtectedContext, bool(bool));
+ MOCK_METHOD1(cleanupPostRender, bool(CleanupMode mode));
+ MOCK_METHOD6(drawLayers,
+ status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
+ base::unique_fd*));
+};
+
+} // namespace mock
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/include/renderengine/private/Description.h b/media/libstagefright/renderfright/include/renderengine/private/Description.h
new file mode 100644
index 0000000..a62161a
--- /dev/null
+++ b/media/libstagefright/renderfright/include/renderengine/private/Description.h
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#ifndef SF_RENDER_ENGINE_DESCRIPTION_H_
+#define SF_RENDER_ENGINE_DESCRIPTION_H_
+
+#include <renderengine/Texture.h>
+#include <ui/GraphicTypes.h>
+
+namespace android {
+namespace renderengine {
+
+/*
+ * This is the structure that holds the state of the rendering engine.
+ * This class is used to generate a corresponding GLSL program and set the
+ * appropriate uniform.
+ */
+struct Description {
+ enum class TransferFunction : int {
+ LINEAR,
+ SRGB,
+ ST2084,
+ HLG, // Hybrid Log-Gamma for HDR.
+ };
+
+ static TransferFunction dataSpaceToTransferFunction(ui::Dataspace dataSpace);
+
+ Description() = default;
+ ~Description() = default;
+
+ bool hasInputTransformMatrix() const;
+ bool hasOutputTransformMatrix() const;
+ bool hasColorMatrix() const;
+
+ // whether textures are premultiplied
+ bool isPremultipliedAlpha = false;
+ // whether this layer is marked as opaque
+ bool isOpaque = true;
+
+ // corner radius of the layer
+ float cornerRadius = 0;
+
+ // Size of the rounded rectangle we are cropping to
+ half2 cropSize;
+
+ // Texture this layer uses
+ Texture texture;
+ bool textureEnabled = false;
+
+ // color used when texturing is disabled or when setting alpha.
+ half4 color;
+
+ // true if the sampled pixel values are in Y410/BT2020 rather than RGBA
+ bool isY410BT2020 = false;
+
+ // transfer functions for the input/output
+ TransferFunction inputTransferFunction = TransferFunction::LINEAR;
+ TransferFunction outputTransferFunction = TransferFunction::LINEAR;
+
+ float displayMaxLuminance;
+ float maxMasteringLuminance;
+ float maxContentLuminance;
+
+ // projection matrix
+ mat4 projectionMatrix;
+
+ // The color matrix will be applied in linear space right before OETF.
+ mat4 colorMatrix;
+ mat4 inputTransformMatrix;
+ mat4 outputTransformMatrix;
+
+ // True if this layer will draw a shadow.
+ bool drawShadows = false;
+};
+
+} // namespace renderengine
+} // namespace android
+
+#endif /* SF_RENDER_ENGINE_DESCRIPTION_H_ */
diff --git a/media/libstagefright/renderfright/mock/Framebuffer.cpp b/media/libstagefright/renderfright/mock/Framebuffer.cpp
new file mode 100644
index 0000000..fbdcaab
--- /dev/null
+++ b/media/libstagefright/renderfright/mock/Framebuffer.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#include <renderengine/mock/Framebuffer.h>
+
+namespace android {
+namespace renderengine {
+namespace mock {
+
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+Framebuffer::Framebuffer() = default;
+Framebuffer::~Framebuffer() = default;
+
+} // namespace mock
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/mock/Image.cpp b/media/libstagefright/renderfright/mock/Image.cpp
new file mode 100644
index 0000000..57f4346
--- /dev/null
+++ b/media/libstagefright/renderfright/mock/Image.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#include <renderengine/mock/Image.h>
+
+namespace android {
+namespace renderengine {
+namespace mock {
+
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+Image::Image() = default;
+Image::~Image() = default;
+
+} // namespace mock
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/mock/RenderEngine.cpp b/media/libstagefright/renderfright/mock/RenderEngine.cpp
new file mode 100644
index 0000000..261636d
--- /dev/null
+++ b/media/libstagefright/renderfright/mock/RenderEngine.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#include <renderengine/mock/RenderEngine.h>
+
+namespace android {
+namespace renderengine {
+namespace mock {
+
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+RenderEngine::RenderEngine() = default;
+RenderEngine::~RenderEngine() = default;
+
+} // namespace mock
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/tests/Android.bp b/media/libstagefright/renderfright/tests/Android.bp
new file mode 100644
index 0000000..e4b13fb
--- /dev/null
+++ b/media/libstagefright/renderfright/tests/Android.bp
@@ -0,0 +1,50 @@
+// 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.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_media_libstagefright_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_media_libstagefright_license"],
+}
+
+cc_test {
+ name: "librenderfright_test",
+ defaults: ["surfaceflinger_defaults"],
+ test_suites: ["device-tests"],
+ srcs: [
+ "RenderEngineTest.cpp",
+ "RenderEngineThreadedTest.cpp",
+ ],
+ static_libs: [
+ "libgmock",
+ "librenderfright",
+ "librenderfright_mocks",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libEGL",
+ "libGLESv2",
+ "libgui",
+ "liblog",
+ "libnativewindow",
+ "libprocessgroup",
+ "libsync",
+ "libui",
+ "libutils",
+ ],
+}
diff --git a/media/libstagefright/renderfright/tests/RenderEngineTest.cpp b/media/libstagefright/renderfright/tests/RenderEngineTest.cpp
new file mode 100644
index 0000000..2697ff4
--- /dev/null
+++ b/media/libstagefright/renderfright/tests/RenderEngineTest.cpp
@@ -0,0 +1,1469 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <chrono>
+#include <condition_variable>
+#include <fstream>
+
+#include <cutils/properties.h>
+#include <gtest/gtest.h>
+#include <renderengine/RenderEngine.h>
+#include <sync/sync.h>
+#include <ui/PixelFormat.h>
+#include "../gl/GLESRenderEngine.h"
+#include "../threaded/RenderEngineThreaded.h"
+
+constexpr int DEFAULT_DISPLAY_WIDTH = 128;
+constexpr int DEFAULT_DISPLAY_HEIGHT = 256;
+constexpr int DEFAULT_DISPLAY_OFFSET = 64;
+constexpr bool WRITE_BUFFER_TO_FILE_ON_FAILURE = false;
+
+namespace android {
+
+struct RenderEngineTest : public ::testing::Test {
+ static void SetUpTestSuite() {
+ sRE = renderengine::gl::GLESRenderEngine::create(
+ renderengine::RenderEngineCreationArgs::Builder()
+ .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+ .setImageCacheSize(1)
+ .setUseColorManagerment(false)
+ .setEnableProtectedContext(false)
+ .setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(true)
+ .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
+ .setRenderEngineType(renderengine::RenderEngine::RenderEngineType::GLES)
+ .build());
+ }
+
+ static void TearDownTestSuite() {
+ // The ordering here is important - sCurrentBuffer must live longer
+ // than RenderEngine to avoid a null reference on tear-down.
+ sRE = nullptr;
+ sCurrentBuffer = nullptr;
+ }
+
+ static sp<GraphicBuffer> allocateDefaultBuffer() {
+ return new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
+ HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_RENDER,
+ "output");
+ }
+
+ // Allocates a 1x1 buffer to fill with a solid color
+ static sp<GraphicBuffer> allocateSourceBuffer(uint32_t width, uint32_t height) {
+ return new GraphicBuffer(width, height, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_TEXTURE,
+ "input");
+ }
+
+ RenderEngineTest() { mBuffer = allocateDefaultBuffer(); }
+
+ ~RenderEngineTest() {
+ if (WRITE_BUFFER_TO_FILE_ON_FAILURE && ::testing::Test::HasFailure()) {
+ writeBufferToFile("/data/texture_out_");
+ }
+ for (uint32_t texName : mTexNames) {
+ sRE->deleteTextures(1, &texName);
+ EXPECT_FALSE(sRE->isTextureNameKnownForTesting(texName));
+ }
+ }
+
+ void writeBufferToFile(const char* basename) {
+ std::string filename(basename);
+ filename.append(::testing::UnitTest::GetInstance()->current_test_info()->name());
+ filename.append(".ppm");
+ std::ofstream file(filename.c_str(), std::ios::binary);
+ if (!file.is_open()) {
+ ALOGE("Unable to open file: %s", filename.c_str());
+ ALOGE("You may need to do: \"adb shell setenforce 0\" to enable "
+ "surfaceflinger to write debug images");
+ return;
+ }
+
+ uint8_t* pixels;
+ mBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+
+ file << "P6\n";
+ file << mBuffer->getWidth() << "\n";
+ file << mBuffer->getHeight() << "\n";
+ file << 255 << "\n";
+
+ std::vector<uint8_t> outBuffer(mBuffer->getWidth() * mBuffer->getHeight() * 3);
+ auto outPtr = reinterpret_cast<uint8_t*>(outBuffer.data());
+
+ for (uint32_t j = 0; j < mBuffer->getHeight(); j++) {
+ const uint8_t* src = pixels + (mBuffer->getStride() * j) * 4;
+ for (uint32_t i = 0; i < mBuffer->getWidth(); i++) {
+ // Only copy R, G and B components
+ outPtr[0] = src[0];
+ outPtr[1] = src[1];
+ outPtr[2] = src[2];
+ outPtr += 3;
+
+ src += 4;
+ }
+ }
+ file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size());
+ mBuffer->unlock();
+ }
+
+ void expectBufferColor(const Region& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
+ size_t c;
+ Rect const* rect = region.getArray(&c);
+ for (size_t i = 0; i < c; i++, rect++) {
+ expectBufferColor(*rect, r, g, b, a);
+ }
+ }
+
+ void expectBufferColor(const Rect& rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+ uint8_t tolerance = 0) {
+ auto colorCompare = [tolerance](const uint8_t* colorA, const uint8_t* colorB) {
+ auto colorBitCompare = [tolerance](uint8_t a, uint8_t b) {
+ uint8_t tmp = a >= b ? a - b : b - a;
+ return tmp <= tolerance;
+ };
+ return std::equal(colorA, colorA + 4, colorB, colorBitCompare);
+ };
+
+ expectBufferColor(rect, r, g, b, a, colorCompare);
+ }
+
+ void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+ std::function<bool(const uint8_t* a, const uint8_t* b)> colorCompare) {
+ uint8_t* pixels;
+ mBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+ int32_t maxFails = 10;
+ int32_t fails = 0;
+ for (int32_t j = 0; j < region.getHeight(); j++) {
+ const uint8_t* src =
+ pixels + (mBuffer->getStride() * (region.top + j) + region.left) * 4;
+ for (int32_t i = 0; i < region.getWidth(); i++) {
+ const uint8_t expected[4] = {r, g, b, a};
+ bool equal = colorCompare(src, expected);
+ EXPECT_TRUE(equal)
+ << "pixel @ (" << region.left + i << ", " << region.top + j << "): "
+ << "expected (" << static_cast<uint32_t>(r) << ", "
+ << static_cast<uint32_t>(g) << ", " << static_cast<uint32_t>(b) << ", "
+ << static_cast<uint32_t>(a) << "), "
+ << "got (" << static_cast<uint32_t>(src[0]) << ", "
+ << static_cast<uint32_t>(src[1]) << ", " << static_cast<uint32_t>(src[2])
+ << ", " << static_cast<uint32_t>(src[3]) << ")";
+ src += 4;
+ if (!equal && ++fails >= maxFails) {
+ break;
+ }
+ }
+ if (fails >= maxFails) {
+ break;
+ }
+ }
+ mBuffer->unlock();
+ }
+
+ void expectAlpha(const Rect& rect, uint8_t a) {
+ auto colorCompare = [](const uint8_t* colorA, const uint8_t* colorB) {
+ return colorA[3] == colorB[3];
+ };
+ expectBufferColor(rect, 0.0f /* r */, 0.0f /*g */, 0.0f /* b */, a, colorCompare);
+ }
+
+ void expectShadowColor(const renderengine::LayerSettings& castingLayer,
+ const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
+ const ubyte4& backgroundColor) {
+ const Rect casterRect(castingLayer.geometry.boundaries);
+ Region casterRegion = Region(casterRect);
+ const float casterCornerRadius = castingLayer.geometry.roundedCornersRadius;
+ if (casterCornerRadius > 0.0f) {
+ // ignore the corners if a corner radius is set
+ Rect cornerRect(casterCornerRadius, casterCornerRadius);
+ casterRegion.subtractSelf(cornerRect.offsetTo(casterRect.left, casterRect.top));
+ casterRegion.subtractSelf(
+ cornerRect.offsetTo(casterRect.right - casterCornerRadius, casterRect.top));
+ casterRegion.subtractSelf(
+ cornerRect.offsetTo(casterRect.left, casterRect.bottom - casterCornerRadius));
+ casterRegion.subtractSelf(cornerRect.offsetTo(casterRect.right - casterCornerRadius,
+ casterRect.bottom - casterCornerRadius));
+ }
+
+ const float shadowInset = shadow.length * -1.0f;
+ const Rect casterWithShadow =
+ Rect(casterRect).inset(shadowInset, shadowInset, shadowInset, shadowInset);
+ const Region shadowRegion = Region(casterWithShadow).subtractSelf(casterRect);
+ const Region backgroundRegion = Region(fullscreenRect()).subtractSelf(casterWithShadow);
+
+ // verify casting layer
+ expectBufferColor(casterRegion, casterColor.r, casterColor.g, casterColor.b, casterColor.a);
+
+ // verify shadows by testing just the alpha since its difficult to validate the shadow color
+ size_t c;
+ Rect const* r = shadowRegion.getArray(&c);
+ for (size_t i = 0; i < c; i++, r++) {
+ expectAlpha(*r, 255);
+ }
+
+ // verify background
+ expectBufferColor(backgroundRegion, backgroundColor.r, backgroundColor.g, backgroundColor.b,
+ backgroundColor.a);
+ }
+
+ static renderengine::ShadowSettings getShadowSettings(const vec2& casterPos, float shadowLength,
+ bool casterIsTranslucent) {
+ renderengine::ShadowSettings shadow;
+ shadow.ambientColor = {0.0f, 0.0f, 0.0f, 0.039f};
+ shadow.spotColor = {0.0f, 0.0f, 0.0f, 0.19f};
+ shadow.lightPos = vec3(casterPos.x, casterPos.y, 0);
+ shadow.lightRadius = 0.0f;
+ shadow.length = shadowLength;
+ shadow.casterIsTranslucent = casterIsTranslucent;
+ return shadow;
+ }
+
+ static Rect fullscreenRect() { return Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT); }
+
+ static Rect offsetRect() {
+ return Rect(DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_WIDTH,
+ DEFAULT_DISPLAY_HEIGHT);
+ }
+
+ static Rect offsetRectAtZero() {
+ return Rect(DEFAULT_DISPLAY_WIDTH - DEFAULT_DISPLAY_OFFSET,
+ DEFAULT_DISPLAY_HEIGHT - DEFAULT_DISPLAY_OFFSET);
+ }
+
+ void invokeDraw(renderengine::DisplaySettings settings,
+ std::vector<const renderengine::LayerSettings*> layers,
+ sp<GraphicBuffer> buffer) {
+ base::unique_fd fence;
+ status_t status =
+ sRE->drawLayers(settings, layers, buffer, true, base::unique_fd(), &fence);
+ sCurrentBuffer = buffer;
+
+ int fd = fence.release();
+ if (fd >= 0) {
+ sync_wait(fd, -1);
+ close(fd);
+ }
+
+ ASSERT_EQ(NO_ERROR, status);
+ if (layers.size() > 0) {
+ ASSERT_TRUE(sRE->isFramebufferImageCachedForTesting(buffer->getId()));
+ }
+ }
+
+ void drawEmptyLayers() {
+ renderengine::DisplaySettings settings;
+ std::vector<const renderengine::LayerSettings*> layers;
+ // Meaningless buffer since we don't do any drawing
+ sp<GraphicBuffer> buffer = new GraphicBuffer();
+ invokeDraw(settings, layers, buffer);
+ }
+
+ template <typename SourceVariant>
+ void fillBuffer(half r, half g, half b, half a);
+
+ template <typename SourceVariant>
+ void fillRedBuffer();
+
+ template <typename SourceVariant>
+ void fillGreenBuffer();
+
+ template <typename SourceVariant>
+ void fillBlueBuffer();
+
+ template <typename SourceVariant>
+ void fillRedTransparentBuffer();
+
+ template <typename SourceVariant>
+ void fillRedOffsetBuffer();
+
+ template <typename SourceVariant>
+ void fillBufferPhysicalOffset();
+
+ template <typename SourceVariant>
+ void fillBufferCheckers(uint32_t rotation);
+
+ template <typename SourceVariant>
+ void fillBufferCheckersRotate0();
+
+ template <typename SourceVariant>
+ void fillBufferCheckersRotate90();
+
+ template <typename SourceVariant>
+ void fillBufferCheckersRotate180();
+
+ template <typename SourceVariant>
+ void fillBufferCheckersRotate270();
+
+ template <typename SourceVariant>
+ void fillBufferWithLayerTransform();
+
+ template <typename SourceVariant>
+ void fillBufferLayerTransform();
+
+ template <typename SourceVariant>
+ void fillBufferWithColorTransform();
+
+ template <typename SourceVariant>
+ void fillBufferColorTransform();
+
+ template <typename SourceVariant>
+ void fillRedBufferWithRoundedCorners();
+
+ template <typename SourceVariant>
+ void fillBufferWithRoundedCorners();
+
+ template <typename SourceVariant>
+ void fillBufferAndBlurBackground();
+
+ template <typename SourceVariant>
+ void overlayCorners();
+
+ void fillRedBufferTextureTransform();
+
+ void fillBufferTextureTransform();
+
+ void fillRedBufferWithPremultiplyAlpha();
+
+ void fillBufferWithPremultiplyAlpha();
+
+ void fillRedBufferWithoutPremultiplyAlpha();
+
+ void fillBufferWithoutPremultiplyAlpha();
+
+ void fillGreenColorBufferThenClearRegion();
+
+ void clearLeftRegion();
+
+ void clearRegion();
+
+ template <typename SourceVariant>
+ void drawShadow(const renderengine::LayerSettings& castingLayer,
+ const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
+ const ubyte4& backgroundColor);
+
+ // Keep around the same renderengine object to save on initialization time.
+ // For now, exercise the GL backend directly so that some caching specifics
+ // can be tested without changing the interface.
+ static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRE;
+ // Hack to avoid NPE in the EGL driver: the GraphicBuffer needs to
+ // be freed *after* RenderEngine is destroyed, so that the EGL image is
+ // destroyed first.
+ static sp<GraphicBuffer> sCurrentBuffer;
+
+ sp<GraphicBuffer> mBuffer;
+
+ std::vector<uint32_t> mTexNames;
+};
+
+std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRE = nullptr;
+sp<GraphicBuffer> RenderEngineTest::sCurrentBuffer = nullptr;
+
+struct ColorSourceVariant {
+ static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
+ RenderEngineTest* /*fixture*/) {
+ layer.source.solidColor = half3(r, g, b);
+ }
+};
+
+struct RelaxOpaqueBufferVariant {
+ static void setOpaqueBit(renderengine::LayerSettings& layer) {
+ layer.source.buffer.isOpaque = false;
+ }
+
+ static uint8_t getAlphaChannel() { return 255; }
+};
+
+struct ForceOpaqueBufferVariant {
+ static void setOpaqueBit(renderengine::LayerSettings& layer) {
+ layer.source.buffer.isOpaque = true;
+ }
+
+ static uint8_t getAlphaChannel() {
+ // The isOpaque bit will override the alpha channel, so this should be
+ // arbitrary.
+ return 10;
+ }
+};
+
+template <typename OpaquenessVariant>
+struct BufferSourceVariant {
+ static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
+ RenderEngineTest* fixture) {
+ sp<GraphicBuffer> buf = RenderEngineTest::allocateSourceBuffer(1, 1);
+ uint32_t texName;
+ fixture->sRE->genTextures(1, &texName);
+ fixture->mTexNames.push_back(texName);
+
+ uint8_t* pixels;
+ buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+
+ for (uint32_t j = 0; j < buf->getHeight(); j++) {
+ uint8_t* iter = pixels + (buf->getStride() * j) * 4;
+ for (uint32_t i = 0; i < buf->getWidth(); i++) {
+ iter[0] = uint8_t(r * 255);
+ iter[1] = uint8_t(g * 255);
+ iter[2] = uint8_t(b * 255);
+ iter[3] = OpaquenessVariant::getAlphaChannel();
+ iter += 4;
+ }
+ }
+
+ buf->unlock();
+
+ layer.source.buffer.buffer = buf;
+ layer.source.buffer.textureName = texName;
+ OpaquenessVariant::setOpaqueBit(layer);
+ }
+};
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBuffer(half r, half g, half b, half a) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = fullscreenRect().toFloatRect();
+ SourceVariant::fillColor(layer, r, g, b, this);
+ layer.alpha = a;
+
+ layers.push_back(&layer);
+
+ invokeDraw(settings, layers, mBuffer);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillRedBuffer() {
+ fillBuffer<SourceVariant>(1.0f, 0.0f, 0.0f, 1.0f);
+ expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillGreenBuffer() {
+ fillBuffer<SourceVariant>(0.0f, 1.0f, 0.0f, 1.0f);
+ expectBufferColor(fullscreenRect(), 0, 255, 0, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBlueBuffer() {
+ fillBuffer<SourceVariant>(0.0f, 0.0f, 1.0f, 1.0f);
+ expectBufferColor(fullscreenRect(), 0, 0, 255, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillRedTransparentBuffer() {
+ fillBuffer<SourceVariant>(1.0f, 0.0f, 0.0f, .2f);
+ expectBufferColor(fullscreenRect(), 51, 0, 0, 51);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillRedOffsetBuffer() {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = offsetRect();
+ settings.clip = offsetRectAtZero();
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = offsetRectAtZero().toFloatRect();
+ SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+ layer.alpha = 1.0f;
+
+ layers.push_back(&layer);
+ invokeDraw(settings, layers, mBuffer);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferPhysicalOffset() {
+ fillRedOffsetBuffer<SourceVariant>();
+
+ expectBufferColor(Rect(DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_WIDTH,
+ DEFAULT_DISPLAY_HEIGHT),
+ 255, 0, 0, 255);
+ Rect offsetRegionLeft(DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_HEIGHT);
+ Rect offsetRegionTop(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_OFFSET);
+
+ expectBufferColor(offsetRegionLeft, 0, 0, 0, 0);
+ expectBufferColor(offsetRegionTop, 0, 0, 0, 0);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferCheckers(uint32_t orientationFlag) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ // Here logical space is 2x2
+ settings.clip = Rect(2, 2);
+ settings.orientation = orientationFlag;
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ renderengine::LayerSettings layerOne;
+ Rect rectOne(0, 0, 1, 1);
+ layerOne.geometry.boundaries = rectOne.toFloatRect();
+ SourceVariant::fillColor(layerOne, 1.0f, 0.0f, 0.0f, this);
+ layerOne.alpha = 1.0f;
+
+ renderengine::LayerSettings layerTwo;
+ Rect rectTwo(0, 1, 1, 2);
+ layerTwo.geometry.boundaries = rectTwo.toFloatRect();
+ SourceVariant::fillColor(layerTwo, 0.0f, 1.0f, 0.0f, this);
+ layerTwo.alpha = 1.0f;
+
+ renderengine::LayerSettings layerThree;
+ Rect rectThree(1, 0, 2, 1);
+ layerThree.geometry.boundaries = rectThree.toFloatRect();
+ SourceVariant::fillColor(layerThree, 0.0f, 0.0f, 1.0f, this);
+ layerThree.alpha = 1.0f;
+
+ layers.push_back(&layerOne);
+ layers.push_back(&layerTwo);
+ layers.push_back(&layerThree);
+
+ invokeDraw(settings, layers, mBuffer);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferCheckersRotate0() {
+ fillBufferCheckers<SourceVariant>(ui::Transform::ROT_0);
+ expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 255, 0, 0,
+ 255);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
+ DEFAULT_DISPLAY_HEIGHT / 2),
+ 0, 0, 255, 255);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2,
+ DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 0, 0, 0, 0);
+ expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2,
+ DEFAULT_DISPLAY_HEIGHT),
+ 0, 255, 0, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferCheckersRotate90() {
+ fillBufferCheckers<SourceVariant>(ui::Transform::ROT_90);
+ expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 0, 255, 0,
+ 255);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
+ DEFAULT_DISPLAY_HEIGHT / 2),
+ 255, 0, 0, 255);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2,
+ DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 0, 0, 255, 255);
+ expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2,
+ DEFAULT_DISPLAY_HEIGHT),
+ 0, 0, 0, 0);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferCheckersRotate180() {
+ fillBufferCheckers<SourceVariant>(ui::Transform::ROT_180);
+ expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 0, 0, 0,
+ 0);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
+ DEFAULT_DISPLAY_HEIGHT / 2),
+ 0, 255, 0, 255);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2,
+ DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 255, 0, 0, 255);
+ expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2,
+ DEFAULT_DISPLAY_HEIGHT),
+ 0, 0, 255, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferCheckersRotate270() {
+ fillBufferCheckers<SourceVariant>(ui::Transform::ROT_270);
+ expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 0, 0, 255,
+ 255);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
+ DEFAULT_DISPLAY_HEIGHT / 2),
+ 0, 0, 0, 0);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2,
+ DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 0, 255, 0, 255);
+ expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2,
+ DEFAULT_DISPLAY_HEIGHT),
+ 255, 0, 0, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferWithLayerTransform() {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ // Here logical space is 2x2
+ settings.clip = Rect(2, 2);
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+ // Translate one pixel diagonally
+ layer.geometry.positionTransform = mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1);
+ SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+ layer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
+ layer.alpha = 1.0f;
+
+ layers.push_back(&layer);
+
+ invokeDraw(settings, layers, mBuffer);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferLayerTransform() {
+ fillBufferWithLayerTransform<SourceVariant>();
+ expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT / 2), 0, 0, 0, 0);
+ expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 0);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2,
+ DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 255, 0, 0, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferWithColorTransform() {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = Rect(1, 1);
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+ SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this);
+ layer.alpha = 1.0f;
+
+ // construct a fake color matrix
+ // annihilate green and blue channels
+ settings.colorTransform = mat4::scale(vec4(1, 0, 0, 1));
+ // set red channel to red + green
+ layer.colorTransform = mat4(1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+
+ layer.alpha = 1.0f;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+
+ layers.push_back(&layer);
+
+ invokeDraw(settings, layers, mBuffer);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferColorTransform() {
+ fillBufferWithColorTransform<SourceVariant>();
+ expectBufferColor(fullscreenRect(), 191, 0, 0, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillRedBufferWithRoundedCorners() {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = fullscreenRect().toFloatRect();
+ layer.geometry.roundedCornersRadius = 5.0f;
+ layer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
+ SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+ layer.alpha = 1.0f;
+
+ layers.push_back(&layer);
+
+ invokeDraw(settings, layers, mBuffer);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferWithRoundedCorners() {
+ fillRedBufferWithRoundedCorners<SourceVariant>();
+ // Corners should be ignored...
+ expectBufferColor(Rect(0, 0, 1, 1), 0, 0, 0, 0);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH - 1, 0, DEFAULT_DISPLAY_WIDTH, 1), 0, 0, 0, 0);
+ expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT - 1, 1, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 0);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 1,
+ DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 0, 0, 0, 0);
+ // ...And the non-rounded portion should be red.
+ // Other pixels may be anti-aliased, so let's not check those.
+ expectBufferColor(Rect(5, 5, DEFAULT_DISPLAY_WIDTH - 5, DEFAULT_DISPLAY_HEIGHT - 5), 255, 0, 0,
+ 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferAndBlurBackground() {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.surface_flinger.supports_background_blur", value, "0");
+ if (!atoi(value)) {
+ // This device doesn't support blurs, no-op.
+ return;
+ }
+
+ auto blurRadius = 50;
+ auto center = DEFAULT_DISPLAY_WIDTH / 2;
+
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ renderengine::LayerSettings backgroundLayer;
+ backgroundLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ SourceVariant::fillColor(backgroundLayer, 0.0f, 1.0f, 0.0f, this);
+ backgroundLayer.alpha = 1.0f;
+ layers.push_back(&backgroundLayer);
+
+ renderengine::LayerSettings leftLayer;
+ leftLayer.geometry.boundaries =
+ Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT).toFloatRect();
+ SourceVariant::fillColor(leftLayer, 1.0f, 0.0f, 0.0f, this);
+ leftLayer.alpha = 1.0f;
+ layers.push_back(&leftLayer);
+
+ renderengine::LayerSettings blurLayer;
+ blurLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ blurLayer.backgroundBlurRadius = blurRadius;
+ blurLayer.alpha = 0;
+ layers.push_back(&blurLayer);
+
+ invokeDraw(settings, layers, mBuffer);
+
+ expectBufferColor(Rect(center - 1, center - 5, center, center + 5), 150, 150, 0, 255,
+ 50 /* tolerance */);
+ expectBufferColor(Rect(center, center - 5, center + 1, center + 5), 150, 150, 0, 255,
+ 50 /* tolerance */);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::overlayCorners() {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<const renderengine::LayerSettings*> layersFirst;
+
+ renderengine::LayerSettings layerOne;
+ layerOne.geometry.boundaries =
+ FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH / 3.0, DEFAULT_DISPLAY_HEIGHT / 3.0);
+ SourceVariant::fillColor(layerOne, 1.0f, 0.0f, 0.0f, this);
+ layerOne.alpha = 0.2;
+
+ layersFirst.push_back(&layerOne);
+ invokeDraw(settings, layersFirst, mBuffer);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 51, 0, 0, 51);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3 + 1, DEFAULT_DISPLAY_HEIGHT / 3 + 1,
+ DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 0, 0, 0, 0);
+
+ std::vector<const renderengine::LayerSettings*> layersSecond;
+ renderengine::LayerSettings layerTwo;
+ layerTwo.geometry.boundaries =
+ FloatRect(DEFAULT_DISPLAY_WIDTH / 3.0, DEFAULT_DISPLAY_HEIGHT / 3.0,
+ DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
+ SourceVariant::fillColor(layerTwo, 0.0f, 1.0f, 0.0f, this);
+ layerTwo.alpha = 1.0f;
+
+ layersSecond.push_back(&layerTwo);
+ invokeDraw(settings, layersSecond, mBuffer);
+
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 0, 0, 0, 0);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3 + 1, DEFAULT_DISPLAY_HEIGHT / 3 + 1,
+ DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 0, 255, 0, 255);
+}
+
+void RenderEngineTest::fillRedBufferTextureTransform() {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = Rect(1, 1);
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ renderengine::LayerSettings layer;
+ // Here will allocate a checker board texture, but transform texture
+ // coordinates so that only the upper left is applied.
+ sp<GraphicBuffer> buf = allocateSourceBuffer(2, 2);
+ uint32_t texName;
+ RenderEngineTest::sRE->genTextures(1, &texName);
+ this->mTexNames.push_back(texName);
+
+ uint8_t* pixels;
+ buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+ // Red top left, Green top right, Blue bottom left, Black bottom right
+ pixels[0] = 255;
+ pixels[1] = 0;
+ pixels[2] = 0;
+ pixels[3] = 255;
+ pixels[4] = 0;
+ pixels[5] = 255;
+ pixels[6] = 0;
+ pixels[7] = 255;
+ pixels[8] = 0;
+ pixels[9] = 0;
+ pixels[10] = 255;
+ pixels[11] = 255;
+ buf->unlock();
+
+ layer.source.buffer.buffer = buf;
+ layer.source.buffer.textureName = texName;
+ // Transform coordinates to only be inside the red quadrant.
+ layer.source.buffer.textureTransform = mat4::scale(vec4(0.2, 0.2, 1, 1));
+ layer.alpha = 1.0f;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+
+ layers.push_back(&layer);
+
+ invokeDraw(settings, layers, mBuffer);
+}
+
+void RenderEngineTest::fillBufferTextureTransform() {
+ fillRedBufferTextureTransform();
+ expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
+}
+
+void RenderEngineTest::fillRedBufferWithPremultiplyAlpha() {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ // Here logical space is 1x1
+ settings.clip = Rect(1, 1);
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ renderengine::LayerSettings layer;
+ sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
+ uint32_t texName;
+ RenderEngineTest::sRE->genTextures(1, &texName);
+ this->mTexNames.push_back(texName);
+
+ uint8_t* pixels;
+ buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+ pixels[0] = 255;
+ pixels[1] = 0;
+ pixels[2] = 0;
+ pixels[3] = 255;
+ buf->unlock();
+
+ layer.source.buffer.buffer = buf;
+ layer.source.buffer.textureName = texName;
+ layer.source.buffer.usePremultipliedAlpha = true;
+ layer.alpha = 0.5f;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+
+ layers.push_back(&layer);
+
+ invokeDraw(settings, layers, mBuffer);
+}
+
+void RenderEngineTest::fillBufferWithPremultiplyAlpha() {
+ fillRedBufferWithPremultiplyAlpha();
+ expectBufferColor(fullscreenRect(), 128, 0, 0, 128);
+}
+
+void RenderEngineTest::fillRedBufferWithoutPremultiplyAlpha() {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ // Here logical space is 1x1
+ settings.clip = Rect(1, 1);
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ renderengine::LayerSettings layer;
+ sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
+ uint32_t texName;
+ RenderEngineTest::sRE->genTextures(1, &texName);
+ this->mTexNames.push_back(texName);
+
+ uint8_t* pixels;
+ buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+ pixels[0] = 255;
+ pixels[1] = 0;
+ pixels[2] = 0;
+ pixels[3] = 255;
+ buf->unlock();
+
+ layer.source.buffer.buffer = buf;
+ layer.source.buffer.textureName = texName;
+ layer.source.buffer.usePremultipliedAlpha = false;
+ layer.alpha = 0.5f;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+
+ layers.push_back(&layer);
+
+ invokeDraw(settings, layers, mBuffer);
+}
+
+void RenderEngineTest::fillBufferWithoutPremultiplyAlpha() {
+ fillRedBufferWithoutPremultiplyAlpha();
+ expectBufferColor(fullscreenRect(), 128, 0, 0, 64, 1);
+}
+
+void RenderEngineTest::clearLeftRegion() {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ // Here logical space is 4x4
+ settings.clip = Rect(4, 4);
+ settings.clearRegion = Region(Rect(2, 4));
+ std::vector<const renderengine::LayerSettings*> layers;
+ // fake layer, without bounds should not render anything
+ renderengine::LayerSettings layer;
+ layers.push_back(&layer);
+ invokeDraw(settings, layers, mBuffer);
+}
+
+void RenderEngineTest::clearRegion() {
+ // Reuse mBuffer
+ clearLeftRegion();
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 255);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
+ DEFAULT_DISPLAY_HEIGHT),
+ 0, 0, 0, 0);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLayer,
+ const renderengine::ShadowSettings& shadow,
+ const ubyte4& casterColor, const ubyte4& backgroundColor) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ // add background layer
+ renderengine::LayerSettings bgLayer;
+ bgLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ ColorSourceVariant::fillColor(bgLayer, backgroundColor.r / 255.0f, backgroundColor.g / 255.0f,
+ backgroundColor.b / 255.0f, this);
+ bgLayer.alpha = backgroundColor.a / 255.0f;
+ layers.push_back(&bgLayer);
+
+ // add shadow layer
+ renderengine::LayerSettings shadowLayer;
+ shadowLayer.geometry.boundaries = castingLayer.geometry.boundaries;
+ shadowLayer.alpha = castingLayer.alpha;
+ shadowLayer.shadow = shadow;
+ layers.push_back(&shadowLayer);
+
+ // add layer casting the shadow
+ renderengine::LayerSettings layer = castingLayer;
+ SourceVariant::fillColor(layer, casterColor.r / 255.0f, casterColor.g / 255.0f,
+ casterColor.b / 255.0f, this);
+ layers.push_back(&layer);
+
+ invokeDraw(settings, layers, mBuffer);
+}
+
+TEST_F(RenderEngineTest, drawLayers_noLayersToDraw) {
+ drawEmptyLayers();
+}
+
+TEST_F(RenderEngineTest, drawLayers_nullOutputBuffer) {
+ renderengine::DisplaySettings settings;
+ std::vector<const renderengine::LayerSettings*> layers;
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = fullscreenRect().toFloatRect();
+ BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+ layers.push_back(&layer);
+ base::unique_fd fence;
+ status_t status = sRE->drawLayers(settings, layers, nullptr, true, base::unique_fd(), &fence);
+
+ ASSERT_EQ(BAD_VALUE, status);
+}
+
+TEST_F(RenderEngineTest, drawLayers_nullOutputFence) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<const renderengine::LayerSettings*> layers;
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = fullscreenRect().toFloatRect();
+ BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+ layer.alpha = 1.0;
+ layers.push_back(&layer);
+
+ status_t status = sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), nullptr);
+ sCurrentBuffer = mBuffer;
+ ASSERT_EQ(NO_ERROR, status);
+ expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
+}
+
+TEST_F(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<const renderengine::LayerSettings*> layers;
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = fullscreenRect().toFloatRect();
+ BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+ layer.alpha = 1.0;
+ layers.push_back(&layer);
+
+ status_t status = sRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd(), nullptr);
+ sCurrentBuffer = mBuffer;
+ ASSERT_EQ(NO_ERROR, status);
+ ASSERT_FALSE(sRE->isFramebufferImageCachedForTesting(mBuffer->getId()));
+ expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) {
+ fillRedBuffer<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) {
+ fillGreenBuffer<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) {
+ fillBlueBuffer<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) {
+ fillRedTransparentBuffer<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) {
+ fillBufferPhysicalOffset<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) {
+ fillBufferCheckersRotate0<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) {
+ fillBufferCheckersRotate90<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) {
+ fillBufferCheckersRotate180<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) {
+ fillBufferCheckersRotate270<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) {
+ fillBufferLayerTransform<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) {
+ fillBufferLayerTransform<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) {
+ fillBufferWithRoundedCorners<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) {
+ fillBufferAndBlurBackground<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_overlayCorners_colorSource) {
+ overlayCorners<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) {
+ fillRedBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) {
+ fillGreenBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) {
+ fillBlueBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) {
+ fillRedTransparentBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) {
+ fillBufferPhysicalOffset<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource) {
+ fillBufferCheckersRotate0<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSource) {
+ fillBufferCheckersRotate90<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSource) {
+ fillBufferCheckersRotate180<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSource) {
+ fillBufferCheckersRotate270<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) {
+ fillBufferLayerTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) {
+ fillBufferLayerTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) {
+ fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) {
+ fillBufferAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) {
+ overlayCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) {
+ fillRedBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) {
+ fillGreenBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) {
+ fillBlueBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) {
+ fillRedTransparentBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) {
+ fillBufferPhysicalOffset<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) {
+ fillBufferCheckersRotate0<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) {
+ fillBufferCheckersRotate90<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) {
+ fillBufferCheckersRotate180<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) {
+ fillBufferCheckersRotate270<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) {
+ fillBufferLayerTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) {
+ fillBufferLayerTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) {
+ fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) {
+ fillBufferAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_overlayCorners_bufferSource) {
+ overlayCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferTextureTransform) {
+ fillBufferTextureTransform();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) {
+ fillBufferWithPremultiplyAlpha();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) {
+ fillBufferWithoutPremultiplyAlpha();
+}
+
+TEST_F(RenderEngineTest, drawLayers_clearRegion) {
+ clearRegion();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = fullscreenRect().toFloatRect();
+ BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+
+ layers.push_back(&layer);
+ invokeDraw(settings, layers, mBuffer);
+ uint64_t bufferId = layer.source.buffer.buffer->getId();
+ EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
+ std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
+ sRE->unbindExternalTextureBufferForTesting(bufferId);
+ std::lock_guard<std::mutex> lock(barrier->mutex);
+ ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
+ [&]() REQUIRES(barrier->mutex) {
+ return barrier->isOpen;
+ }));
+ EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
+ EXPECT_EQ(NO_ERROR, barrier->result);
+}
+
+TEST_F(RenderEngineTest, bindExternalBuffer_withNullBuffer) {
+ status_t result = sRE->bindExternalTextureBuffer(0, nullptr, nullptr);
+ ASSERT_EQ(BAD_VALUE, result);
+}
+
+TEST_F(RenderEngineTest, bindExternalBuffer_cachesImages) {
+ sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
+ uint32_t texName;
+ sRE->genTextures(1, &texName);
+ mTexNames.push_back(texName);
+
+ sRE->bindExternalTextureBuffer(texName, buf, nullptr);
+ uint64_t bufferId = buf->getId();
+ EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
+ std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
+ sRE->unbindExternalTextureBufferForTesting(bufferId);
+ std::lock_guard<std::mutex> lock(barrier->mutex);
+ ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
+ [&]() REQUIRES(barrier->mutex) {
+ return barrier->isOpen;
+ }));
+ EXPECT_EQ(NO_ERROR, barrier->result);
+ EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
+}
+
+TEST_F(RenderEngineTest, cacheExternalBuffer_withNullBuffer) {
+ std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
+ sRE->cacheExternalTextureBufferForTesting(nullptr);
+ std::lock_guard<std::mutex> lock(barrier->mutex);
+ ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
+ [&]() REQUIRES(barrier->mutex) {
+ return barrier->isOpen;
+ }));
+ EXPECT_TRUE(barrier->isOpen);
+ EXPECT_EQ(BAD_VALUE, barrier->result);
+}
+
+TEST_F(RenderEngineTest, cacheExternalBuffer_cachesImages) {
+ sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
+ uint64_t bufferId = buf->getId();
+ std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
+ sRE->cacheExternalTextureBufferForTesting(buf);
+ {
+ std::lock_guard<std::mutex> lock(barrier->mutex);
+ ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
+ [&]() REQUIRES(barrier->mutex) {
+ return barrier->isOpen;
+ }));
+ EXPECT_EQ(NO_ERROR, barrier->result);
+ }
+ EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
+ barrier = sRE->unbindExternalTextureBufferForTesting(bufferId);
+ {
+ std::lock_guard<std::mutex> lock(barrier->mutex);
+ ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
+ [&]() REQUIRES(barrier->mutex) {
+ return barrier->isOpen;
+ }));
+ EXPECT_EQ(NO_ERROR, barrier->result);
+ }
+ EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) {
+ const ubyte4 casterColor(255, 0, 0, 255);
+ const ubyte4 backgroundColor(255, 255, 255, 255);
+ const float shadowLength = 5.0f;
+ Rect casterBounds(1, 1);
+ casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+ renderengine::LayerSettings castingLayer;
+ castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+ castingLayer.alpha = 1.0f;
+ renderengine::ShadowSettings settings =
+ getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+ false /* casterIsTranslucent */);
+
+ drawShadow<ColorSourceVariant>(castingLayer, settings, casterColor, backgroundColor);
+ expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) {
+ const ubyte4 casterColor(255, 0, 0, 255);
+ const ubyte4 backgroundColor(255, 255, 255, 255);
+ const float shadowLength = 5.0f;
+ Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+ casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+ renderengine::LayerSettings castingLayer;
+ castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+ castingLayer.alpha = 1.0f;
+ renderengine::ShadowSettings settings =
+ getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+ false /* casterIsTranslucent */);
+
+ drawShadow<ColorSourceVariant>(castingLayer, settings, casterColor, backgroundColor);
+ expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) {
+ const ubyte4 casterColor(255, 0, 0, 255);
+ const ubyte4 backgroundColor(255, 255, 255, 255);
+ const float shadowLength = 5.0f;
+ Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+ casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+ renderengine::LayerSettings castingLayer;
+ castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+ castingLayer.alpha = 1.0f;
+ renderengine::ShadowSettings settings =
+ getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+ false /* casterIsTranslucent */);
+
+ drawShadow<BufferSourceVariant<ForceOpaqueBufferVariant>>(castingLayer, settings, casterColor,
+ backgroundColor);
+ expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) {
+ const ubyte4 casterColor(255, 0, 0, 255);
+ const ubyte4 backgroundColor(255, 255, 255, 255);
+ const float shadowLength = 5.0f;
+ Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+ casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+ renderengine::LayerSettings castingLayer;
+ castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+ castingLayer.geometry.roundedCornersRadius = 3.0f;
+ castingLayer.geometry.roundedCornersCrop = casterBounds.toFloatRect();
+ castingLayer.alpha = 1.0f;
+ renderengine::ShadowSettings settings =
+ getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+ false /* casterIsTranslucent */);
+
+ drawShadow<BufferSourceVariant<ForceOpaqueBufferVariant>>(castingLayer, settings, casterColor,
+ backgroundColor);
+ expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) {
+ const ubyte4 casterColor(255, 0, 0, 255);
+ const ubyte4 backgroundColor(255, 255, 255, 255);
+ const float shadowLength = 5.0f;
+ Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+ casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+ renderengine::LayerSettings castingLayer;
+ castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+ castingLayer.alpha = 0.5f;
+ renderengine::ShadowSettings settings =
+ getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+ true /* casterIsTranslucent */);
+
+ drawShadow<BufferSourceVariant<RelaxOpaqueBufferVariant>>(castingLayer, settings, casterColor,
+ backgroundColor);
+
+ // verify only the background since the shadow will draw behind the caster
+ const float shadowInset = settings.length * -1.0f;
+ const Rect casterWithShadow =
+ Rect(casterBounds).inset(shadowInset, shadowInset, shadowInset, shadowInset);
+ const Region backgroundRegion = Region(fullscreenRect()).subtractSelf(casterWithShadow);
+ expectBufferColor(backgroundRegion, backgroundColor.r, backgroundColor.g, backgroundColor.b,
+ backgroundColor.a);
+}
+
+TEST_F(RenderEngineTest, cleanupPostRender_cleansUpOnce) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<const renderengine::LayerSettings*> layers;
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = fullscreenRect().toFloatRect();
+ BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+ layer.alpha = 1.0;
+ layers.push_back(&layer);
+
+ base::unique_fd fenceOne;
+ sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fenceOne);
+ base::unique_fd fenceTwo;
+ sRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne), &fenceTwo);
+
+ const int fd = fenceTwo.get();
+ if (fd >= 0) {
+ sync_wait(fd, -1);
+ }
+ // Only cleanup the first time.
+ EXPECT_TRUE(sRE->cleanupPostRender(
+ renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES));
+ EXPECT_FALSE(sRE->cleanupPostRender(
+ renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES));
+}
+
+TEST_F(RenderEngineTest, cleanupPostRender_whenCleaningAll_replacesTextureMemory) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<const renderengine::LayerSettings*> layers;
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = fullscreenRect().toFloatRect();
+ BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+ layer.alpha = 1.0;
+ layers.push_back(&layer);
+
+ base::unique_fd fence;
+ sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence);
+
+ const int fd = fence.get();
+ if (fd >= 0) {
+ sync_wait(fd, -1);
+ }
+
+ uint64_t bufferId = layer.source.buffer.buffer->getId();
+ uint32_t texName = layer.source.buffer.textureName;
+ EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
+ EXPECT_EQ(bufferId, sRE->getBufferIdForTextureNameForTesting(texName));
+
+ EXPECT_TRUE(sRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL));
+
+ // Now check that our view of memory is good.
+ EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
+ EXPECT_EQ(std::nullopt, sRE->getBufferIdForTextureNameForTesting(bufferId));
+ EXPECT_TRUE(sRE->isTextureNameKnownForTesting(texName));
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/media/libstagefright/renderfright/tests/RenderEngineThreadedTest.cpp b/media/libstagefright/renderfright/tests/RenderEngineThreadedTest.cpp
new file mode 100644
index 0000000..97c7442
--- /dev/null
+++ b/media/libstagefright/renderfright/tests/RenderEngineThreadedTest.cpp
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/properties.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+#include "../threaded/RenderEngineThreaded.h"
+
+namespace android {
+
+using testing::_;
+using testing::Eq;
+using testing::Mock;
+using testing::Return;
+
+struct RenderEngineThreadedTest : public ::testing::Test {
+ ~RenderEngineThreadedTest() {}
+
+ void SetUp() override {
+ mThreadedRE = renderengine::threaded::RenderEngineThreaded::create(
+ [this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); });
+ }
+
+ std::unique_ptr<renderengine::threaded::RenderEngineThreaded> mThreadedRE;
+ renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+};
+
+TEST_F(RenderEngineThreadedTest, dump) {
+ std::string testString = "XYZ";
+ EXPECT_CALL(*mRenderEngine, dump(_));
+ mThreadedRE->dump(testString);
+}
+
+TEST_F(RenderEngineThreadedTest, primeCache) {
+ EXPECT_CALL(*mRenderEngine, primeCache());
+ mThreadedRE->primeCache();
+}
+
+TEST_F(RenderEngineThreadedTest, genTextures) {
+ uint32_t texName;
+ EXPECT_CALL(*mRenderEngine, genTextures(1, &texName));
+ mThreadedRE->genTextures(1, &texName);
+}
+
+TEST_F(RenderEngineThreadedTest, deleteTextures) {
+ uint32_t texName;
+ EXPECT_CALL(*mRenderEngine, deleteTextures(1, &texName));
+ mThreadedRE->deleteTextures(1, &texName);
+}
+
+TEST_F(RenderEngineThreadedTest, bindExternalBuffer_nullptrBuffer) {
+ EXPECT_CALL(*mRenderEngine, bindExternalTextureBuffer(0, Eq(nullptr), Eq(nullptr)))
+ .WillOnce(Return(BAD_VALUE));
+ status_t result = mThreadedRE->bindExternalTextureBuffer(0, nullptr, nullptr);
+ ASSERT_EQ(BAD_VALUE, result);
+}
+
+TEST_F(RenderEngineThreadedTest, bindExternalBuffer_withBuffer) {
+ sp<GraphicBuffer> buf = new GraphicBuffer();
+ EXPECT_CALL(*mRenderEngine, bindExternalTextureBuffer(0, buf, Eq(nullptr)))
+ .WillOnce(Return(NO_ERROR));
+ status_t result = mThreadedRE->bindExternalTextureBuffer(0, buf, nullptr);
+ ASSERT_EQ(NO_ERROR, result);
+}
+
+TEST_F(RenderEngineThreadedTest, cacheExternalTextureBuffer_nullptr) {
+ EXPECT_CALL(*mRenderEngine, cacheExternalTextureBuffer(Eq(nullptr)));
+ mThreadedRE->cacheExternalTextureBuffer(nullptr);
+}
+
+TEST_F(RenderEngineThreadedTest, cacheExternalTextureBuffer_withBuffer) {
+ sp<GraphicBuffer> buf = new GraphicBuffer();
+ EXPECT_CALL(*mRenderEngine, cacheExternalTextureBuffer(buf));
+ mThreadedRE->cacheExternalTextureBuffer(buf);
+}
+
+TEST_F(RenderEngineThreadedTest, unbindExternalTextureBuffer) {
+ EXPECT_CALL(*mRenderEngine, unbindExternalTextureBuffer(0x0));
+ mThreadedRE->unbindExternalTextureBuffer(0x0);
+}
+
+TEST_F(RenderEngineThreadedTest, bindFrameBuffer_returnsBadValue) {
+ std::unique_ptr<renderengine::Framebuffer> framebuffer;
+ EXPECT_CALL(*mRenderEngine, bindFrameBuffer(framebuffer.get())).WillOnce(Return(BAD_VALUE));
+ status_t result = mThreadedRE->bindFrameBuffer(framebuffer.get());
+ ASSERT_EQ(BAD_VALUE, result);
+}
+
+TEST_F(RenderEngineThreadedTest, bindFrameBuffer_returnsNoError) {
+ std::unique_ptr<renderengine::Framebuffer> framebuffer;
+ EXPECT_CALL(*mRenderEngine, bindFrameBuffer(framebuffer.get())).WillOnce(Return(NO_ERROR));
+ status_t result = mThreadedRE->bindFrameBuffer(framebuffer.get());
+ ASSERT_EQ(NO_ERROR, result);
+}
+
+TEST_F(RenderEngineThreadedTest, unbindFrameBuffer) {
+ std::unique_ptr<renderengine::Framebuffer> framebuffer;
+ EXPECT_CALL(*mRenderEngine, unbindFrameBuffer(framebuffer.get()));
+ mThreadedRE->unbindFrameBuffer(framebuffer.get());
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns20) {
+ size_t size = 20;
+ EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size));
+ size_t result = mThreadedRE->getMaxTextureSize();
+ ASSERT_EQ(size, result);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns0) {
+ size_t size = 0;
+ EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size));
+ size_t result = mThreadedRE->getMaxTextureSize();
+ ASSERT_EQ(size, result);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxViewportDims_returns20) {
+ size_t dims = 20;
+ EXPECT_CALL(*mRenderEngine, getMaxViewportDims()).WillOnce(Return(dims));
+ size_t result = mThreadedRE->getMaxViewportDims();
+ ASSERT_EQ(dims, result);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxViewportDims_returns0) {
+ size_t dims = 0;
+ EXPECT_CALL(*mRenderEngine, getMaxViewportDims()).WillOnce(Return(dims));
+ size_t result = mThreadedRE->getMaxViewportDims();
+ ASSERT_EQ(dims, result);
+}
+
+TEST_F(RenderEngineThreadedTest, isProtected_returnsFalse) {
+ EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(false));
+ status_t result = mThreadedRE->isProtected();
+ ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, isProtected_returnsTrue) {
+ EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(true));
+ size_t result = mThreadedRE->isProtected();
+ ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsFalse) {
+ EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(false));
+ status_t result = mThreadedRE->supportsProtectedContent();
+ ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsTrue) {
+ EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(true));
+ status_t result = mThreadedRE->supportsProtectedContent();
+ ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsFalse) {
+ EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).WillOnce(Return(false));
+ status_t result = mThreadedRE->useProtectedContext(false);
+ ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsTrue) {
+ EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).WillOnce(Return(true));
+ status_t result = mThreadedRE->useProtectedContext(false);
+ ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsFalse) {
+ EXPECT_CALL(*mRenderEngine,
+ cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL))
+ .WillOnce(Return(false));
+ status_t result =
+ mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL);
+ ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsTrue) {
+ EXPECT_CALL(*mRenderEngine,
+ cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL))
+ .WillOnce(Return(true));
+ status_t result =
+ mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL);
+ ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, drawLayers) {
+ renderengine::DisplaySettings settings;
+ std::vector<const renderengine::LayerSettings*> layers;
+ sp<GraphicBuffer> buffer = new GraphicBuffer();
+ base::unique_fd bufferFence;
+ base::unique_fd drawFence;
+
+ EXPECT_CALL(*mRenderEngine, drawLayers)
+ .WillOnce([](const renderengine::DisplaySettings&,
+ const std::vector<const renderengine::LayerSettings*>&,
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
+ base::unique_fd*) -> status_t { return NO_ERROR; });
+
+ status_t result = mThreadedRE->drawLayers(settings, layers, buffer, false,
+ std::move(bufferFence), &drawFence);
+ ASSERT_EQ(NO_ERROR, result);
+}
+
+} // namespace android
diff --git a/media/libstagefright/renderfright/threaded/RenderEngineThreaded.cpp b/media/libstagefright/renderfright/threaded/RenderEngineThreaded.cpp
new file mode 100644
index 0000000..222d3ee
--- /dev/null
+++ b/media/libstagefright/renderfright/threaded/RenderEngineThreaded.cpp
@@ -0,0 +1,402 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "RenderEngineThreaded.h"
+
+#include <sched.h>
+#include <chrono>
+#include <future>
+
+#include <android-base/stringprintf.h>
+#include <utils/Trace.h>
+
+#include "gl/GLESRenderEngine.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace renderengine {
+namespace threaded {
+
+std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory) {
+ return std::make_unique<RenderEngineThreaded>(std::move(factory));
+}
+
+RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory) {
+ ATRACE_CALL();
+
+ std::lock_guard lockThread(mThreadMutex);
+ mThread = std::thread(&RenderEngineThreaded::threadMain, this, factory);
+}
+
+RenderEngineThreaded::~RenderEngineThreaded() {
+ {
+ std::lock_guard lock(mThreadMutex);
+ mRunning = false;
+ mCondition.notify_one();
+ }
+
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+}
+
+// NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations.
+void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_SAFETY_ANALYSIS {
+ ATRACE_CALL();
+
+ struct sched_param param = {0};
+ param.sched_priority = 2;
+ if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) {
+ ALOGE("Couldn't set SCHED_FIFO");
+ }
+
+ mRenderEngine = factory();
+
+ std::unique_lock<std::mutex> lock(mThreadMutex);
+ pthread_setname_np(pthread_self(), mThreadName);
+
+ while (mRunning) {
+ if (!mFunctionCalls.empty()) {
+ auto task = mFunctionCalls.front();
+ mFunctionCalls.pop();
+ task(*mRenderEngine);
+ }
+ mCondition.wait(lock, [this]() REQUIRES(mThreadMutex) {
+ return !mRunning || !mFunctionCalls.empty();
+ });
+ }
+}
+
+void RenderEngineThreaded::primeCache() const {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::primeCache");
+ instance.primeCache();
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+void RenderEngineThreaded::dump(std::string& result) {
+ std::promise<std::string> resultPromise;
+ std::future<std::string> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &result](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::dump");
+ std::string localResult = result;
+ instance.dump(localResult);
+ resultPromise.set_value(std::move(localResult));
+ });
+ }
+ mCondition.notify_one();
+ // Note: This is an rvalue.
+ result.assign(resultFuture.get());
+}
+
+bool RenderEngineThreaded::useNativeFenceSync() const {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& /*instance*/) {
+ ATRACE_NAME("REThreaded::useNativeFenceSync");
+ bool returnValue = SyncFeatures::GetInstance().useNativeFenceSync();
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+bool RenderEngineThreaded::useWaitSync() const {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& /*instance*/) {
+ ATRACE_NAME("REThreaded::useWaitSync");
+ bool returnValue = SyncFeatures::GetInstance().useWaitSync();
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, count, names](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::genTextures");
+ instance.genTextures(count, names);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, count, &names](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::deleteTextures");
+ instance.deleteTextures(count, names);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+void RenderEngineThreaded::bindExternalTextureImage(uint32_t texName, const Image& image) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push(
+ [&resultPromise, texName, &image](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::bindExternalTextureImage");
+ instance.bindExternalTextureImage(texName, image);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+status_t RenderEngineThreaded::bindExternalTextureBuffer(uint32_t texName,
+ const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& fence) {
+ std::promise<status_t> resultPromise;
+ std::future<status_t> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push(
+ [&resultPromise, texName, &buffer, &fence](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::bindExternalTextureBuffer");
+ status_t status = instance.bindExternalTextureBuffer(texName, buffer, fence);
+ resultPromise.set_value(status);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+void RenderEngineThreaded::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &buffer](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::cacheExternalTextureBuffer");
+ instance.cacheExternalTextureBuffer(buffer);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+void RenderEngineThreaded::unbindExternalTextureBuffer(uint64_t bufferId) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &bufferId](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::unbindExternalTextureBuffer");
+ instance.unbindExternalTextureBuffer(bufferId);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+status_t RenderEngineThreaded::bindFrameBuffer(Framebuffer* framebuffer) {
+ std::promise<status_t> resultPromise;
+ std::future<status_t> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &framebuffer](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::bindFrameBuffer");
+ status_t status = instance.bindFrameBuffer(framebuffer);
+ resultPromise.set_value(status);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+void RenderEngineThreaded::unbindFrameBuffer(Framebuffer* framebuffer) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &framebuffer](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::unbindFrameBuffer");
+ instance.unbindFrameBuffer(framebuffer);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+size_t RenderEngineThreaded::getMaxTextureSize() const {
+ std::promise<size_t> resultPromise;
+ std::future<size_t> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::getMaxTextureSize");
+ size_t size = instance.getMaxTextureSize();
+ resultPromise.set_value(size);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+size_t RenderEngineThreaded::getMaxViewportDims() const {
+ std::promise<size_t> resultPromise;
+ std::future<size_t> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::getMaxViewportDims");
+ size_t size = instance.getMaxViewportDims();
+ resultPromise.set_value(size);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+bool RenderEngineThreaded::isProtected() const {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::isProtected");
+ bool returnValue = instance.isProtected();
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+bool RenderEngineThreaded::supportsProtectedContent() const {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::supportsProtectedContent");
+ bool returnValue = instance.supportsProtectedContent();
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+bool RenderEngineThreaded::useProtectedContext(bool useProtectedContext) {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push(
+ [&resultPromise, useProtectedContext](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::useProtectedContext");
+ bool returnValue = instance.useProtectedContext(useProtectedContext);
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+Framebuffer* RenderEngineThreaded::getFramebufferForDrawing() {
+ std::promise<Framebuffer*> resultPromise;
+ std::future<Framebuffer*> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::getFramebufferForDrawing");
+ Framebuffer* framebuffer = instance.getFramebufferForDrawing();
+ resultPromise.set_value(framebuffer);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+bool RenderEngineThreaded::cleanupPostRender(CleanupMode mode) {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, mode](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::cleanupPostRender");
+ bool returnValue = instance.cleanupPostRender(mode);
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+status_t RenderEngineThreaded::drawLayers(const DisplaySettings& display,
+ const std::vector<const LayerSettings*>& layers,
+ const sp<GraphicBuffer>& buffer,
+ const bool useFramebufferCache,
+ base::unique_fd&& bufferFence,
+ base::unique_fd* drawFence) {
+ std::promise<status_t> resultPromise;
+ std::future<status_t> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &display, &layers, &buffer, useFramebufferCache,
+ &bufferFence, &drawFence](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::drawLayers");
+ status_t status = instance.drawLayers(display, layers, buffer, useFramebufferCache,
+ std::move(bufferFence), drawFence);
+ resultPromise.set_value(status);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+} // namespace threaded
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/renderfright/threaded/RenderEngineThreaded.h b/media/libstagefright/renderfright/threaded/RenderEngineThreaded.h
new file mode 100644
index 0000000..86a49e9
--- /dev/null
+++ b/media/libstagefright/renderfright/threaded/RenderEngineThreaded.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+#include "renderengine/RenderEngine.h"
+
+namespace android {
+namespace renderengine {
+namespace threaded {
+
+using CreateInstanceFactory = std::function<std::unique_ptr<renderengine::RenderEngine>()>;
+
+/**
+ * This class extends a basic RenderEngine class. It contains a thread. Each time a function of
+ * this class is called, we create a lambda function that is put on a queue. The main thread then
+ * executes the functions in order.
+ */
+class RenderEngineThreaded : public RenderEngine {
+public:
+ static std::unique_ptr<RenderEngineThreaded> create(CreateInstanceFactory factory);
+
+ RenderEngineThreaded(CreateInstanceFactory factory);
+ ~RenderEngineThreaded() override;
+ void primeCache() const override;
+
+ void dump(std::string& result) override;
+
+ bool useNativeFenceSync() const override;
+ bool useWaitSync() const override;
+ void genTextures(size_t count, uint32_t* names) override;
+ void deleteTextures(size_t count, uint32_t const* names) override;
+ void bindExternalTextureImage(uint32_t texName, const Image& image) override;
+ status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& fence) override;
+ void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
+ void unbindExternalTextureBuffer(uint64_t bufferId) override;
+ status_t bindFrameBuffer(Framebuffer* framebuffer) override;
+ void unbindFrameBuffer(Framebuffer* framebuffer) override;
+ size_t getMaxTextureSize() const override;
+ size_t getMaxViewportDims() const override;
+
+ bool isProtected() const override;
+ bool supportsProtectedContent() const override;
+ bool useProtectedContext(bool useProtectedContext) override;
+ bool cleanupPostRender(CleanupMode mode) override;
+
+ status_t drawLayers(const DisplaySettings& display,
+ const std::vector<const LayerSettings*>& layers,
+ const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
+ base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
+
+protected:
+ Framebuffer* getFramebufferForDrawing() override;
+
+private:
+ void threadMain(CreateInstanceFactory factory);
+
+ /* ------------------------------------------------------------------------
+ * Threading
+ */
+ const char* const mThreadName = "RenderEngineThread";
+ // Protects the creation and destruction of mThread.
+ mutable std::mutex mThreadMutex;
+ std::thread mThread GUARDED_BY(mThreadMutex);
+ bool mRunning GUARDED_BY(mThreadMutex) = true;
+ mutable std::queue<std::function<void(renderengine::RenderEngine& instance)>> mFunctionCalls
+ GUARDED_BY(mThreadMutex);
+ mutable std::condition_variable mCondition;
+
+ /* ------------------------------------------------------------------------
+ * Render Engine
+ */
+ std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
+};
+} // namespace threaded
+} // namespace renderengine
+} // namespace android
diff --git a/media/libstagefright/tests/HEVC/Android.bp b/media/libstagefright/tests/HEVC/Android.bp
index 501478c..91bf385 100644
--- a/media/libstagefright/tests/HEVC/Android.bp
+++ b/media/libstagefright/tests/HEVC/Android.bp
@@ -27,6 +27,7 @@
cc_test {
name: "HEVCUtilsUnitTest",
+ test_suites: ["device-tests"],
gtest: true,
srcs: [
diff --git a/media/libstagefright/tests/extractorFactory/Android.bp b/media/libstagefright/tests/extractorFactory/Android.bp
index 171755f..13d5b89 100644
--- a/media/libstagefright/tests/extractorFactory/Android.bp
+++ b/media/libstagefright/tests/extractorFactory/Android.bp
@@ -28,6 +28,7 @@
cc_test {
name: "ExtractorFactoryTest",
gtest: true,
+ test_suites: ["device-tests"],
srcs: [
"ExtractorFactoryTest.cpp",
diff --git a/media/libstagefright/tests/fuzzers/Android.bp b/media/libstagefright/tests/fuzzers/Android.bp
index 65e74e6..0097830 100644
--- a/media/libstagefright/tests/fuzzers/Android.bp
+++ b/media/libstagefright/tests/fuzzers/Android.bp
@@ -30,6 +30,7 @@
"libgui",
"libbinder",
"liblog",
+ "media_permission-aidl-cpp",
],
include_dirs: [
"frameworks/av/media/libstagefright",
diff --git a/media/libstagefright/tests/fuzzers/WriterFuzzer.cpp b/media/libstagefright/tests/fuzzers/WriterFuzzer.cpp
index 03e9b43..969c6e1 100644
--- a/media/libstagefright/tests/fuzzers/WriterFuzzer.cpp
+++ b/media/libstagefright/tests/fuzzers/WriterFuzzer.cpp
@@ -17,6 +17,7 @@
// dylan.katz@leviathansecurity.com
#include <android-base/file.h>
+#include <android/media/permission/Identity.h>
#include <ctype.h>
#include <media/mediarecorder.h>
#include <media/stagefright/MPEG4Writer.h>
@@ -39,6 +40,8 @@
namespace android {
+using media::permission::Identity;
+
std::string getFourCC(FuzzedDataProvider *fdp) {
std::string fourCC = fdp->ConsumeRandomLengthString(4);
// Replace any existing nulls
@@ -163,9 +166,11 @@
StandardWriters writerType = dataProvider.ConsumeEnum<StandardWriters>();
sp<MediaWriter> writer = createWriter(tf.fd, writerType, fileMeta);
- std::string packageName = dataProvider.ConsumeRandomLengthString(kMaxPackageNameLen);
-
- sp<MediaRecorder> mr = new MediaRecorder(String16(packageName.c_str()));
+ Identity i;
+ i.packageName = dataProvider.ConsumeRandomLengthString(kMaxPackageNameLen);
+ i.uid = dataProvider.ConsumeIntegral<int32_t>();
+ i.pid = dataProvider.ConsumeIntegral<int32_t>();
+ sp<MediaRecorder> mr = new MediaRecorder(i);
writer->setListener(mr);
uint8_t baseOpLen = operations.size();
diff --git a/media/libstagefright/timedtext/TEST_MAPPING b/media/libstagefright/timedtext/TEST_MAPPING
new file mode 100644
index 0000000..35a5b11
--- /dev/null
+++ b/media/libstagefright/timedtext/TEST_MAPPING
@@ -0,0 +1,9 @@
+// mappings for frameworks/av/media/libstagefright/timedtext
+{
+ // tests which require dynamic content
+ // invoke with: atest -- --enable-module-dynamic-download=true
+ // TODO(b/148094059): unit tests not allowed to download content
+ "dynamic-presubmit": [
+ { "name": "TimedTextUnitTest" }
+ ]
+}
diff --git a/media/libstagefright/timedtext/test/Android.bp b/media/libstagefright/timedtext/test/Android.bp
index e8ee2ee..0b632bf 100644
--- a/media/libstagefright/timedtext/test/Android.bp
+++ b/media/libstagefright/timedtext/test/Android.bp
@@ -27,6 +27,7 @@
cc_test {
name: "TimedTextUnitTest",
+ test_suites: ["device-tests"],
gtest: true,
srcs: [
diff --git a/media/libstagefright/webm/Android.bp b/media/libstagefright/webm/Android.bp
index 68752cd..3ceacfe 100644
--- a/media/libstagefright/webm/Android.bp
+++ b/media/libstagefright/webm/Android.bp
@@ -40,6 +40,7 @@
"libstagefright_foundation",
"libutils",
"liblog",
+ "media_permission-aidl-cpp",
],
header_libs: [
diff --git a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
index dbdb43c..67c6102 100644
--- a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
+++ b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
@@ -146,7 +146,10 @@
};
static std::vector<std::string> names = {
prefixes[0] + variants[0] + ".xml",
- prefixes[1] + variants[1] + ".xml"
+ prefixes[1] + variants[1] + ".xml",
+
+ // shaping information is not currently variant specific.
+ "media_codecs_shaping.xml"
};
return names;
}
@@ -346,6 +349,8 @@
status_t addAlias(const char **attrs);
status_t addFeature(const char **attrs);
status_t addLimit(const char **attrs);
+ status_t addMapping(const char **attrs);
+ status_t addTuning(const char **attrs);
status_t addQuirk(const char **attrs, const char *prefix = nullptr);
status_t addSetting(const char **attrs, const char *prefix = nullptr);
status_t enterMediaCodec(const char **attrs, bool encoder);
@@ -428,7 +433,7 @@
if (findFileInDirs(searchDirs, fileName, &path)) {
err = parseXmlPath(path);
} else {
- ALOGD("Cannot find %s", path.c_str());
+ ALOGI("Did not find %s in search path", fileName.c_str());
}
res = combineStatus(res, err);
}
@@ -438,7 +443,7 @@
status_t MediaCodecsXmlParser::Impl::parseXmlPath(const std::string &path) {
std::lock_guard<std::mutex> guard(mLock);
if (!fileExists(path)) {
- ALOGD("Cannot find %s", path.c_str());
+ ALOGV("Cannot find %s", path.c_str());
mParsingStatus = combineStatus(mParsingStatus, NAME_NOT_FOUND);
return NAME_NOT_FOUND;
}
@@ -741,13 +746,19 @@
{
// ignore limits and features specified outside of type
if (!mState->inType()
- && (strEq(name, "Limit") || strEq(name, "Feature") || strEq(name, "Variant"))) {
+ && (strEq(name, "Limit") || strEq(name, "Feature")
+ || strEq(name, "Variant") || strEq(name, "Mapping")
+ || strEq(name, "Tuning"))) {
PLOGD("ignoring %s specified outside of a Type", name);
return;
} else if (strEq(name, "Limit")) {
err = addLimit(attrs);
} else if (strEq(name, "Feature")) {
err = addFeature(attrs);
+ } else if (strEq(name, "Mapping")) {
+ err = addMapping(attrs);
+ } else if (strEq(name, "Tuning")) {
+ err = addTuning(attrs);
} else if (strEq(name, "Variant") && section != SECTION_VARIANT) {
err = limitVariants(attrs);
mState->enterSection(err == OK ? SECTION_VARIANT : SECTION_UNKNOWN);
@@ -981,7 +992,9 @@
TypeMap::iterator typeIt;
if (codecIt == mData->mCodecMap.end()) { // New codec name
if (updating) {
- return { NAME_NOT_FOUND, "MediaCodec: cannot update non-existing codec" };
+ std::string msg = "MediaCodec: cannot update non-existing codec: ";
+ msg = msg + name;
+ return { NAME_NOT_FOUND, msg };
}
// Create a new codec in mCodecMap
codecIt = mData->mCodecMap.insert(Codec(name, CodecProperties())).first;
@@ -994,19 +1007,25 @@
codecIt->second.order = mData->mCodecMap.size();
} else { // Existing codec name
if (!updating) {
- return { ALREADY_EXISTS, "MediaCodec: cannot add existing codec" };
+ std::string msg = "MediaCodec: cannot add existing codec: ";
+ msg = msg + name;
+ return { ALREADY_EXISTS, msg };
}
if (type != nullptr) {
typeIt = codecIt->second.typeMap.find(type);
if (typeIt == codecIt->second.typeMap.end()) {
- return { NAME_NOT_FOUND, "MediaCodec: cannot update non-existing type for codec" };
+ std::string msg = "MediaCodec: cannot update non-existing type for codec: ";
+ msg = msg + name;
+ return { NAME_NOT_FOUND, msg };
}
} else {
// This should happen only when the codec has at most one type.
typeIt = codecIt->second.typeMap.begin();
if (typeIt == codecIt->second.typeMap.end()
|| codecIt->second.typeMap.size() != 1) {
- return { BAD_VALUE, "MediaCodec: cannot update codec without type specified" };
+ std::string msg = "MediaCodec: cannot update codec without type specified: ";
+ msg = msg + name;
+ return { BAD_VALUE, msg };
}
}
}
@@ -1386,6 +1405,92 @@
return OK;
}
+status_t MediaCodecsXmlParser::Impl::Parser::addMapping(const char **attrs) {
+ CHECK(mState->inType());
+ size_t i = 0;
+ const char *a_name = nullptr;
+ const char *a_value = nullptr;
+ const char *a_kind = nullptr;
+
+ while (attrs[i] != nullptr) {
+ CHECK((i & 1) == 0);
+ if (attrs[i + 1] == nullptr) {
+ PLOGD("Mapping: attribute '%s' is null", attrs[i]);
+ return BAD_VALUE;
+ }
+
+ if (strEq(attrs[i], "name")) {
+ a_name = attrs[++i];
+ } else if (strEq(attrs[i], "kind")) {
+ a_kind = attrs[++i];
+ } else if (strEq(attrs[i], "value")) {
+ a_value = attrs[++i];
+ } else {
+ PLOGD("Mapping: ignoring unrecognized attribute '%s'", attrs[i]);
+ ++i;
+ }
+ ++i;
+ }
+
+ // Every mapping must have all 3 fields
+ if (a_name == nullptr) {
+ PLOGD("Mapping with no 'name' attribute");
+ return BAD_VALUE;
+ }
+
+ if (a_kind == nullptr) {
+ PLOGD("Mapping with no 'kind' attribute");
+ return BAD_VALUE;
+ }
+
+ if (a_value == nullptr) {
+ PLOGD("Mapping with no 'value' attribute");
+ return BAD_VALUE;
+ }
+
+ mState->addDetail(std::string("mapping-") + a_kind + "-" + a_name, a_value);
+ return OK;
+}
+
+status_t MediaCodecsXmlParser::Impl::Parser::addTuning(const char **attrs) {
+ CHECK(mState->inType());
+ size_t i = 0;
+ const char *a_name = nullptr;
+ const char *a_value = nullptr;
+
+ while (attrs[i] != nullptr) {
+ CHECK((i & 1) == 0);
+ if (attrs[i + 1] == nullptr) {
+ PLOGD("Mapping: attribute '%s' is null", attrs[i]);
+ return BAD_VALUE;
+ }
+
+ if (strEq(attrs[i], "name")) {
+ a_name = attrs[++i];
+ } else if (strEq(attrs[i], "value")) {
+ a_value = attrs[++i];
+ } else {
+ PLOGD("Tuning: ignoring unrecognized attribute '%s'", attrs[i]);
+ ++i;
+ }
+ ++i;
+ }
+
+ // Every tuning must have both fields
+ if (a_name == nullptr) {
+ PLOGD("Tuning with no 'name' attribute");
+ return BAD_VALUE;
+ }
+
+ if (a_value == nullptr) {
+ PLOGD("Tuning with no 'value' attribute");
+ return BAD_VALUE;
+ }
+
+ mState->addDetail(std::string("tuning-") + a_name, a_value);
+ return OK;
+}
+
status_t MediaCodecsXmlParser::Impl::Parser::addAlias(const char **attrs) {
CHECK(mState->inCodec());
size_t i = 0;
diff --git a/media/libstagefright/xmlparser/TEST_MAPPING b/media/libstagefright/xmlparser/TEST_MAPPING
new file mode 100644
index 0000000..8626d72
--- /dev/null
+++ b/media/libstagefright/xmlparser/TEST_MAPPING
@@ -0,0 +1,6 @@
+// test mapping for frameworks/av/media/libstagefright/xmlparser
+{
+ "presubmit": [
+ { "name": "XMLParserTest" }
+ ]
+}
diff --git a/media/libstagefright/xmlparser/api/current.txt b/media/libstagefright/xmlparser/api/current.txt
index 16c8af8..ecfd85e 100644
--- a/media/libstagefright/xmlparser/api/current.txt
+++ b/media/libstagefright/xmlparser/api/current.txt
@@ -65,6 +65,16 @@
method public void set_default(String);
}
+ public class Mapping {
+ ctor public Mapping();
+ method public String getKind();
+ method public String getName();
+ method public String getValue();
+ method public void setKind(String);
+ method public void setName(String);
+ method public void setValue(String);
+ }
+
public class MediaCodec {
ctor public MediaCodec();
method public java.util.List<media.codecs.Alias> getAlias_optional();
@@ -73,9 +83,11 @@
method public String getEnabled();
method public java.util.List<media.codecs.Feature> getFeature_optional();
method public java.util.List<media.codecs.Limit> getLimit_optional();
+ method public java.util.List<media.codecs.Mapping> getMapping_optional();
method public String getName();
method public java.util.List<media.codecs.Quirk> getQuirk_optional();
method public String getRank();
+ method public java.util.List<media.codecs.Tuning> getTuning_optional();
method public String getType();
method public java.util.List<media.codecs.Type> getType_optional();
method public String getUpdate();
@@ -125,6 +137,14 @@
method public java.util.List<media.codecs.Setting> getVariant_optional();
}
+ public class Tuning {
+ ctor public Tuning();
+ method public String getName();
+ method public String getValue();
+ method public void setName(String);
+ method public void setValue(String);
+ }
+
public class Type {
ctor public Type();
method public java.util.List<media.codecs.Alias> getAlias();
diff --git a/media/libstagefright/xmlparser/media_codecs.xsd b/media/libstagefright/xmlparser/media_codecs.xsd
index 3b5681f..c9a7efc 100644
--- a/media/libstagefright/xmlparser/media_codecs.xsd
+++ b/media/libstagefright/xmlparser/media_codecs.xsd
@@ -63,6 +63,8 @@
<xs:element name="Alias" type="Alias" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="Limit" type="Limit" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="Feature" type="Feature" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="Mapping" type="Mapping" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="Tuning" type="Tuning" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="Variant" type="Variant" minOccurs="0" maxOccurs="unbounded"/>
</xs:choice>
<xs:attribute name="name" type="xs:string"/>
@@ -122,6 +124,15 @@
<xs:attribute name="enabled" type="xs:string"/>
<xs:attribute name="update" type="xs:string"/>
</xs:complexType>
+ <xs:complexType name="Mapping">
+ <xs:attribute name="name" type="xs:string"/>
+ <xs:attribute name="kind" type="xs:string"/>
+ <xs:attribute name="value" type="xs:string"/>
+ </xs:complexType>
+ <xs:complexType name="Tuning">
+ <xs:attribute name="name" type="xs:string"/>
+ <xs:attribute name="value" type="xs:string"/>
+ </xs:complexType>
<xs:complexType name="Include">
<xs:attribute name="href" type="xs:string"/>
</xs:complexType>
diff --git a/media/libstagefright/xmlparser/test/Android.bp b/media/libstagefright/xmlparser/test/Android.bp
index 2fd577c..06a52f1 100644
--- a/media/libstagefright/xmlparser/test/Android.bp
+++ b/media/libstagefright/xmlparser/test/Android.bp
@@ -25,6 +25,7 @@
cc_test {
name: "XMLParserTest",
+ test_suites: ["device-tests"],
gtest: true,
srcs: [
diff --git a/media/libstagefright/xmlparser/test/XMLParserTest.cpp b/media/libstagefright/xmlparser/test/XMLParserTest.cpp
index 9ddd374..7629d97 100644
--- a/media/libstagefright/xmlparser/test/XMLParserTest.cpp
+++ b/media/libstagefright/xmlparser/test/XMLParserTest.cpp
@@ -127,6 +127,24 @@
setCodecProperties("test8.encoder", true, 8, {}, {}, {}, "audio/opus",
{pair<string, string>("max-limit1", "limit1Max")}, {}, "");
+ setCodecProperties("test9.encoder", true, 9, {}, {}, {}, "video/avc",
+ {
+ pair<string, string>("mapping-sure-before", "after"),
+ },
+ {}, "");
+
+ setCodecProperties("test10.encoder", true, 10, {}, {}, {}, "video/hevc",
+ {
+ pair<string, string>("mapping-fire-from", "to"),
+ },
+ {}, "");
+ setCodecProperties("test11.encoder", true, 11, {}, {}, {}, "video/av01",
+ {
+ pair<string, string>("tuning-hungry", "yes"),
+ pair<string, string>("tuning-pi", "3.1415"),
+ },
+ {}, "");
+
setRoleProperties("audio_decoder.mp3", false, 1, "audio/mpeg", "test1.decoder",
{pair<string, string>("attribute::disabled", "present"),
pair<string, string>("rank", "4")});
@@ -162,6 +180,17 @@
setRoleProperties("audio_encoder.opus", true, 8, "audio/opus", "test8.encoder",
{pair<string, string>("max-limit1", "limit1Max")});
+ setRoleProperties("video_encoder.avc", true, 9, "video/avc", "test9.encoder",
+ {pair<string, string>("mapping-sure-before", "after")});
+
+ setRoleProperties("video_encoder.hevc", true, 10, "video/hevc", "test10.encoder",
+ { pair<string, string>("mapping-fire-from", "to")});
+
+ setRoleProperties("video_encoder.av01", true, 11, "video/av01", "test11.encoder",
+ {pair<string, string>("tuning-hungry", "yes"),
+ pair<string, string>("tuning-pi", "3.1415")
+ });
+
setServiceAttribute(
{pair<string, string>("domain-telephony", "0"), pair<string, string>("domain-tv", "0"),
pair<string, string>("setting2", "0"), pair<string, string>("variant-variant1", "0")});
diff --git a/media/libstagefright/xmlparser/test/testdata/media_codecs_unit_test.xml b/media/libstagefright/xmlparser/test/testdata/media_codecs_unit_test.xml
index a7299d3..8cae423 100644
--- a/media/libstagefright/xmlparser/test/testdata/media_codecs_unit_test.xml
+++ b/media/libstagefright/xmlparser/test/testdata/media_codecs_unit_test.xml
@@ -76,5 +76,17 @@
<MediaCodec name="test8.encoder" type="audio/opus">
<Limit name="limit1" max="limit1Max" />
</MediaCodec>
+ <!-- entry for testing Mapping -->
+ <MediaCodec name="test9.encoder" type="video/avc" >
+ <Mapping kind="sure" name="before" value="after"/>
+ </MediaCodec>
+ <MediaCodec name="test10.encoder" type="video/hevc" >
+ <Mapping kind="fire" name="from" value="to"/>
+ </MediaCodec>
+ <!-- entry for testing Tuning -->
+ <MediaCodec name="test11.encoder" type="video/av01" >
+ <Tuning name="hungry" value="yes"/>
+ <Tuning name="pi" value="3.1415"/>
+ </MediaCodec>
</Encoders>
</Included>
diff --git a/media/libwatchdog/Android.bp b/media/libwatchdog/Android.bp
index fe717b1..411c206 100644
--- a/media/libwatchdog/Android.bp
+++ b/media/libwatchdog/Android.bp
@@ -23,6 +23,7 @@
cc_library {
name: "libwatchdog",
+ host_supported: true,
srcs: [
"Watchdog.cpp",
],
@@ -38,6 +39,11 @@
darwin: {
enabled: false,
},
+ linux_glibc: {
+ cflags: [
+ "-Dsigev_notify_thread_id=_sigev_un._tid",
+ ],
+ },
},
apex_available: ["com.android.media"],
min_sdk_version: "29",
diff --git a/media/mediaserver/Android.bp b/media/mediaserver/Android.bp
index 38bb791..a9617ec 100644
--- a/media/mediaserver/Android.bp
+++ b/media/mediaserver/Android.bp
@@ -34,6 +34,7 @@
shared_libs: [
"android.hardware.media.omx@1.0",
"libandroidicu",
+ "libfmq",
"libbinder",
"libhidlbase",
"liblog",
diff --git a/media/mediaserver/manifest_media_c2_software.xml b/media/mediaserver/manifest_media_c2_software.xml
index f23ed44..a5b4896 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.1</version>
+ <version>1.2</version>
<interface>
<name>IComponentStore</name>
<instance>software</instance>
diff --git a/media/mtp/IMtpDatabase.h b/media/mtp/IMtpDatabase.h
index 81fa60c..3b9bbb0 100644
--- a/media/mtp/IMtpDatabase.h
+++ b/media/mtp/IMtpDatabase.h
@@ -39,7 +39,7 @@
// Called to report success or failure of the SendObject file transfer.
virtual void endSendObject(MtpObjectHandle handle,
bool succeeded) = 0;
-
+
// Called to rescan a file, such as after an edit.
virtual void rescanFile(const char* path,
MtpObjectHandle handle,
@@ -91,6 +91,8 @@
int64_t& outFileLength,
MtpObjectFormat& outFormat) = 0;
+ virtual int openFilePath(const char* path, bool transcode) = 0;
+
virtual MtpResponseCode beginDeleteObject(MtpObjectHandle handle) = 0;
virtual void endDeleteObject(MtpObjectHandle handle, bool succeeded) = 0;
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 8677b90..becbe6e 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -790,11 +790,30 @@
auto start = std::chrono::steady_clock::now();
const char* filePath = (const char *)pathBuf;
- mtp_file_range mfr;
- mfr.fd = open(filePath, O_RDONLY);
- if (mfr.fd < 0) {
- return MTP_RESPONSE_GENERAL_ERROR;
+ mtp_file_range mfr;
+ struct stat sstat;
+ uint64_t finalsize;
+ bool transcode = android::base::GetBoolProperty("sys.fuse.transcode_mtp", false);
+ ALOGD("Mtp transcode = %d", transcode);
+ mfr.fd = mDatabase->openFilePath(filePath, transcode);
+ // Doing this here because we want to update fileLength only for this case and leave the
+ // regular path as unchanged as possible.
+ if (mfr.fd >= 0) {
+ fstat(mfr.fd, &sstat);
+ finalsize = sstat.st_size;
+ fileLength = finalsize;
+ } else {
+ ALOGW("Mtp open via IMtpDatabase failed for %s. Falling back to the original",
+ filePath);
+
+ mfr.fd = open(filePath, O_RDONLY);
+ if (mfr.fd < 0) {
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+ fstat(mfr.fd, &sstat);
+ finalsize = sstat.st_size;
}
+
mfr.offset = 0;
mfr.length = fileLength;
mfr.command = mRequest.getOperationCode();
@@ -815,9 +834,6 @@
auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> diff = end - start;
- struct stat sstat;
- fstat(mfr.fd, &sstat);
- uint64_t finalsize = sstat.st_size;
ALOGV("Sent a file over MTP. Time: %f s, Size: %" PRIu64 ", Rate: %f bytes/s",
diff.count(), finalsize, ((double) finalsize) / diff.count());
closeObjFd(mfr.fd, filePath);
diff --git a/media/mtp/tests/Android.bp b/media/mtp/tests/Android.bp
deleted file mode 100644
index 6ed94ac..0000000
--- a/media/mtp/tests/Android.bp
+++ /dev/null
@@ -1,56 +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.
-//
-
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_av_media_mtp_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_av_media_mtp_license"],
-}
-
-cc_test {
- name: "mtp_ffs_handle_test",
- test_suites: ["device-tests"],
- srcs: ["MtpFfsHandle_test.cpp"],
- shared_libs: [
- "libbase",
- "libmtp",
- "liblog",
- ],
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- ],
-}
-
-cc_test {
- name: "posix_async_io_test",
- test_suites: ["device-tests"],
- srcs: ["PosixAsyncIO_test.cpp"],
- shared_libs: [
- "libbase",
- "libmtp",
- "liblog",
- ],
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- ],
-}
diff --git a/media/mtp/tests/AndroidTest.xml b/media/mtp/tests/AndroidTest.xml
deleted file mode 100644
index c1f4753..0000000
--- a/media/mtp/tests/AndroidTest.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<configuration description="Config for mtp_ffs_handle_test">
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="cleanup" value="true" />
- <option name="push" value="mtp_ffs_handle_test->/data/local/tmp/mtp_ffs_handle_test" />
- </target_preparer>
- <option name="test-suite-tag" value="apct" />
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="mtp_ffs_handle_test" />
- </test>
-</configuration>
\ No newline at end of file
diff --git a/media/mtp/tests/MtpFfsHandleTest/Android.bp b/media/mtp/tests/MtpFfsHandleTest/Android.bp
new file mode 100644
index 0000000..ec9c7a4
--- /dev/null
+++ b/media/mtp/tests/MtpFfsHandleTest/Android.bp
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_media_mtp_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_media_mtp_license"],
+}
+
+cc_test {
+ name: "mtp_ffs_handle_test",
+ test_suites: ["device-tests"],
+ srcs: ["MtpFfsHandle_test.cpp"],
+ shared_libs: [
+ "libbase",
+ "libmtp",
+ "liblog",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+}
diff --git a/media/mtp/tests/MtpFfsHandleTest/AndroidTest.xml b/media/mtp/tests/MtpFfsHandleTest/AndroidTest.xml
new file mode 100644
index 0000000..38bab27
--- /dev/null
+++ b/media/mtp/tests/MtpFfsHandleTest/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for mtp_ffs_handle_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="mtp_ffs_handle_test->/data/local/tmp/mtp_ffs_handle_test" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="mtp_ffs_handle_test" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/media/mtp/tests/MtpFfsHandle_test.cpp b/media/mtp/tests/MtpFfsHandleTest/MtpFfsHandle_test.cpp
similarity index 100%
rename from media/mtp/tests/MtpFfsHandle_test.cpp
rename to media/mtp/tests/MtpFfsHandleTest/MtpFfsHandle_test.cpp
diff --git a/media/mtp/tests/MtpFuzzer/Android.bp b/media/mtp/tests/MtpFuzzer/Android.bp
new file mode 100644
index 0000000..5365f4b
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/Android.bp
@@ -0,0 +1,40 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_media_mtp_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_media_mtp_license"],
+}
+
+cc_fuzz {
+ name: "mtp_fuzzer",
+ srcs: [
+ "mtp_fuzzer.cpp",
+ "MtpMockDatabase.cpp",
+ ],
+ shared_libs: [
+ "libmtp",
+ "libbase",
+ "liblog",
+ "libutils",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-DMTP_DEVICE",
+ "-Wno-unused-parameter",
+ ],
+ dictionary: "mtp_fuzzer.dict",
+ corpus: ["corpus/*"],
+
+ fuzz_config: {
+
+ cc: ["jameswei@google.com"],
+ componentid: 1344,
+ acknowledgement: [
+ "Grant Hernandez of Google",
+ ],
+ },
+}
diff --git a/media/mtp/tests/MtpFuzzer/MtpMockDatabase.cpp b/media/mtp/tests/MtpFuzzer/MtpMockDatabase.cpp
new file mode 100644
index 0000000..8aafe33
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/MtpMockDatabase.cpp
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <string>
+
+#define LOG_TAG "MtpFuzzer"
+
+#include <log/log.h>
+
+#include "MtpDebug.h"
+#include "MtpMockDatabase.h"
+#include "MtpObjectInfo.h"
+
+namespace android {
+
+MtpMockDatabase::MtpMockDatabase() : mLastObjectHandle(0) {}
+
+MtpMockDatabase::~MtpMockDatabase() {
+ for (MtpObjectInfo* i : mObjects) {
+ delete i;
+ }
+ mObjects.clear();
+}
+
+void MtpMockDatabase::addObject(MtpObjectInfo* info) {
+ assert(hasStorage(info->storageID));
+
+ // we take ownership
+ mObjects.push_back(info);
+
+ return;
+}
+
+MtpObjectHandle MtpMockDatabase::allocateObjectHandle() {
+ // this is in sync with our mObjects database
+ return mLastObjectHandle++;
+}
+
+// Called from SendObjectInfo to reserve a database entry for the incoming file.
+MtpObjectHandle MtpMockDatabase::beginSendObject(const char* path, MtpObjectFormat format,
+ MtpObjectHandle parent, MtpStorageID storage) {
+ if (!hasStorage(storage)) {
+ ALOGW("%s: Tried to lookup storageID %u, but doesn't exist\n", __func__, storage);
+ return kInvalidObjectHandle;
+ }
+
+ ALOGD("MockDatabase %s: path=%s oformat=0x%04x parent_handle=%u "
+ "storage_id=%u\n",
+ __func__, path, format, parent, storage);
+
+ return mLastObjectHandle;
+}
+
+// Called to report success or failure of the SendObject file transfer.
+void MtpMockDatabase::endSendObject(MtpObjectHandle handle, bool succeeded) {
+ ALOGD("MockDatabase %s: ohandle=%u succeeded=%d\n", __func__, handle, succeeded);
+}
+
+// Called to rescan a file, such as after an edit.
+void MtpMockDatabase::rescanFile(const char* path, MtpObjectHandle handle, MtpObjectFormat format) {
+ ALOGD("MockDatabase %s: path=%s ohandle=%u, oformat=0x%04x\n", __func__, path, handle, format);
+}
+
+MtpObjectHandleList* MtpMockDatabase::getObjectList(MtpStorageID storageID, MtpObjectFormat format,
+ MtpObjectHandle parent) {
+ ALOGD("MockDatabase %s: storage_id=%u oformat=0x%04x ohandle=%u\n", __func__, storageID, format,
+ parent);
+ return nullptr;
+}
+
+int MtpMockDatabase::getNumObjects(MtpStorageID storageID, MtpObjectFormat format,
+ MtpObjectHandle parent) {
+ ALOGD("MockDatabase %s: storage_id=%u oformat=0x%04x ohandle=%u\n", __func__, storageID, format,
+ parent);
+ // TODO: return MTP_RESPONSE_OK when it stops segfaulting
+ return 0;
+}
+
+// callee should delete[] the results from these
+// results can be NULL
+MtpObjectFormatList* MtpMockDatabase::getSupportedPlaybackFormats() {
+ ALOGD("MockDatabase %s\n", __func__);
+ return nullptr;
+}
+MtpObjectFormatList* MtpMockDatabase::getSupportedCaptureFormats() {
+ ALOGD("MockDatabase %s\n", __func__);
+ return nullptr;
+}
+MtpObjectPropertyList* MtpMockDatabase::getSupportedObjectProperties(MtpObjectFormat format) {
+ ALOGD("MockDatabase %s: oformat=0x%04x\n", __func__, format);
+ return nullptr;
+}
+MtpDevicePropertyList* MtpMockDatabase::getSupportedDeviceProperties() {
+ ALOGD("MockDatabase %s\n", __func__);
+ return nullptr;
+}
+
+MtpResponseCode MtpMockDatabase::getObjectPropertyValue(MtpObjectHandle handle,
+ MtpObjectProperty property,
+ MtpDataPacket& packet) {
+ ALOGD("MockDatabase %s: ohandle=%u property=%s\n", __func__, handle,
+ MtpDebug::getObjectPropCodeName(property));
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpMockDatabase::setObjectPropertyValue(MtpObjectHandle handle,
+ MtpObjectProperty property,
+ MtpDataPacket& packet) {
+ ALOGD("MockDatabase %s: ohandle=%u property=%s\n", __func__, handle,
+ MtpDebug::getObjectPropCodeName(property));
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpMockDatabase::getDevicePropertyValue(MtpDeviceProperty property,
+ MtpDataPacket& packet) {
+ ALOGD("MockDatabase %s: property=%s\n", __func__, MtpDebug::getDevicePropCodeName(property));
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpMockDatabase::setDevicePropertyValue(MtpDeviceProperty property,
+ MtpDataPacket& packet) {
+ ALOGD("MockDatabase %s: property=%s\n", __func__, MtpDebug::getDevicePropCodeName(property));
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpMockDatabase::resetDeviceProperty(MtpDeviceProperty property) {
+ ALOGD("MockDatabase %s: property=%s\n", __func__, MtpDebug::getDevicePropCodeName(property));
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpMockDatabase::getObjectPropertyList(MtpObjectHandle handle, uint32_t format,
+ uint32_t property, int groupCode, int depth,
+ MtpDataPacket& packet) {
+ ALOGD("MockDatabase %s: ohandle=%u format=%s property=%s groupCode=%d "
+ "depth=%d\n",
+ __func__, handle, MtpDebug::getFormatCodeName(format),
+ MtpDebug::getObjectPropCodeName(property), groupCode, depth);
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpMockDatabase::getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info) {
+ ALOGD("MockDatabase %s: ohandle=%u\n", __func__, handle);
+
+ // used for the root
+ if (handle == kInvalidObjectHandle) {
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+ } else {
+ if (mObjects.size() == 0) {
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+ }
+
+ // this is used to let the fuzzer make progress, otherwise
+ // it has to brute-force a 32-bit handle
+ MtpObjectHandle reducedHandle = handle % mObjects.size();
+ MtpObjectInfo* obj = mObjects[reducedHandle];
+
+ // make a copy, but make sure to maintain ownership of string pointers
+ info = *obj;
+
+ // fixup the response handle
+ info.mHandle = handle;
+
+ if (obj->mName) info.mName = strdup(obj->mName);
+ if (obj->mKeywords) info.mKeywords = strdup(obj->mKeywords);
+
+ return MTP_RESPONSE_OK;
+ }
+}
+
+void* MtpMockDatabase::getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) {
+ ALOGD("MockDatabase %s: ohandle=%u\n", __func__, handle);
+
+ size_t allocSize = handle % 0x1000;
+ void* data = calloc(allocSize, sizeof(uint8_t));
+ if (!data) {
+ return nullptr;
+ } else {
+ ALOGD("MockDatabase %s\n", __func__);
+ outThumbSize = allocSize;
+ return data;
+ }
+}
+
+MtpResponseCode MtpMockDatabase::getObjectFilePath(MtpObjectHandle handle,
+ MtpStringBuffer& outFilePath,
+ int64_t& outFileLength,
+ MtpObjectFormat& outFormat) {
+ ALOGD("MockDatabase %s: ohandle=%u\n", __func__, handle);
+
+ if (mObjects.size() == 0) {
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+ }
+
+ // this is used to let the fuzzer make progress, otherwise
+ // it has to brute-force a 32-bit handle
+ MtpObjectHandle reducedHandle = handle % mObjects.size();
+ MtpObjectInfo* obj = mObjects[reducedHandle];
+ MtpStorage* storage = mStorage[obj->mStorageID];
+
+ // walk up the tree to build a full path of the object
+ MtpObjectHandle currentHandle = reducedHandle;
+ std::string path = "";
+
+ while (currentHandle != MTP_PARENT_ROOT) {
+ MtpObjectInfo* next = mObjects[currentHandle];
+
+ // prepend the name
+ if (path == "")
+ path = std::string(next->mName);
+ else
+ path = std::string(next->mName) + "/" + path;
+
+ currentHandle = next->mParent;
+ }
+
+ outFilePath.set(storage->getPath());
+ outFilePath.append("/");
+ outFilePath.append(path.c_str());
+
+ outFormat = obj->mFormat;
+
+ ALOGD("MockDatabase %s: get file %s\n", __func__, (const char*)outFilePath);
+
+ struct stat sstat;
+ // this should not happen unless our database view of the filesystem is out of
+ // sync
+ if (stat((const char*)outFilePath, &sstat) < 0) {
+ ALOGE("MockDatabase %s: unable to stat %s\n", __func__, (const char*)outFilePath);
+
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+ }
+
+ outFileLength = sstat.st_size;
+
+ return MTP_RESPONSE_OK;
+}
+
+int MtpMockDatabase::openFilePath(const char* path, bool transcode) {
+ ALOGD("MockDatabase %s: filePath=%s transcode=%d\n", __func__, path, transcode);
+ return 0;
+}
+
+MtpResponseCode MtpMockDatabase::beginDeleteObject(MtpObjectHandle handle) {
+ ALOGD("MockDatabase %s: ohandle=%u\n", __func__, handle);
+ return MTP_RESPONSE_OK;
+}
+void MtpMockDatabase::endDeleteObject(MtpObjectHandle handle, bool succeeded) {
+ ALOGD("MockDatabase %s: ohandle=%u succeeded=%d\n", __func__, handle, succeeded);
+ return;
+}
+
+MtpObjectHandleList* MtpMockDatabase::getObjectReferences(MtpObjectHandle handle) {
+ ALOGD("MockDatabase %s: ohandle=%u\n", __func__, handle);
+ return nullptr;
+}
+
+MtpResponseCode MtpMockDatabase::setObjectReferences(MtpObjectHandle handle,
+ MtpObjectHandleList* references) {
+ ALOGD("MockDatabase %s: ohandle=%u\n", __func__, handle);
+ return MTP_RESPONSE_OK;
+}
+
+MtpProperty* MtpMockDatabase::getObjectPropertyDesc(MtpObjectProperty property,
+ MtpObjectFormat format) {
+ ALOGD("MockDatabase %s: property=%s format=%s\n", __func__,
+ MtpDebug::getObjectPropCodeName(property), MtpDebug::getFormatCodeName(format));
+
+ return nullptr;
+}
+
+MtpProperty* MtpMockDatabase::getDevicePropertyDesc(MtpDeviceProperty property) {
+ ALOGD("MockDatabase %s: property=%s\n", __func__, MtpDebug::getDevicePropCodeName(property));
+ return nullptr;
+}
+
+MtpResponseCode MtpMockDatabase::beginMoveObject(MtpObjectHandle handle, MtpObjectHandle newParent,
+ MtpStorageID newStorage) {
+ ALOGD("MockDatabase %s: ohandle=%u newParent=%u newStorage=%u\n", __func__, handle, newParent,
+ newStorage);
+ return MTP_RESPONSE_OK;
+}
+
+void MtpMockDatabase::endMoveObject(MtpObjectHandle oldParent, MtpObjectHandle newParent,
+ MtpStorageID oldStorage, MtpStorageID newStorage,
+ MtpObjectHandle handle, bool succeeded) {
+ ALOGD("MockDatabase %s: oldParent=%u newParent=%u oldStorage=%u newStorage=%u "
+ "ohandle=%u succeeded=%d\n",
+ __func__, oldParent, newParent, oldStorage, newStorage, handle, succeeded);
+ return;
+}
+
+MtpResponseCode MtpMockDatabase::beginCopyObject(MtpObjectHandle handle, MtpObjectHandle newParent,
+ MtpStorageID newStorage) {
+ ALOGD("MockDatabase %s: ohandle=%u newParent=%u newStorage=%u\n", __func__, handle, newParent,
+ newStorage);
+ return MTP_RESPONSE_OK;
+}
+
+void MtpMockDatabase::endCopyObject(MtpObjectHandle handle, bool succeeded) {
+ ALOGD("MockDatabase %s: ohandle=%u succeeded=%d\n", __func__, handle, succeeded);
+}
+
+}; // namespace android
diff --git a/media/mtp/tests/MtpFuzzer/MtpMockDatabase.h b/media/mtp/tests/MtpFuzzer/MtpMockDatabase.h
new file mode 100644
index 0000000..e9e6a64
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/MtpMockDatabase.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _MTP_MOCK_DATABASE_H
+#define _MTP_MOCK_DATABASE_H
+
+#include <map>
+
+#include "IMtpDatabase.h"
+#include "MtpStorage.h"
+
+namespace android {
+
+class MtpMockDatabase : public IMtpDatabase {
+ std::map<MtpStorageID, MtpStorage*> mStorage;
+ std::vector<MtpObjectInfo*> mObjects;
+ uint32_t mLastObjectHandle;
+
+public:
+ MtpMockDatabase();
+ virtual ~MtpMockDatabase();
+
+ // MtpFuzzer methods
+ void addStorage(MtpStorage* storage) {
+ // we don't own this
+ mStorage[storage->getStorageID()] = storage;
+ }
+
+ bool hasStorage(MtpStorageID storage) { return mStorage.find(storage) != mStorage.end(); }
+
+ void addObject(MtpObjectInfo* info);
+ MtpObjectHandle allocateObjectHandle();
+
+ // libmtp interface methods
+ // Called from SendObjectInfo to reserve a database entry for the incoming
+ // file.
+ MtpObjectHandle beginSendObject(const char* path, MtpObjectFormat format,
+ MtpObjectHandle parent, MtpStorageID storage);
+
+ // Called to report success or failure of the SendObject file transfer.
+ void endSendObject(MtpObjectHandle handle, bool succeeded);
+
+ // Called to rescan a file, such as after an edit.
+ void rescanFile(const char* path, MtpObjectHandle handle, MtpObjectFormat format);
+
+ MtpObjectHandleList* getObjectList(MtpStorageID storageID, MtpObjectFormat format,
+ MtpObjectHandle parent);
+
+ int getNumObjects(MtpStorageID storageID, MtpObjectFormat format, MtpObjectHandle parent);
+
+ // callee should delete[] the results from these
+ // results can be NULL
+ MtpObjectFormatList* getSupportedPlaybackFormats();
+ MtpObjectFormatList* getSupportedCaptureFormats();
+ MtpObjectPropertyList* getSupportedObjectProperties(MtpObjectFormat format);
+ MtpDevicePropertyList* getSupportedDeviceProperties();
+
+ MtpResponseCode getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property,
+ MtpDataPacket& packet);
+
+ MtpResponseCode setObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property,
+ MtpDataPacket& packet);
+
+ MtpResponseCode getDevicePropertyValue(MtpDeviceProperty property, MtpDataPacket& packet);
+
+ MtpResponseCode setDevicePropertyValue(MtpDeviceProperty property, MtpDataPacket& packet);
+
+ MtpResponseCode resetDeviceProperty(MtpDeviceProperty property);
+
+ MtpResponseCode getObjectPropertyList(MtpObjectHandle handle, uint32_t format,
+ uint32_t property, int groupCode, int depth,
+ MtpDataPacket& packet);
+
+ MtpResponseCode getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info);
+
+ void* getThumbnail(MtpObjectHandle handle, size_t& outThumbSize);
+
+ MtpResponseCode getObjectFilePath(MtpObjectHandle handle, MtpStringBuffer& outFilePath,
+ int64_t& outFileLength, MtpObjectFormat& outFormat);
+
+ int openFilePath(const char* path, bool transcode);
+
+ MtpResponseCode beginDeleteObject(MtpObjectHandle handle);
+ void endDeleteObject(MtpObjectHandle handle, bool succeeded);
+
+ MtpObjectHandleList* getObjectReferences(MtpObjectHandle handle);
+
+ MtpResponseCode setObjectReferences(MtpObjectHandle handle, MtpObjectHandleList* references);
+
+ MtpProperty* getObjectPropertyDesc(MtpObjectProperty property, MtpObjectFormat format);
+
+ MtpProperty* getDevicePropertyDesc(MtpDeviceProperty property);
+
+ MtpResponseCode beginMoveObject(MtpObjectHandle handle, MtpObjectHandle newParent,
+ MtpStorageID newStorage);
+
+ void endMoveObject(MtpObjectHandle oldParent, MtpObjectHandle newParent,
+ MtpStorageID oldStorage, MtpStorageID newStorage, MtpObjectHandle handle,
+ bool succeeded);
+
+ MtpResponseCode beginCopyObject(MtpObjectHandle handle, MtpObjectHandle newParent,
+ MtpStorageID newStorage);
+ void endCopyObject(MtpObjectHandle handle, bool succeeded);
+};
+
+}; // namespace android
+
+#endif // _MTP_MOCK_DATABASE_H
diff --git a/media/mtp/tests/MtpFuzzer/MtpMockHandle.h b/media/mtp/tests/MtpFuzzer/MtpMockHandle.h
new file mode 100644
index 0000000..111485c
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/MtpMockHandle.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_MOCK_HANDLE_H
+#define _MTP_MOCK_HANDLE_H
+
+#include <vector>
+
+typedef std::vector<uint8_t> packet_t;
+
+namespace android {
+class MtpMockHandle : public IMtpHandle {
+private:
+ size_t mPacketNumber;
+ size_t mPacketOffset;
+ std::vector<packet_t> mPackets;
+
+public:
+ MtpMockHandle() : mPacketNumber(0), mPacketOffset(0) {}
+
+ void add_packet(packet_t pkt) { mPackets.push_back(pkt); }
+
+ // Return number of bytes read/written, or -1 and errno is set
+ int read(void *data, size_t len) {
+ if (mPacketNumber >= mPackets.size()) {
+ return 0;
+ } else {
+ int readAmt = 0;
+ packet_t pkt = mPackets[mPacketNumber];
+
+ ALOGD("%s: sz %zu, pkt %zu+%zu/%zu\n", __func__, len, mPacketNumber, mPacketOffset,
+ pkt.size());
+
+ // packet is bigger than what the caller can handle,
+ if (pkt.size() > len) {
+ memcpy(data, pkt.data() + mPacketOffset, len);
+
+ mPacketOffset += len;
+ readAmt = len;
+ // packet is equal or smaller than the caller buffer
+ } else {
+ memcpy(data, pkt.data() + mPacketOffset, pkt.size());
+
+ mPacketNumber++;
+ mPacketOffset = 0;
+ readAmt = pkt.size();
+ }
+
+ return readAmt;
+ }
+ }
+ int write(const void *data, size_t len) {
+ ALOGD("MockHandle %s: len=%zu\n", __func__, len);
+ // fake the write
+ return len;
+ }
+
+ // Return 0 if send/receive is successful, or -1 and errno is set
+ int receiveFile(mtp_file_range mfr, bool zero_packet) {
+ ALOGD("MockHandle %s\n", __func__);
+ return 0;
+ }
+ int sendFile(mtp_file_range mfr) {
+ ALOGD("MockHandle %s\n", __func__);
+ return 0;
+ }
+ int sendEvent(mtp_event me) {
+ ALOGD("MockHandle %s: len=%zu\n", __func__, me.length);
+ return 0;
+ }
+
+ // Return 0 if operation is successful, or -1 else
+ int start(bool ptp) { return 0; }
+
+ void close() {}
+
+ virtual ~MtpMockHandle() {}
+};
+}; // namespace android
+
+#endif // _MTP_MOCK_HANDLE_H
diff --git a/media/mtp/tests/MtpFuzzer/corpus/1-mtp-open_session.pkt b/media/mtp/tests/MtpFuzzer/corpus/1-mtp-open_session.pkt
new file mode 100644
index 0000000..38f8ed2
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/corpus/1-mtp-open_session.pkt
Binary files differ
diff --git a/media/mtp/tests/MtpFuzzer/corpus/2-mtp-get_device_info.pkt b/media/mtp/tests/MtpFuzzer/corpus/2-mtp-get_device_info.pkt
new file mode 100644
index 0000000..7759380
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/corpus/2-mtp-get_device_info.pkt
Binary files differ
diff --git a/media/mtp/tests/MtpFuzzer/corpus/3-mtp-get_object_handles.pkt b/media/mtp/tests/MtpFuzzer/corpus/3-mtp-get_object_handles.pkt
new file mode 100644
index 0000000..e88410f
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/corpus/3-mtp-get_object_handles.pkt
Binary files differ
diff --git a/media/mtp/tests/MtpFuzzer/corpus/4-mtp-get_object_info.pkt b/media/mtp/tests/MtpFuzzer/corpus/4-mtp-get_object_info.pkt
new file mode 100644
index 0000000..e283fb4
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/corpus/4-mtp-get_object_info.pkt
Binary files differ
diff --git a/media/mtp/tests/MtpFuzzer/corpus/5-mtp-send_object_info.pkt b/media/mtp/tests/MtpFuzzer/corpus/5-mtp-send_object_info.pkt
new file mode 100644
index 0000000..7627f88
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/corpus/5-mtp-send_object_info.pkt
Binary files differ
diff --git a/media/mtp/tests/MtpFuzzer/mtp_fuzzer.cpp b/media/mtp/tests/MtpFuzzer/mtp_fuzzer.cpp
new file mode 100644
index 0000000..f578462
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/mtp_fuzzer.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/unique_fd.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <string>
+
+#define LOG_TAG "MtpFuzzer"
+
+#include "IMtpHandle.h"
+#include "MtpMockDatabase.h"
+#include "MtpMockHandle.h"
+#include "MtpObjectInfo.h"
+#include "MtpServer.h"
+#include "MtpStorage.h"
+#include "MtpUtils.h"
+
+const char* storage_desc = "Fuzz Storage";
+// prefer tmpfs for file operations to avoid wearing out flash
+const char* storage_path = "/storage/fuzzer/0";
+const char* source_database = "srcdb/";
+
+namespace android {
+class MtpMockServer {
+public:
+ std::unique_ptr<MtpMockHandle> mHandle;
+ std::unique_ptr<MtpStorage> mStorage;
+ std::unique_ptr<MtpMockDatabase> mDatabase;
+ std::unique_ptr<MtpServer> mMtp;
+ int mStorageId;
+
+ MtpMockServer(const char* storage_path) : mStorageId(0) {
+ bool ptp = false;
+ const char* manu = "Google";
+ const char* model = "Pixel 3XL";
+ const char* version = "1.0";
+ const char* serial = "ABDEF1231";
+
+ // This is unused in our harness
+ int controlFd = -1;
+
+ mHandle = std::make_unique<MtpMockHandle>();
+ mStorage = std::make_unique<MtpStorage>(mStorageId, storage_path, storage_desc, true,
+ 0x200000000L);
+ mDatabase = std::make_unique<MtpMockDatabase>();
+ mDatabase->addStorage(mStorage.get());
+
+ mMtp = std::make_unique<MtpServer>(mDatabase.get(), controlFd, ptp, manu, model, version,
+ serial);
+ mMtp->addStorage(mStorage.get());
+
+ // clear the old handle first, so we don't leak memory
+ delete mMtp->mHandle;
+ mMtp->mHandle = mHandle.get();
+ }
+
+ void run() { mMtp->run(); }
+
+ int createDatabaseFromSourceDir(const char* fromPath, const char* toPath,
+ MtpObjectHandle parentHandle) {
+ int ret = 0;
+ std::string fromPathStr(fromPath);
+ std::string toPathStr(toPath);
+
+ DIR* dir = opendir(fromPath);
+ if (!dir) {
+ ALOGE("opendir %s failed", fromPath);
+ return -1;
+ }
+ if (fromPathStr[fromPathStr.size() - 1] != '/') fromPathStr += '/';
+ if (toPathStr[toPathStr.size() - 1] != '/') toPathStr += '/';
+
+ struct dirent* entry;
+ while ((entry = readdir(dir))) {
+ const char* name = entry->d_name;
+
+ // ignore "." and ".."
+ if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
+ continue;
+ }
+
+ std::string oldFile = fromPathStr + name;
+ std::string newFile = toPathStr + name;
+
+ if (entry->d_type == DT_DIR) {
+ ret += makeFolder(newFile.c_str());
+
+ MtpObjectInfo* objectInfo = new MtpObjectInfo(mDatabase->allocateObjectHandle());
+ objectInfo->mStorageID = mStorage->getStorageID();
+ objectInfo->mParent = parentHandle;
+ objectInfo->mFormat = MTP_FORMAT_ASSOCIATION; // folder
+ objectInfo->mName = strdup(name);
+ objectInfo->mKeywords = strdup("");
+
+ mDatabase->addObject(objectInfo);
+
+ ret += createDatabaseFromSourceDir(oldFile.c_str(), newFile.c_str(),
+ objectInfo->mHandle);
+ } else {
+ ret += copyFile(oldFile.c_str(), newFile.c_str());
+
+ MtpObjectInfo* objectInfo = new MtpObjectInfo(mDatabase->allocateObjectHandle());
+ objectInfo->mStorageID = mStorage->getStorageID();
+ objectInfo->mParent = parentHandle;
+ objectInfo->mFormat = MTP_FORMAT_TEXT;
+ objectInfo->mName = strdup(name);
+ objectInfo->mKeywords = strdup("");
+
+ mDatabase->addObject(objectInfo);
+ }
+ }
+
+ closedir(dir);
+ return ret;
+ }
+};
+}; // namespace android
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) __attribute__((optnone)) {
+ // reset our storage (from MtpUtils.h)
+ android::deletePath(storage_path);
+ android::makeFolder("/storage/fuzzer");
+ android::makeFolder(storage_path);
+
+ std::unique_ptr<android::MtpMockServer> mtp =
+ std::make_unique<android::MtpMockServer>(storage_path);
+
+ size_t off = 0;
+
+ // Packetize the input stream
+ for (size_t i = 0; i < size; i++) {
+ // A longer delimiter could be used, but this worked in practice
+ if (data[i] == '@') {
+ size_t pktsz = i - off;
+ if (pktsz > 0) {
+ packet_t pkt = packet_t((unsigned char*)data + off, (unsigned char*)data + i);
+ // insert into packet buffer
+ mtp->mHandle->add_packet(pkt);
+ off = i;
+ }
+ }
+ }
+
+ mtp->createDatabaseFromSourceDir(source_database, storage_path, MTP_PARENT_ROOT);
+ mtp->run();
+
+ return 0;
+}
diff --git a/media/mtp/tests/MtpFuzzer/mtp_fuzzer.dict b/media/mtp/tests/MtpFuzzer/mtp_fuzzer.dict
new file mode 100644
index 0000000..4c3f136
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/mtp_fuzzer.dict
@@ -0,0 +1,74 @@
+mtp_operation_get_device_info="\x01\x10"
+mtp_operation_open_session="\x02\x10"
+mtp_operation_close_session="\x03\x10"
+mtp_operation_get_storage_ids="\x04\x10"
+mtp_operation_get_storage_info="\x05\x10"
+mtp_operation_get_num_objects="\x06\x10"
+mtp_operation_get_object_handles="\x07\x10"
+mtp_operation_get_object_info="\x08\x10"
+mtp_operation_get_object="\x09\x10"
+mtp_operation_get_thumb="\x0A\x10"
+mtp_operation_delete_object="\x0B\x10"
+mtp_operation_send_object_info="\x0C\x10"
+mtp_operation_send_object="\x0D\x10"
+mtp_operation_initiate_capture="\x0E\x10"
+mtp_operation_format_store="\x0F\x10"
+mtp_operation_reset_device="\x10\x10"
+mtp_operation_self_test="\x11\x10"
+mtp_operation_set_object_protection="\x12\x10"
+mtp_operation_power_down="\x13\x10"
+mtp_operation_get_device_prop_desc="\x14\x10"
+mtp_operation_get_device_prop_value="\x15\x10"
+mtp_operation_set_device_prop_value="\x16\x10"
+mtp_operation_reset_device_prop_value="\x17\x10"
+mtp_operation_terminate_open_capture="\x18\x10"
+mtp_operation_move_object="\x19\x10"
+mtp_operation_copy_object="\x1A\x10"
+mtp_operation_get_partial_object="\x1B\x10"
+mtp_operation_initiate_open_capture="\x1C\x10"
+mtp_operation_get_object_props_supported="\x01\x98"
+mtp_operation_get_object_prop_desc="\x02\x98"
+mtp_operation_get_object_prop_value="\x03\x98"
+mtp_operation_set_object_prop_value="\x04\x98"
+mtp_operation_get_object_prop_list="\x05\x98"
+mtp_operation_set_object_prop_list="\x06\x98"
+mtp_operation_get_interdependent_prop_desc="\x07\x98"
+mtp_operation_send_object_prop_list="\x08\x98"
+mtp_operation_get_object_references="\x10\x98"
+mtp_operation_set_object_references="\x11\x98"
+mtp_operation_skip="\x20\x98"
+mtp_operation_get_partial_object_64="\xC1\x95"
+mtp_operation_send_partial_object="\xC2\x95"
+mtp_operation_truncate_object="\xC3\x95"
+mtp_operation_begin_edit_object="\xC4\x95"
+mtp_operation_end_edit_object="\xC5\x95"
+
+# Association (for example, a folder)
+mtp_format_association="\x01\x30"
+
+# types
+mtp_type_undefined="\x00\x00"
+mtp_type_int8="\x01\x00"
+mtp_type_uint8="\x02\x00"
+mtp_type_int16="\x03\x00"
+mtp_type_uint16="\x04\x00"
+mtp_type_int32="\x05\x00"
+mtp_type_uint32="\x06\x00"
+mtp_type_int64="\x07\x00"
+mtp_type_uint64="\x08\x00"
+mtp_type_int128="\x09\x00"
+mtp_type_uint128="\x0A\x00"
+mtp_type_aint8="\x01\x40"
+mtp_type_auint8="\x02\x40"
+mtp_type_aint16="\x03\x40"
+mtp_type_auint16="\x04\x40"
+mtp_type_aint32="\x05\x40"
+mtp_type_auint32="\x06\x40"
+mtp_type_aint64="\x07\x40"
+mtp_type_auint64="\x08\x40"
+mtp_type_aint128="\x09\x40"
+mtp_type_auint128="\x0A\x40"
+mtp_type_str="\xFF\xFF"
+
+# also used for max size (>4GB)
+mtp_parent_root="\xFF\xFF\xFF\xFF"
diff --git a/media/mtp/tests/PosixAsyncIOTest/Android.bp b/media/mtp/tests/PosixAsyncIOTest/Android.bp
new file mode 100644
index 0000000..09cf6b7
--- /dev/null
+++ b/media/mtp/tests/PosixAsyncIOTest/Android.bp
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_media_mtp_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_media_mtp_license"],
+}
+
+cc_test {
+ name: "posix_async_io_test",
+ test_suites: ["device-tests"],
+ srcs: ["PosixAsyncIO_test.cpp"],
+ shared_libs: [
+ "libbase",
+ "libmtp",
+ "liblog",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+}
diff --git a/media/mtp/tests/PosixAsyncIOTest/AndroidTest.xml b/media/mtp/tests/PosixAsyncIOTest/AndroidTest.xml
new file mode 100644
index 0000000..cbb10fb
--- /dev/null
+++ b/media/mtp/tests/PosixAsyncIOTest/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for posix_async_io_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="posix_async_io_test->/data/local/tmp/posix_async_io_test" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="posix_async_io_test" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/media/mtp/tests/PosixAsyncIO_test.cpp b/media/mtp/tests/PosixAsyncIOTest/PosixAsyncIO_test.cpp
similarity index 100%
rename from media/mtp/tests/PosixAsyncIO_test.cpp
rename to media/mtp/tests/PosixAsyncIOTest/PosixAsyncIO_test.cpp
diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp
index 05f6efa..8d527e9 100644
--- a/media/ndk/Android.bp
+++ b/media/ndk/Android.bp
@@ -149,7 +149,10 @@
export_header_lib_headers: ["jni_headers"],
- export_include_dirs: ["include"],
+ export_include_dirs: [
+ "include",
+ "include_platform",
+ ],
export_shared_lib_headers: [
"libgui",
@@ -207,6 +210,7 @@
cc_test {
name: "AImageReaderWindowHandleTest",
+ test_suites: ["device-tests"],
srcs: ["tests/AImageReaderWindowHandleTest.cpp"],
shared_libs: [
"libbinder",
@@ -266,5 +270,9 @@
},
},
- apex_available: ["com.android.media"],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media",
+ "com.android.media.swcodec",
+ ],
}
diff --git a/media/ndk/NdkImagePriv.h b/media/ndk/NdkImagePriv.h
index 0e8cbcb..05115b9 100644
--- a/media/ndk/NdkImagePriv.h
+++ b/media/ndk/NdkImagePriv.h
@@ -30,6 +30,26 @@
using namespace android;
+// Formats not listed in the public API, but still available to AImageReader
+enum AIMAGE_PRIVATE_FORMATS {
+ /**
+ * Unprocessed implementation-dependent raw
+ * depth measurements, opaque with 16 bit
+ * samples.
+ *
+ */
+
+ AIMAGE_FORMAT_RAW_DEPTH = 0x1002,
+
+ /**
+ * Device specific 10 bits depth RAW image format.
+ *
+ * <p>Unprocessed implementation-dependent raw depth measurements, opaque with 10 bit samples
+ * and device specific bit layout.</p>
+ */
+ AIMAGE_FORMAT_RAW_DEPTH10 = 0x1003,
+};
+
// TODO: this only supports ImageReader
struct AImage {
AImage(AImageReader* reader, int32_t format, uint64_t usage, BufferItem* buffer,
diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp
index 051466a..1067e24 100644
--- a/media/ndk/NdkImageReader.cpp
+++ b/media/ndk/NdkImageReader.cpp
@@ -21,7 +21,6 @@
#include "NdkImagePriv.h"
#include "NdkImageReaderPriv.h"
-#include <private/media/NdkImage.h>
#include <cutils/atomic.h>
#include <utils/Log.h>
@@ -73,6 +72,7 @@
case AIMAGE_FORMAT_Y8:
case AIMAGE_FORMAT_HEIC:
case AIMAGE_FORMAT_DEPTH_JPEG:
+ case AIMAGE_FORMAT_RAW_DEPTH10:
return true;
case AIMAGE_FORMAT_PRIVATE:
// For private format, cpu usage is prohibited.
@@ -103,6 +103,7 @@
case AIMAGE_FORMAT_Y8:
case AIMAGE_FORMAT_HEIC:
case AIMAGE_FORMAT_DEPTH_JPEG:
+ case AIMAGE_FORMAT_RAW_DEPTH10:
return 1;
case AIMAGE_FORMAT_PRIVATE:
return 0;
diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp
index af21a99..1ae2b44 100644
--- a/media/ndk/NdkMediaCodec.cpp
+++ b/media/ndk/NdkMediaCodec.cpp
@@ -19,7 +19,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "NdkMediaCodec"
-#include <media/NdkMediaCodec.h>
+#include <media/NdkMediaCodecPlatform.h>
#include <media/NdkMediaError.h>
#include <media/NdkMediaFormatPriv.h>
#include "NdkMediaCryptoPriv.h"
@@ -45,6 +45,10 @@
return AMEDIA_OK;
} else if (err == -EAGAIN) {
return (media_status_t) AMEDIACODEC_INFO_TRY_AGAIN_LATER;
+ } else if (err == NO_MEMORY) {
+ return AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE;
+ } else if (err == DEAD_OBJECT) {
+ return AMEDIACODEC_ERROR_RECLAIMED;
}
ALOGE("sf error code: %d", err);
return AMEDIA_ERROR_UNKNOWN;
@@ -255,7 +259,7 @@
break;
}
msg->findString("detail", &detail);
- ALOGE("Decoder reported error(0x%x), actionCode(%d), detail(%s)",
+ ALOGE("Codec reported error(0x%x), actionCode(%d), detail(%s)",
err, actionCode, detail.c_str());
Mutex::Autolock _l(mCodec->mAsyncCallbackLock);
@@ -308,7 +312,11 @@
extern "C" {
-static AMediaCodec * createAMediaCodec(const char *name, bool name_is_type, bool encoder) {
+static AMediaCodec * createAMediaCodec(const char *name,
+ bool name_is_type,
+ bool encoder,
+ pid_t pid = android::MediaCodec::kNoPid,
+ uid_t uid = android::MediaCodec::kNoUid) {
AMediaCodec *mData = new AMediaCodec();
mData->mLooper = new ALooper;
mData->mLooper->setName("NDK MediaCodec_looper");
@@ -322,9 +330,20 @@
return NULL;
}
if (name_is_type) {
- mData->mCodec = android::MediaCodec::CreateByType(mData->mLooper, name, encoder);
+ mData->mCodec = android::MediaCodec::CreateByType(
+ mData->mLooper,
+ name,
+ encoder,
+ nullptr /* err */,
+ pid,
+ uid);
} else {
- mData->mCodec = android::MediaCodec::CreateByComponentName(mData->mLooper, name);
+ mData->mCodec = android::MediaCodec::CreateByComponentName(
+ mData->mLooper,
+ name,
+ nullptr /* err */,
+ pid,
+ uid);
}
if (mData->mCodec == NULL) { // failed to create codec
AMediaCodec_delete(mData);
@@ -344,17 +363,38 @@
EXPORT
AMediaCodec* AMediaCodec_createCodecByName(const char *name) {
- return createAMediaCodec(name, false, false);
+ return createAMediaCodec(name, false /* name_is_type */, false /* encoder */);
}
EXPORT
AMediaCodec* AMediaCodec_createDecoderByType(const char *mime_type) {
- return createAMediaCodec(mime_type, true, false);
+ return createAMediaCodec(mime_type, true /* name_is_type */, false /* encoder */);
}
EXPORT
AMediaCodec* AMediaCodec_createEncoderByType(const char *name) {
- return createAMediaCodec(name, true, true);
+ return createAMediaCodec(name, true /* name_is_type */, true /* encoder */);
+}
+
+EXPORT
+AMediaCodec* AMediaCodec_createCodecByNameForClient(const char *name,
+ pid_t pid,
+ uid_t uid) {
+ return createAMediaCodec(name, false /* name_is_type */, false /* encoder */, pid, uid);
+}
+
+EXPORT
+AMediaCodec* AMediaCodec_createDecoderByTypeForClient(const char *mime_type,
+ pid_t pid,
+ uid_t uid) {
+ return createAMediaCodec(mime_type, true /* name_is_type */, false /* encoder */, pid, uid);
+}
+
+EXPORT
+AMediaCodec* AMediaCodec_createEncoderByTypeForClient(const char *name,
+ pid_t pid,
+ uid_t uid) {
+ return createAMediaCodec(name, true /* name_is_type */, true /* encoder */, pid, uid);
}
EXPORT
@@ -914,5 +954,13 @@
return AMEDIA_OK;
}
+EXPORT const char* AMEDIACODEC_KEY_HDR10_PLUS_INFO = AMEDIAFORMAT_KEY_HDR10_PLUS_INFO;
+EXPORT const char* AMEDIACODEC_KEY_LOW_LATENCY = AMEDIAFORMAT_KEY_LOW_LATENCY;
+EXPORT const char* AMEDIACODEC_KEY_OFFSET_TIME = "time-offset-us";
+EXPORT const char* AMEDIACODEC_KEY_REQUEST_SYNC_FRAME = "request-sync";
+EXPORT const char* AMEDIACODEC_KEY_SUSPEND = "drop-input-frames";
+EXPORT const char* AMEDIACODEC_KEY_SUSPEND_TIME = "drop-start-time-us";
+EXPORT const char* AMEDIACODEC_KEY_VIDEO_BITRATE = "video-bitrate";
+
} // extern "C"
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index 3af9771..6e9945d 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -380,12 +380,15 @@
}
Vector<uint8_t> session;
status_t status = mObj->mDrm->openSession(DrmPlugin::kSecurityLevelMax, session);
- if (status == OK) {
- mObj->mIds.push_front(session);
- List<idvec_t>::iterator iter = mObj->mIds.begin();
- sessionId->ptr = iter->array();
- sessionId->length = iter->size();
+ if (status != OK) {
+ sessionId->ptr = NULL;
+ sessionId->length = 0;
+ return translateStatus(status);
}
+ mObj->mIds.push_front(session);
+ List<idvec_t>::iterator iter = mObj->mIds.begin();
+ sessionId->ptr = iter->array();
+ sessionId->length = iter->size();
return AMEDIA_OK;
}
@@ -489,6 +492,7 @@
} else {
keySetId->ptr = NULL;
keySetId->length = 0;
+ return translateStatus(status);
}
return AMEDIA_OK;
}
diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp
index 0c65e9e..07fc5de 100644
--- a/media/ndk/NdkMediaExtractor.cpp
+++ b/media/ndk/NdkMediaExtractor.cpp
@@ -419,6 +419,7 @@
EXPORT
media_status_t AMediaExtractor_getSampleFormat(AMediaExtractor *ex, AMediaFormat *fmt) {
+ ALOGV("AMediaExtractor_getSampleFormat");
if (fmt == NULL) {
return AMEDIA_ERROR_INVALID_PARAMETER;
}
@@ -428,6 +429,9 @@
if (err != OK) {
return translate_error(err);
}
+#ifdef LOG_NDEBUG
+ sampleMeta->dumpToLog();
+#endif
sp<AMessage> meta;
AMediaFormat_getFormat(fmt, &meta);
@@ -483,6 +487,19 @@
meta->setBuffer(AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO, audioPresentationsData);
}
+ int64_t val64;
+ if (sampleMeta->findInt64(kKeySampleFileOffset, &val64)) {
+ meta->setInt64("sample-file-offset", val64);
+ ALOGV("SampleFileOffset Found");
+ }
+ if (sampleMeta->findInt64(kKeyLastSampleIndexInChunk, &val64)) {
+ meta->setInt64("last-sample-index-in-chunk" /*AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK*/,
+ val64);
+ ALOGV("kKeyLastSampleIndexInChunk Found");
+ }
+
+ ALOGV("AMediaFormat_toString:%s", AMediaFormat_toString(fmt));
+
return AMEDIA_OK;
}
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index 8680641..c1793ce 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -334,6 +334,7 @@
EXPORT const char* AMEDIAFORMAT_KEY_IS_SYNC_FRAME = "is-sync-frame";
EXPORT const char* AMEDIAFORMAT_KEY_I_FRAME_INTERVAL = "i-frame-interval";
EXPORT const char* AMEDIAFORMAT_KEY_LANGUAGE = "language";
+EXPORT const char* AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK = "last-sample-index-in-chunk";
EXPORT const char* AMEDIAFORMAT_KEY_LATENCY = "latency";
EXPORT const char* AMEDIAFORMAT_KEY_LEVEL = "level";
EXPORT const char* AMEDIAFORMAT_KEY_LOCATION = "location";
@@ -359,17 +360,21 @@
EXPORT const char* AMEDIAFORMAT_KEY_PUSH_BLANK_BUFFERS_ON_STOP = "push-blank-buffers-on-shutdown";
EXPORT const char* AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER = "repeat-previous-frame-after";
EXPORT const char* AMEDIAFORMAT_KEY_ROTATION = "rotation-degrees";
+EXPORT const char* AMEDIAFORMAT_KEY_SAMPLE_FILE_OFFSET = "sample-file-offset";
EXPORT const char* AMEDIAFORMAT_KEY_SAMPLE_RATE = "sample-rate";
+EXPORT const char* AMEDIAFORMAT_KEY_SAMPLE_TIME_BEFORE_APPEND = "sample-time-before-append";
EXPORT const char* AMEDIAFORMAT_KEY_SAR_HEIGHT = "sar-height";
EXPORT const char* AMEDIAFORMAT_KEY_SAR_WIDTH = "sar-width";
EXPORT const char* AMEDIAFORMAT_KEY_SEI = "sei";
EXPORT const char* AMEDIAFORMAT_KEY_SLICE_HEIGHT = "slice-height";
+EXPORT const char* AMEDIAFORMAT_KEY_SLOW_MOTION_MARKERS = "slow-motion-markers";
EXPORT const char* AMEDIAFORMAT_KEY_STRIDE = "stride";
EXPORT const char* AMEDIAFORMAT_KEY_TARGET_TIME = "target-time";
EXPORT const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_COUNT = "temporal-layer-count";
EXPORT const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID = "temporal-layer-id";
EXPORT const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYERING = "ts-schema";
EXPORT const char* AMEDIAFORMAT_KEY_TEXT_FORMAT_DATA = "text-format-data";
+EXPORT const char* AMEDIAFORMAT_KEY_THUMBNAIL_CSD_AV1C = "thumbnail-csd-av1c";
EXPORT const char* AMEDIAFORMAT_KEY_THUMBNAIL_CSD_HEVC = "thumbnail-csd-hevc";
EXPORT const char* AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT = "thumbnail-height";
EXPORT const char* AMEDIAFORMAT_KEY_THUMBNAIL_TIME = "thumbnail-time";
@@ -381,7 +386,17 @@
EXPORT const char* AMEDIAFORMAT_KEY_TRACK_ID = "track-id";
EXPORT const char* AMEDIAFORMAT_KEY_TRACK_INDEX = "track-index";
EXPORT const char* AMEDIAFORMAT_KEY_VALID_SAMPLES = "valid-samples";
+EXPORT const char* AMEDIAFORMAT_VIDEO_QP_B_MAX = "video-qp-b-max";
+EXPORT const char* AMEDIAFORMAT_VIDEO_QP_B_MIN = "video-qp-b-min";
+EXPORT const char* AMEDIAFORMAT_VIDEO_QP_I_MAX = "video-qp-i-max";
+EXPORT const char* AMEDIAFORMAT_VIDEO_QP_I_MIN = "video-qp-i-min";
+EXPORT const char* AMEDIAFORMAT_VIDEO_QP_MAX = "video-qp-max";
+EXPORT const char* AMEDIAFORMAT_VIDEO_QP_MIN = "video-qp-min";
+EXPORT const char* AMEDIAFORMAT_VIDEO_QP_P_MAX = "video-qp-p-max";
+EXPORT const char* AMEDIAFORMAT_VIDEO_QP_P_MIN = "video-qp-p-min";
EXPORT const char* AMEDIAFORMAT_KEY_WIDTH = "width";
+EXPORT const char* AMEDIAFORMAT_KEY_XMP_OFFSET = "xmp-offset";
+EXPORT const char* AMEDIAFORMAT_KEY_XMP_SIZE = "xmp-size";
EXPORT const char* AMEDIAFORMAT_KEY_YEAR = "year";
} // extern "C"
diff --git a/media/ndk/NdkMediaMuxer.cpp b/media/ndk/NdkMediaMuxer.cpp
index d1992bf..1965e62 100644
--- a/media/ndk/NdkMediaMuxer.cpp
+++ b/media/ndk/NdkMediaMuxer.cpp
@@ -17,28 +17,24 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "NdkMediaMuxer"
-
-#include <media/NdkMediaMuxer.h>
+#include <android_util_Binder.h>
+#include <jni.h>
+#include <media/IMediaHTTPService.h>
#include <media/NdkMediaCodec.h>
#include <media/NdkMediaErrorPriv.h>
#include <media/NdkMediaFormatPriv.h>
-
-
-#include <utils/Log.h>
-#include <utils/StrongPointer.h>
+#include <media/NdkMediaMuxer.h>
+#include <media/stagefright/MediaAppender.h>
+#include <media/stagefright/MediaMuxer.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/MediaMuxer.h>
-#include <media/IMediaHTTPService.h>
-#include <android_util_Binder.h>
-
-#include <jni.h>
+#include <utils/Log.h>
+#include <utils/StrongPointer.h>
using namespace android;
struct AMediaMuxer {
- sp<MediaMuxer> mImpl;
-
+ sp<MediaMuxerBase> mImpl;
};
extern "C" {
@@ -46,8 +42,15 @@
EXPORT
AMediaMuxer* AMediaMuxer_new(int fd, OutputFormat format) {
ALOGV("ctor");
- AMediaMuxer *mData = new AMediaMuxer();
- mData->mImpl = new MediaMuxer(fd, (android::MediaMuxer::OutputFormat)format);
+ AMediaMuxer *mData = new (std::nothrow) AMediaMuxer();
+ if (mData == nullptr) {
+ return nullptr;
+ }
+ mData->mImpl = new (std::nothrow) MediaMuxer(fd, (android::MediaMuxer::OutputFormat)format);
+ if (mData->mImpl == nullptr) {
+ delete mData;
+ return nullptr;
+ }
return mData;
}
@@ -94,6 +97,34 @@
muxer->mImpl->writeSampleData(buf, trackIdx, info->presentationTimeUs, info->flags));
}
+EXPORT
+AMediaMuxer* AMediaMuxer_append(int fd, AppendMode mode) {
+ ALOGV("append");
+ AMediaMuxer* mData = new (std::nothrow) AMediaMuxer();
+ if (mData == nullptr) {
+ return nullptr;
+ }
+ mData->mImpl = MediaAppender::create(fd, (android::MediaAppender::AppendMode)mode);
+ if (mData->mImpl == nullptr) {
+ delete mData;
+ return nullptr;
+ }
+ return mData;
+}
+
+EXPORT
+ssize_t AMediaMuxer_getTrackCount(AMediaMuxer* muxer) {
+ return muxer->mImpl->getTrackCount();
+}
+
+EXPORT
+AMediaFormat* AMediaMuxer_getTrackFormat(AMediaMuxer* muxer, size_t idx) {
+ sp<AMessage> format = muxer->mImpl->getTrackFormat(idx);
+ if (format != nullptr) {
+ return AMediaFormat_fromMsg(&format);
+ }
+ return nullptr;
+}
} // extern "C"
diff --git a/media/ndk/TEST_MAPPING b/media/ndk/TEST_MAPPING
new file mode 100644
index 0000000..e420812
--- /dev/null
+++ b/media/ndk/TEST_MAPPING
@@ -0,0 +1,7 @@
+// mappings for frameworks/av/media/ndk
+{
+ "presubmit": [
+ { "name": "AImageReaderWindowHandleTest" },
+ { "name": "libmediandk_test" }
+ ]
+}
diff --git a/media/ndk/include/media/NdkImage.h b/media/ndk/include/media/NdkImage.h
index e19dd3a..71bc6d9 100644
--- a/media/ndk/include/media/NdkImage.h
+++ b/media/ndk/include/media/NdkImage.h
@@ -50,7 +50,10 @@
*/
typedef struct AImage AImage;
-// Formats not listed here will not be supported by AImageReader
+/**
+ * AImage supported formats: AImageReader only guarantees the support for the formats
+ * listed here.
+ */
enum AIMAGE_FORMATS {
/**
* 32 bits RGBA format, 8 bits for each of the four channels.
@@ -813,7 +816,7 @@
* Available since API level 26.
*
* @param image the {@link AImage} of interest.
- * @param outBuffer The memory area pointed to by buffer will contain the acquired AHardwareBuffer
+ * @param buffer The memory area pointed to by buffer will contain the acquired AHardwareBuffer
* handle.
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
diff --git a/media/ndk/include/media/NdkImageReader.h b/media/ndk/include/media/NdkImageReader.h
index d86f3c7..4bd7f2a 100644
--- a/media/ndk/include/media/NdkImageReader.h
+++ b/media/ndk/include/media/NdkImageReader.h
@@ -328,10 +328,10 @@
* still acquire images from this {@link AImageReader} and access {@link AHardwareBuffer} via
* {@link AImage_getHardwareBuffer()}. The {@link AHardwareBuffer} gained this way can then
* be passed back to hardware (such as GPU or hardware encoder if supported) for future processing.
- * For example, you can obtain an {@link EGLClientBuffer} from the {@link AHardwareBuffer} by using
- * {@link eglGetNativeClientBufferANDROID} extension and pass that {@link EGLClientBuffer} to {@link
- * eglCreateImageKHR} to create an {@link EGLImage} resource type, which may then be bound to a
- * texture via {@link glEGLImageTargetTexture2DOES} on supported devices. This can be useful for
+ * For example, you can obtain an EGLClientBuffer from the {@link AHardwareBuffer} by using
+ * eglGetNativeClientBufferANDROID extension and pass that EGLClientBuffer to
+ * eglCreateImageKHR to create an EGLImage resource type, which may then be bound to a
+ * texture via glEGLImageTargetTexture2DOES on supported devices. This can be useful for
* transporting textures that may be shared cross-process.</p>
* <p>In general, when software access to image data is not necessary, an {@link AImageReader}
* created with {@link AIMAGE_FORMAT_PRIVATE} format is more efficient, compared with {@link
@@ -339,7 +339,7 @@
*
* <p>Note that not all format and usage flag combination is supported by the {@link AImageReader},
* especially if \c format is {@link AIMAGE_FORMAT_PRIVATE}, \c usage must not include either
- * {@link AHARDWAREBUFFER_USAGE_READ_RARELY} or {@link AHARDWAREBUFFER_USAGE_READ_OFTEN}</p>
+ * {@link AHARDWAREBUFFER_USAGE_CPU_READ_RARELY} or {@link AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN}</p>
*
* @param width The default width in pixels of the Images that this reader will produce.
* @param height The default height in pixels of the Images that this reader will produce.
@@ -358,7 +358,7 @@
* <th>Compatible usage flags</th>
* </tr>
* <tr>
- * <td>non-{@link AIMAGE_FORMAT_PRIVATE PRIVATE} formats defined in {@link AImage.h}
+ * <td>non-{@link AIMAGE_FORMAT_PRIVATE} formats defined in {@link NdkImage.h}
* </td>
* <td>{@link AHARDWAREBUFFER_USAGE_CPU_READ_RARELY} or
* {@link AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN}</td>
@@ -441,6 +441,10 @@
AImageReader* reader,
AHardwareBuffer* buffer);
+/**
+ * A listener to the AHardwareBuffer removal event, use
+ * {@link AImageReader_setBufferRemovedListener} to register the listener object to AImageReader.
+ */
typedef struct AImageReader_BufferRemovedListener {
/// Optional application context passed as the first parameter of the callback.
void* context;
diff --git a/media/ndk/include/media/NdkMediaCodec.h b/media/ndk/include/media/NdkMediaCodec.h
index 60cdd32..519148e 100644
--- a/media/ndk/include/media/NdkMediaCodec.h
+++ b/media/ndk/include/media/NdkMediaCodec.h
@@ -570,6 +570,14 @@
*/
media_status_t AMediaCodecCryptoInfo_getEncryptedBytes(AMediaCodecCryptoInfo*, size_t *dst) __INTRODUCED_IN(21);
+extern const char* AMEDIACODEC_KEY_HDR10_PLUS_INFO __INTRODUCED_IN(31);
+extern const char* AMEDIACODEC_KEY_LOW_LATENCY __INTRODUCED_IN(31);
+extern const char* AMEDIACODEC_KEY_OFFSET_TIME __INTRODUCED_IN(31);
+extern const char* AMEDIACODEC_KEY_REQUEST_SYNC_FRAME __INTRODUCED_IN(31);
+extern const char* AMEDIACODEC_KEY_SUSPEND __INTRODUCED_IN(31);
+extern const char* AMEDIACODEC_KEY_SUSPEND_TIME __INTRODUCED_IN(31);
+extern const char* AMEDIACODEC_KEY_VIDEO_BITRATE __INTRODUCED_IN(31);
+
__END_DECLS
#endif //_NDK_MEDIA_CODEC_H
diff --git a/media/ndk/include/media/NdkMediaError.h b/media/ndk/include/media/NdkMediaError.h
index 2be1d6e..02fdc79 100644
--- a/media/ndk/include/media/NdkMediaError.h
+++ b/media/ndk/include/media/NdkMediaError.h
@@ -40,7 +40,11 @@
__BEGIN_DECLS
+/**
+ * Media error message types returned from NDK media functions.
+ */
typedef enum {
+ /** The requested media operation completed successfully. */
AMEDIA_OK = 0,
/**
@@ -55,14 +59,34 @@
AMEDIACODEC_ERROR_RECLAIMED = 1101,
AMEDIA_ERROR_BASE = -10000,
+
+ /** The called media function failed with an unknown error. */
AMEDIA_ERROR_UNKNOWN = AMEDIA_ERROR_BASE,
+
+ /** The input media data is corrupt or incomplete. */
AMEDIA_ERROR_MALFORMED = AMEDIA_ERROR_BASE - 1,
+
+ /** The required operation or media formats are not supported. */
AMEDIA_ERROR_UNSUPPORTED = AMEDIA_ERROR_BASE - 2,
+
+ /** An invalid (or already closed) object is used in the function call. */
AMEDIA_ERROR_INVALID_OBJECT = AMEDIA_ERROR_BASE - 3,
+
+ /** At least one of the invalid parameters is used. */
AMEDIA_ERROR_INVALID_PARAMETER = AMEDIA_ERROR_BASE - 4,
+
+ /** The media object is not in the right state for the required operation. */
AMEDIA_ERROR_INVALID_OPERATION = AMEDIA_ERROR_BASE - 5,
+
+ /** Media stream ends while processing the requested operation. */
AMEDIA_ERROR_END_OF_STREAM = AMEDIA_ERROR_BASE - 6,
+
+ /** An Error occurred when the Media object is carrying IO operation. */
AMEDIA_ERROR_IO = AMEDIA_ERROR_BASE - 7,
+
+ /** The required operation would have to be blocked (on I/O or others),
+ * but blocking is not enabled.
+ */
AMEDIA_ERROR_WOULD_BLOCK = AMEDIA_ERROR_BASE - 8,
AMEDIA_DRM_ERROR_BASE = -20000,
@@ -77,10 +101,20 @@
AMEDIA_DRM_LICENSE_EXPIRED = AMEDIA_DRM_ERROR_BASE - 9,
AMEDIA_IMGREADER_ERROR_BASE = -30000,
+
+ /** There are no more image buffers to read/write image data. */
AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE = AMEDIA_IMGREADER_ERROR_BASE - 1,
+
+ /** The AImage object has used up the allowed maximum image buffers. */
AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED = AMEDIA_IMGREADER_ERROR_BASE - 2,
+
+ /** The required image buffer could not be locked to read. */
AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE = AMEDIA_IMGREADER_ERROR_BASE - 3,
+
+ /** The media data or buffer could not be unlocked. */
AMEDIA_IMGREADER_CANNOT_UNLOCK_IMAGE = AMEDIA_IMGREADER_ERROR_BASE - 4,
+
+ /** The media/buffer needs to be locked to perform the required operation. */
AMEDIA_IMGREADER_IMAGE_NOT_LOCKED = AMEDIA_IMGREADER_ERROR_BASE - 5,
} media_status_t;
diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h
index 0ce08c1..fbd855d 100644
--- a/media/ndk/include/media/NdkMediaFormat.h
+++ b/media/ndk/include/media/NdkMediaFormat.h
@@ -302,6 +302,24 @@
*/
extern const char* AMEDIAFORMAT_KEY_LOW_LATENCY __INTRODUCED_IN(30);
+extern const char* AMEDIAFORMAT_KEY_HDR10_PLUS_INFO __INTRODUCED_IN(31);
+extern const char* AMEDIAFORMAT_KEY_SLOW_MOTION_MARKERS __INTRODUCED_IN(31);
+extern const char* AMEDIAFORMAT_KEY_THUMBNAIL_CSD_AV1C __INTRODUCED_IN(31);
+extern const char* AMEDIAFORMAT_KEY_XMP_OFFSET __INTRODUCED_IN(31);
+extern const char* AMEDIAFORMAT_KEY_XMP_SIZE __INTRODUCED_IN(31);
+extern const char* AMEDIAFORMAT_KEY_SAMPLE_FILE_OFFSET __INTRODUCED_IN(31);
+extern const char* AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK __INTRODUCED_IN(31);
+extern const char* AMEDIAFORMAT_KEY_SAMPLE_TIME_BEFORE_APPEND __INTRODUCED_IN(31);
+
+extern const char* AMEDIAFORMAT_VIDEO_QP_B_MAX __INTRODUCED_IN(31);
+extern const char* AMEDIAFORMAT_VIDEO_QP_B_MIN __INTRODUCED_IN(31);
+extern const char* AMEDIAFORMAT_VIDEO_QP_I_MAX __INTRODUCED_IN(31);
+extern const char* AMEDIAFORMAT_VIDEO_QP_I_MIN __INTRODUCED_IN(31);
+extern const char* AMEDIAFORMAT_VIDEO_QP_MAX __INTRODUCED_IN(31);
+extern const char* AMEDIAFORMAT_VIDEO_QP_MIN __INTRODUCED_IN(31);
+extern const char* AMEDIAFORMAT_VIDEO_QP_P_MAX __INTRODUCED_IN(31);
+extern const char* AMEDIAFORMAT_VIDEO_QP_P_MIN __INTRODUCED_IN(31);
+
__END_DECLS
#endif // _NDK_MEDIA_FORMAT_H
diff --git a/media/ndk/include/media/NdkMediaMuxer.h b/media/ndk/include/media/NdkMediaMuxer.h
index 519e249..d7eccb8 100644
--- a/media/ndk/include/media/NdkMediaMuxer.h
+++ b/media/ndk/include/media/NdkMediaMuxer.h
@@ -54,6 +54,17 @@
AMEDIAMUXER_OUTPUT_FORMAT_THREE_GPP = 2,
} OutputFormat;
+typedef enum {
+ /* Last group of pictures(GOP) of video track can be incomplete, so it would be safe to
+ * scrap that and rewrite. If both audio and video tracks are present in a file, then
+ * samples of audio track after last GOP of video would be scrapped too.
+ * If only audio track is present, then no sample would be discarded.
+ */
+ AMEDIAMUXER_APPEND_IGNORE_LAST_VIDEO_GOP = 0,
+ // Keep all existing samples as it is and append new samples after that only.
+ AMEDIAMUXER_APPEND_TO_EXISTING_DATA = 1,
+} AppendMode;
+
/**
* Create new media muxer.
*
@@ -138,6 +149,46 @@
size_t trackIdx, const uint8_t *data,
const AMediaCodecBufferInfo *info) __INTRODUCED_IN(21);
+/**
+ * Creates a new media muxer for appending data to an existing MPEG4 file.
+ * This is a synchronous API call and could take a while to return if the existing file is large.
+ * Only works for MPEG4 files matching one of the following characteristics:
+ * <ul>
+ * <li>a single audio track.</li>
+ * <li>a single video track.</li>
+ * <li>a single audio and a single video track.</li>
+ * </ul>
+ * @param fd Must be opened with read and write permission. Does not take ownership of
+ * this fd i.e., caller is responsible for closing fd.
+ * @param mode Specifies how data will be appended; the AppendMode enum describes
+ * the possible methods for appending..
+ * @return Pointer to AMediaMuxer if the file(fd) has tracks already, otherwise, nullptr.
+ * {@link AMediaMuxer_delete} should be used to free the returned pointer.
+ *
+ * Available since API level 31.
+ */
+AMediaMuxer* AMediaMuxer_append(int fd, AppendMode mode) __INTRODUCED_IN(31);
+
+/**
+ * Returns the number of tracks added in the file passed to {@link AMediaMuxer_new} or
+ * the number of existing tracks in the file passed to {@link AMediaMuxer_append}.
+ * Should be called in INITIALIZED or STARTED state, otherwise returns -1.
+ *
+ * Available since API level 31.
+ */
+ssize_t AMediaMuxer_getTrackCount(AMediaMuxer*) __INTRODUCED_IN(31);
+
+/**
+ * Returns AMediaFormat of the added track with index idx in the file passed to
+ * {@link AMediaMuxer_new} or the AMediaFormat of the existing track with index idx
+ * in the file passed to {@link AMediaMuxer_append}.
+ * Should be called in INITIALIZED or STARTED state, otherwise returns nullptr.
+ * {@link AMediaFormat_delete} should be used to free the returned pointer.
+ *
+ * Available since API level 31.
+ */
+AMediaFormat* AMediaMuxer_getTrackFormat(AMediaMuxer* muxer, size_t idx) __INTRODUCED_IN(31);
+
__END_DECLS
#endif // _NDK_MEDIA_MUXER_H
diff --git a/media/ndk/include/private/media/NdkImage.h b/media/ndk/include/private/media/NdkImage.h
deleted file mode 100644
index 4368a56..0000000
--- a/media/ndk/include/private/media/NdkImage.h
+++ /dev/null
@@ -1,30 +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.
- */
-
-#ifndef _PRIVATE_MEDIA_NDKIMAGE_H_
-#define _PRIVATE_MEDIA_NDKIMAGE_H_
-// Formats not listed in the public API, but still available to AImageReader
-enum AIMAGE_PRIVATE_FORMATS {
- /**
- * Unprocessed implementation-dependent raw
- * depth measurements, opaque with 16 bit
- * samples.
- *
- */
-
- AIMAGE_FORMAT_RAW_DEPTH = 0x1002,
-};
-#endif // _PRIVATE_MEDIA_NDKIMAGE
diff --git a/media/ndk/include_platform/media/NdkMediaCodecPlatform.h b/media/ndk/include_platform/media/NdkMediaCodecPlatform.h
new file mode 100644
index 0000000..6edd712
--- /dev/null
+++ b/media/ndk/include_platform/media/NdkMediaCodecPlatform.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NDK_MEDIA_CODEC_PLATFORM_H
+#define _NDK_MEDIA_CODEC_PLATFORM_H
+
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+#include <media/NdkMediaCodec.h>
+
+__BEGIN_DECLS
+
+/**
+ * Special uid and pid values used with AMediaCodec_createCodecByNameForClient,
+ * AMediaCodec_createDecoderByTypeForClient and AMediaCodec_createEncoderByTypeForClient.
+ *
+ * Introduced in API 31.
+ */
+enum {
+ /**
+ * Uid value to indicate using calling uid.
+ */
+ AMEDIACODEC_CALLING_UID = -1,
+ /**
+ * Pid value to indicate using calling pid.
+ */
+ AMEDIACODEC_CALLING_PID = -1,
+};
+
+/**
+ * Create codec by name on behalf of a client.
+ *
+ * The usage is similar to AMediaCodec_createCodecByName(), except that the codec instance
+ * will be attributed to the client of {uid, pid}, instead of the caller.
+ *
+ * Only certain privileged users are allowed to specify {uid, pid} that's different from the
+ * caller's. Without the privilege, this API will behave the same as
+ * AMediaCodec_createCodecByName().
+ *
+ * Available since API level 31.
+ */
+AMediaCodec* AMediaCodec_createCodecByNameForClient(const char *name,
+ pid_t pid,
+ uid_t uid) __INTRODUCED_IN(31);
+
+/**
+ * Create codec by mime type on behalf of a client.
+ *
+ * The usage is similar to AMediaCodec_createDecoderByType(), except that the codec instance
+ * will be attributed to the client of {uid, pid}, instead of the caller.
+ *
+ * Only certain privileged users are allowed to specify {uid, pid} that's different from the
+ * caller's. Without the privilege, this API will behave the same as
+ * AMediaCodec_createDecoderByType().
+ *
+ * Available since API level 31.
+ */
+AMediaCodec* AMediaCodec_createDecoderByTypeForClient(const char *mime_type,
+ pid_t pid,
+ uid_t uid) __INTRODUCED_IN(31);
+
+/**
+ * Create encoder by name on behalf of a client.
+ *
+ * The usage is similar to AMediaCodec_createEncoderByType(), except that the codec instance
+ * will be attributed to the client of {uid, pid}, instead of the caller.
+ *
+ * Only certain privileged users are allowed to specify {uid, pid} that's different from the
+ * caller's. Without the privilege, this API will behave the same as
+ * AMediaCodec_createEncoderByType().
+ *
+ * Available since API level 31.
+ */
+AMediaCodec* AMediaCodec_createEncoderByTypeForClient(const char *mime_type,
+ pid_t pid,
+ uid_t uid) __INTRODUCED_IN(31);
+
+__END_DECLS
+
+#endif //_NDK_MEDIA_CODEC_PLATFORM_H
+
+/** @} */
diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt
index 29f1da8..7e9e57e 100644
--- a/media/ndk/libmediandk.map.txt
+++ b/media/ndk/libmediandk.map.txt
@@ -27,6 +27,13 @@
AImage_getPlaneRowStride; # introduced=24
AImage_getTimestamp; # introduced=24
AImage_getWidth; # introduced=24
+ AMEDIACODEC_KEY_HDR10_PLUS_INFO; # var introduced=31
+ AMEDIACODEC_KEY_LOW_LATENCY; # var introduced=31
+ AMEDIACODEC_KEY_OFFSET_TIME; # var introduced=31
+ AMEDIACODEC_KEY_REQUEST_SYNC_FRAME; # var introduced=31
+ AMEDIACODEC_KEY_SUSPEND; # var introduced=31
+ AMEDIACODEC_KEY_SUSPEND_TIME; # var introduced=31
+ AMEDIACODEC_KEY_VIDEO_BITRATE; # var introduced=31
AMEDIAFORMAT_KEY_AAC_DRC_ATTENUATION_FACTOR; # var introduced=28
AMEDIAFORMAT_KEY_AAC_DRC_BOOST_FACTOR; # var introduced=28
AMEDIAFORMAT_KEY_AAC_DRC_HEAVY_COMPRESSION; # var introduced=28
@@ -91,6 +98,7 @@
AMEDIAFORMAT_KEY_GRID_ROWS; # var introduced=28
AMEDIAFORMAT_KEY_HAPTIC_CHANNEL_COUNT; # var introduced=29
AMEDIAFORMAT_KEY_HDR_STATIC_INFO; # var introduced=28
+ AMEDIAFORMAT_KEY_HDR10_PLUS_INFO; # var introduced=31
AMEDIAFORMAT_KEY_HEIGHT; # var introduced=21
AMEDIAFORMAT_KEY_ICC_PROFILE; # var introduced=29
AMEDIAFORMAT_KEY_INTRA_REFRESH_PERIOD; # var introduced=28
@@ -101,6 +109,7 @@
AMEDIAFORMAT_KEY_IS_SYNC_FRAME; # var introduced=29
AMEDIAFORMAT_KEY_I_FRAME_INTERVAL; # var introduced=21
AMEDIAFORMAT_KEY_LANGUAGE; # var introduced=21
+ AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK; # var introduced=31
AMEDIAFORMAT_KEY_LATENCY; # var introduced=28
AMEDIAFORMAT_KEY_LEVEL; # var introduced=28
AMEDIAFORMAT_KEY_LOCATION; # var introduced=29
@@ -126,17 +135,21 @@
AMEDIAFORMAT_KEY_PUSH_BLANK_BUFFERS_ON_STOP; # var introduced=21
AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER; # var introduced=21
AMEDIAFORMAT_KEY_ROTATION; # var introduced=28
+ AMEDIAFORMAT_KEY_SAMPLE_FILE_OFFSET; # var introduced=31
+ AMEDIAFORMAT_KEY_SAMPLE_TIME_BEFORE_APPEND; # var introduced=31
AMEDIAFORMAT_KEY_SAMPLE_RATE; # var introduced=21
AMEDIAFORMAT_KEY_SAR_HEIGHT; # var introduced=29
AMEDIAFORMAT_KEY_SAR_WIDTH; # var introduced=29
AMEDIAFORMAT_KEY_SEI; # var introduced=28
AMEDIAFORMAT_KEY_SLICE_HEIGHT; # var introduced=28
+ AMEDIAFORMAT_KEY_SLOW_MOTION_MARKERS; # var introduced=31
AMEDIAFORMAT_KEY_STRIDE; # var introduced=21
AMEDIAFORMAT_KEY_TARGET_TIME; # var introduced=29
AMEDIAFORMAT_KEY_TEMPORAL_LAYER_COUNT; # var introduced=29
AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID; # var introduced=28
AMEDIAFORMAT_KEY_TEMPORAL_LAYERING; # var introduced=28
AMEDIAFORMAT_KEY_TEXT_FORMAT_DATA; # var introduced=29
+ AMEDIAFORMAT_KEY_THUMBNAIL_CSD_AV1C; # var introduced=31
AMEDIAFORMAT_KEY_THUMBNAIL_CSD_HEVC; # var introduced=29
AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT; # var introduced=29
AMEDIAFORMAT_KEY_THUMBNAIL_TIME; # var introduced=29
@@ -149,6 +162,8 @@
AMEDIAFORMAT_KEY_TRACK_ID; # var introduced=28
AMEDIAFORMAT_KEY_VALID_SAMPLES; # var introduced=29
AMEDIAFORMAT_KEY_WIDTH; # var introduced=21
+ AMEDIAFORMAT_KEY_XMP_OFFSET; # var introduced=31
+ AMEDIAFORMAT_KEY_XMP_SIZE; # var introduced=31
AMEDIAFORMAT_KEY_YEAR; # var introduced=29
AMediaCodecActionCode_isRecoverable; # introduced=28
AMediaCodecActionCode_isTransient; # introduced=28
@@ -163,8 +178,11 @@
AMediaCodecCryptoInfo_setPattern; # introduced=24
AMediaCodec_configure;
AMediaCodec_createCodecByName;
+ AMediaCodec_createCodecByNameForClient; # apex # introduced=31
AMediaCodec_createDecoderByType;
+ AMediaCodec_createDecoderByTypeForClient; # apex # introduced=31
AMediaCodec_createEncoderByType;
+ AMediaCodec_createEncoderByTypeForClient; # apex # introduced=31
AMediaCodec_delete;
AMediaCodec_dequeueInputBuffer;
AMediaCodec_dequeueOutputBuffer;
@@ -271,7 +289,10 @@
AMediaFormat_setString;
AMediaFormat_toString;
AMediaMuxer_addTrack;
+ AMediaMuxer_append; # introduced=31
AMediaMuxer_delete;
+ AMediaMuxer_getTrackCount; # introduced=31
+ AMediaMuxer_getTrackFormat; # introduced=31
AMediaMuxer_new;
AMediaMuxer_setLocation;
AMediaMuxer_setOrientationHint;
diff --git a/media/ndk/tests/AImageReaderWindowHandleTest.cpp b/media/ndk/tests/AImageReaderWindowHandleTest.cpp
index 5b65064..27864c2 100644
--- a/media/ndk/tests/AImageReaderWindowHandleTest.cpp
+++ b/media/ndk/tests/AImageReaderWindowHandleTest.cpp
@@ -17,10 +17,10 @@
#include <gtest/gtest.h>
#include <media/NdkImageReader.h>
#include <media/NdkImage.h>
-#include <private/media/NdkImage.h>
#include <mediautils/AImageReaderUtils.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
+#include <NdkImagePriv.h>
#include <NdkImageReaderPriv.h>
#include <vndk/hardware_buffer.h>
#include <memory>
diff --git a/media/ndk/tests/Android.bp b/media/ndk/tests/Android.bp
new file mode 100644
index 0000000..984b3ee
--- /dev/null
+++ b/media/ndk/tests/Android.bp
@@ -0,0 +1,41 @@
+// 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.
+
+// Headers module is in frameworks/av/Android.bp because modules are not allowed
+// to refer to headers in parent directories and the headers live in
+// frameworks/av/include.
+
+package {
+ default_applicable_licenses: ["frameworks_av_media_ndk_license"],
+}
+
+cc_test {
+ name: "libmediandk_test",
+ test_suites: ["device-tests"],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libmediandk",
+ "libutils",
+ ],
+
+ srcs: [
+ "NdkMediaFormat_test.cpp",
+ ],
+}
diff --git a/media/ndk/tests/NdkMediaFormat_test.cpp b/media/ndk/tests/NdkMediaFormat_test.cpp
new file mode 100644
index 0000000..668d0a4
--- /dev/null
+++ b/media/ndk/tests/NdkMediaFormat_test.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NdkMediaFormat_test"
+
+#include <gtest/gtest.h>
+#include <utils/RefBase.h>
+
+#include <media/NdkMediaFormat.h>
+
+namespace android {
+
+class NdkMediaFormatTest : public ::testing::Test {
+};
+
+
+TEST(NdkMediaFormat_tests, test_create) {
+
+ AMediaFormat *fmt1 = AMediaFormat_new();
+ AMediaFormat *fmt2 = AMediaFormat_new();
+
+ EXPECT_NE(fmt1, fmt2);
+ EXPECT_NE(fmt1, nullptr);
+ EXPECT_NE(fmt2, nullptr);
+
+ AMediaFormat_delete(fmt1);
+ AMediaFormat_delete(fmt2);
+}
+
+TEST(NdkMediaFormat_tests, test_int32) {
+ AMediaFormat *fmt1 = AMediaFormat_new();
+ int32_t i32;
+ int64_t i64;
+ AMediaFormat_setInt32(fmt1, "five", 5);
+
+ EXPECT_TRUE(AMediaFormat_getInt32(fmt1, "five", &i32));
+ EXPECT_FALSE(AMediaFormat_getInt64(fmt1, "five", &i64));
+ EXPECT_EQ(i32, 5);
+
+ AMediaFormat_delete(fmt1);
+}
+
+TEST(NdkMediaFormat_tests, test_int64) {
+ AMediaFormat *fmt1 = AMediaFormat_new();
+ int64_t i64;
+ AMediaFormat_setInt64(fmt1, "verylarge", INT64_MAX);
+
+ EXPECT_TRUE(AMediaFormat_getInt64(fmt1, "verylarge", &i64));
+ EXPECT_EQ(i64, INT64_MAX);
+
+ // return unchanged if not found
+ i64 = -1;
+ EXPECT_FALSE(AMediaFormat_getInt64(fmt1, "five", &i64));
+ EXPECT_EQ(i64, -1);
+
+ AMediaFormat_delete(fmt1);
+}
+
+TEST(NdkMediaFormat_tests, test_size) {
+ AMediaFormat *fmt1 = AMediaFormat_new();
+
+ size_t size = -15;
+ AMediaFormat_setSize(fmt1, "small", 1);
+ AMediaFormat_setSize(fmt1, "medium", 10);
+ AMediaFormat_setSize(fmt1, "large", 100);
+ EXPECT_TRUE(AMediaFormat_getSize(fmt1, "medium", &size));
+ EXPECT_EQ(size, 10);
+
+ AMediaFormat_delete(fmt1);
+}
+
+TEST(NdkMediaFormat_tests, test_float) {
+ AMediaFormat *fmt1 = AMediaFormat_new();
+ float f;
+ AMediaFormat_setFloat(fmt1, "boat", 1.5);
+ AMediaFormat_setFloat(fmt1, "ship", 0.5);
+ EXPECT_TRUE(AMediaFormat_getFloat(fmt1, "boat", &f));
+ EXPECT_EQ(f, 1.5);
+ AMediaFormat_delete(fmt1);
+}
+
+TEST(NdkMediaFormat_tests, test_double) {
+ AMediaFormat *fmt1 = AMediaFormat_new();
+ double d;
+ AMediaFormat_setDouble(fmt1, "trouble", 100.5);
+ AMediaFormat_setDouble(fmt1, "dip", 0.5);
+ EXPECT_TRUE(AMediaFormat_getDouble(fmt1, "trouble", &d));
+ EXPECT_EQ(d, 100.5);
+ AMediaFormat_delete(fmt1);
+}
+
+TEST(NdkMediaFormat_tests, test_string) {
+ AMediaFormat *fmt1 = AMediaFormat_new();
+
+ const char *content = "This is my test string";
+ const char *out = nullptr;
+ AMediaFormat_setString(fmt1, "stringtheory", content);
+ EXPECT_TRUE(AMediaFormat_getString(fmt1, "stringtheory", &out));
+ EXPECT_NE(out, nullptr);
+ EXPECT_EQ(strcmp(out,content), 0);
+
+ AMediaFormat_delete(fmt1);
+}
+
+
+TEST(NdkMediaFormat_tests, test_clear) {
+ AMediaFormat *fmt1 = AMediaFormat_new();
+
+ int32_t i32;
+ AMediaFormat_setInt32(fmt1, "five", 5);
+ size_t size = -15;
+ AMediaFormat_setSize(fmt1, "medium", 10);
+ float f;
+ AMediaFormat_setFloat(fmt1, "boat", 1.5);
+
+ AMediaFormat_clear(fmt1);
+ EXPECT_FALSE(AMediaFormat_getInt32(fmt1, "five", &i32));
+ EXPECT_FALSE(AMediaFormat_getSize(fmt1, "medium", &size));
+ EXPECT_FALSE(AMediaFormat_getFloat(fmt1, "boat", &f));
+
+ AMediaFormat_delete(fmt1);
+}
+
+TEST(NdkMediaFormat_tests, test_copy) {
+ AMediaFormat *fmt1 = AMediaFormat_new();
+ AMediaFormat *fmt2 = AMediaFormat_new();
+
+ double d;
+ int32_t i32;
+
+ // test copy functionality (NB: we cleared everything just above here)
+ AMediaFormat_setDouble(fmt1, "trouble", 100.5);
+ EXPECT_TRUE(AMediaFormat_getDouble(fmt1, "trouble", &d));
+ EXPECT_FALSE(AMediaFormat_getDouble(fmt2, "trouble", &d));
+
+ EXPECT_EQ(AMEDIA_OK, AMediaFormat_copy(fmt2, fmt1));
+
+ EXPECT_TRUE(AMediaFormat_getDouble(fmt2, "trouble", &d));
+ EXPECT_EQ(d, 100.5);
+
+ AMediaFormat *fmt3 = nullptr;
+ EXPECT_NE(AMEDIA_OK, AMediaFormat_copy(fmt3, fmt1));
+ EXPECT_NE(AMEDIA_OK, AMediaFormat_copy(fmt1, fmt3));
+
+ // we should lose an entry when we copy over it
+ AMediaFormat_setInt32(fmt2, "vanishing", 50);
+ EXPECT_FALSE(AMediaFormat_getInt32(fmt1, "vanishing", &i32));
+ EXPECT_TRUE(AMediaFormat_getInt32(fmt2, "vanishing", &i32));
+ EXPECT_EQ(AMEDIA_OK, AMediaFormat_copy(fmt2, fmt1));
+ EXPECT_FALSE(AMediaFormat_getInt32(fmt2, "vanishing", &i32));
+
+ AMediaFormat_delete(fmt1);
+ AMediaFormat_delete(fmt2);
+}
+
+TEST(NdkMediaFormat_tests, test_buffer) {
+ AMediaFormat *fmt1 = AMediaFormat_new();
+
+ typedef struct blockomem {
+ int leading;
+ int filled[100];
+ int trailing;
+ } block_t;
+ block_t buf = {};
+ buf.leading = 1;
+ buf.trailing = 2;
+ void *data;
+ size_t bsize;
+
+ AMediaFormat_setBuffer(fmt1, "mybuffer", &buf, sizeof(buf));
+ EXPECT_TRUE(AMediaFormat_getBuffer(fmt1, "mybuffer", &data, &bsize));
+ EXPECT_NE(&buf, data);
+ EXPECT_EQ(sizeof(buf), bsize);
+ block_t *bufp = (block_t*) data;
+ EXPECT_EQ(bufp->leading, buf.leading);
+ EXPECT_EQ(bufp->trailing, buf.trailing);
+ EXPECT_EQ(0, memcmp(&buf, data, bsize));
+
+ AMediaFormat_delete(fmt1);
+}
+
+} // namespace android
diff --git a/media/tests/benchmark/src/native/decoder/C2Decoder.cpp b/media/tests/benchmark/src/native/decoder/C2Decoder.cpp
index 46c4a7c..6539f24 100644
--- a/media/tests/benchmark/src/native/decoder/C2Decoder.cpp
+++ b/media/tests/benchmark/src/native/decoder/C2Decoder.cpp
@@ -53,8 +53,8 @@
}
int64_t sTime = mStats->getCurTime();
- mComponent = mClient->CreateComponentByName(compName.c_str(), mListener, &mClient);
- if (mComponent == nullptr) {
+ if (mClient->CreateComponentByName(compName.c_str(), mListener, &mComponent, &mClient) !=
+ C2_OK) {
ALOGE("Create component failed for %s", compName.c_str());
return -1;
}
diff --git a/media/tests/benchmark/src/native/encoder/C2Encoder.cpp b/media/tests/benchmark/src/native/encoder/C2Encoder.cpp
index 6a50d40..ca79881 100644
--- a/media/tests/benchmark/src/native/encoder/C2Encoder.cpp
+++ b/media/tests/benchmark/src/native/encoder/C2Encoder.cpp
@@ -68,8 +68,8 @@
}
int64_t sTime = mStats->getCurTime();
- mComponent = mClient->CreateComponentByName(compName.c_str(), mListener, &mClient);
- if (mComponent == nullptr) {
+ if (mClient->CreateComponentByName(compName.c_str(), mListener, &mComponent, &mClient) !=
+ C2_OK) {
ALOGE("Create component failed for %s", compName.c_str());
return -1;
}
diff --git a/media/utils/Android.bp b/media/utils/Android.bp
index 59c2e65..9e48c1f 100644
--- a/media/utils/Android.bp
+++ b/media/utils/Android.bp
@@ -41,6 +41,7 @@
"libprocessinfoservice_aidl",
],
shared_libs: [
+ "libaudioclient_aidl_conversion",
"libaudioutils", // for clock.h
"libbinder",
"libcutils",
@@ -50,6 +51,7 @@
"libhidlbase",
"android.hardware.graphics.bufferqueue@1.0",
"android.hidl.token@1.0-utils",
+ "media_permission-aidl-cpp",
],
export_static_lib_headers: [
"libbatterystats_aidl",
@@ -68,6 +70,10 @@
"libmedia_headers",
],
+ export_shared_lib_headers: [
+ "media_permission-aidl-cpp"
+ ],
+
include_dirs: [
// For DEBUGGER_SIGNAL
"system/core/debuggerd/include",
@@ -75,3 +81,10 @@
local_include_dirs: ["include"],
export_include_dirs: ["include"],
}
+
+cc_library_headers {
+ name: "libmediautils_headers",
+ vendor_available: true, // required for platform/hardware/interfaces
+
+ export_include_dirs: ["include"],
+}
diff --git a/media/utils/ProcessInfo.cpp b/media/utils/ProcessInfo.cpp
index e9c9f8d..e212794 100644
--- a/media/utils/ProcessInfo.cpp
+++ b/media/utils/ProcessInfo.cpp
@@ -27,6 +27,9 @@
namespace android {
+static constexpr int32_t INVALID_ADJ = -10000;
+static constexpr int32_t NATIVE_ADJ = -1000;
+
ProcessInfo::ProcessInfo() {}
bool ProcessInfo::getPriority(int pid, int* priority) {
@@ -35,8 +38,6 @@
size_t length = 1;
int32_t state;
- static const int32_t INVALID_ADJ = -10000;
- static const int32_t NATIVE_ADJ = -1000;
int32_t score = INVALID_ADJ;
status_t err = service->getProcessStatesAndOomScoresFromPids(length, &pid, &state, &score);
if (err != OK) {
@@ -45,8 +46,17 @@
}
ALOGV("pid %d state %d score %d", pid, state, score);
if (score <= NATIVE_ADJ) {
- ALOGE("pid %d invalid OOM adjustments value %d", pid, score);
- return false;
+ std::scoped_lock lock{mOverrideLock};
+
+ // If this process if not tracked by ActivityManagerService, look for overrides.
+ auto it = mOverrideMap.find(pid);
+ if (it != mOverrideMap.end()) {
+ ALOGI("pid %d invalid OOM score %d, override to %d", pid, score, it->second.oomScore);
+ score = it->second.oomScore;
+ } else {
+ ALOGE("pid %d invalid OOM score %d", pid, score);
+ return false;
+ }
}
// Use OOM adjustments value as the priority. Lower the value, higher the priority.
@@ -61,6 +71,26 @@
return (callingPid == getpid()) || (callingPid == pid) || (callingUid == AID_MEDIA);
}
+bool ProcessInfo::overrideProcessInfo(int pid, int procState, int oomScore) {
+ std::scoped_lock lock{mOverrideLock};
+
+ mOverrideMap.erase(pid);
+
+ // Disable the override if oomScore is set to NATIVE_ADJ or below.
+ if (oomScore <= NATIVE_ADJ) {
+ return false;
+ }
+
+ mOverrideMap.emplace(pid, ProcessInfoOverride{procState, oomScore});
+ return true;
+}
+
+void ProcessInfo::removeProcessInfoOverride(int pid) {
+ std::scoped_lock lock{mOverrideLock};
+
+ mOverrideMap.erase(pid);
+}
+
ProcessInfo::~ProcessInfo() {}
} // namespace android
diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp
index 1b1717f..e3931b1 100644
--- a/media/utils/ServiceUtilities.cpp
+++ b/media/utils/ServiceUtilities.cpp
@@ -22,6 +22,10 @@
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
#include "mediautils/ServiceUtilities.h"
+#include <system/audio-hal-enums.h>
+#include <media/AidlConversion.h>
+#include <media/AidlConversionUtil.h>
+#include <android/media/permission/Identity.h>
#include <iterator>
#include <algorithm>
@@ -36,14 +40,16 @@
namespace android {
+using media::permission::Identity;
+
static const String16 sAndroidPermissionRecordAudio("android.permission.RECORD_AUDIO");
static const String16 sModifyPhoneState("android.permission.MODIFY_PHONE_STATE");
static const String16 sModifyAudioRouting("android.permission.MODIFY_AUDIO_ROUTING");
static String16 resolveCallingPackage(PermissionController& permissionController,
- const String16& opPackageName, uid_t uid) {
- if (opPackageName.size() > 0) {
- return opPackageName;
+ const std::optional<String16> opPackageName, uid_t uid) {
+ if (opPackageName.has_value() && opPackageName.value().size() > 0) {
+ return opPackageName.value();
}
// In some cases the calling code has no access to the package it runs under.
// For example, code using the wilhelm framework's OpenSL-ES APIs. In this
@@ -56,52 +62,70 @@
permissionController.getPackagesForUid(uid, packages);
if (packages.isEmpty()) {
ALOGE("No packages for uid %d", uid);
- return opPackageName; // empty string
+ return String16();
}
return packages[0];
}
-static bool checkRecordingInternal(const String16& opPackageName, pid_t pid,
- uid_t uid, bool start, bool isHotwordSource) {
+static int32_t getOpForSource(audio_source_t source) {
+ switch (source) {
+ case AUDIO_SOURCE_HOTWORD:
+ return AppOpsManager::OP_RECORD_AUDIO_HOTWORD;
+ case AUDIO_SOURCE_REMOTE_SUBMIX:
+ return AppOpsManager::OP_RECORD_AUDIO_OUTPUT;
+ case AUDIO_SOURCE_VOICE_DOWNLINK:
+ return AppOpsManager::OP_RECORD_INCOMING_PHONE_AUDIO;
+ case AUDIO_SOURCE_DEFAULT:
+ default:
+ return AppOpsManager::OP_RECORD_AUDIO;
+ }
+}
+
+static bool checkRecordingInternal(const Identity& identity, const String16& msg,
+ bool start, audio_source_t source) {
// 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
+ uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
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.
// IMPORTANT: DON'T USE PermissionCache - RUNTIME PERMISSIONS CHANGE.
PermissionController permissionController;
- const bool ok = permissionController.checkPermission(sAndroidPermissionRecordAudio, pid, uid);
+ const bool ok = permissionController.checkPermission(sAndroidPermissionRecordAudio,
+ identity.pid, identity.uid);
if (!ok) {
ALOGE("Request requires %s", String8(sAndroidPermissionRecordAudio).c_str());
return false;
}
String16 resolvedOpPackageName = resolveCallingPackage(
- permissionController, opPackageName, uid);
+ permissionController, VALUE_OR_FATAL(aidl2legacy_string_view_String16(
+ identity.packageName.value_or(""))), uid);
if (resolvedOpPackageName.size() == 0) {
return false;
}
- AppOpsManager appOps;
- const int32_t opRecordAudio = appOps.permissionToOpCode(sAndroidPermissionRecordAudio);
+ AppOpsManager appOps;
+ const int32_t op = getOpForSource(source);
if (start) {
- const int32_t op = isHotwordSource ?
- AppOpsManager::OP_RECORD_AUDIO_HOTWORD : opRecordAudio;
- if (appOps.startOpNoThrow(op, uid, resolvedOpPackageName, /*startIfModeDefault*/ false)
- != AppOpsManager::MODE_ALLOWED) {
- ALOGE("Request denied by app op: %d", op);
+ if (int32_t mode = appOps.startOpNoThrow(op, identity.uid,
+ resolvedOpPackageName, /*startIfModeDefault*/ false,
+ VALUE_OR_FATAL(aidl2legacy_optional_string_view_optional_String16(
+ identity.attributionTag)), msg) == AppOpsManager::MODE_ERRORED) {
+ ALOGE("Request start for \"%s\" (uid %d) denied by app op: %d, mode: %d",
+ String8(resolvedOpPackageName).c_str(), identity.uid, op, mode);
return false;
}
} else {
- // Always use OP_RECORD_AUDIO for checks at creation time.
- if (appOps.checkOp(opRecordAudio, uid, resolvedOpPackageName)
- != AppOpsManager::MODE_ALLOWED) {
- ALOGE("Request denied by app op: %d", opRecordAudio);
+ if (int32_t mode = appOps.checkOp(op, uid,
+ resolvedOpPackageName) == AppOpsManager::MODE_ERRORED) {
+ ALOGE("Request check for \"%s\" (uid %d) denied by app op: %d, mode: %d",
+ String8(resolvedOpPackageName).c_str(), identity.uid, op, mode);
return false;
}
}
@@ -109,34 +133,40 @@
return true;
}
-bool recordingAllowed(const String16& opPackageName, pid_t pid, uid_t uid) {
- return checkRecordingInternal(opPackageName, pid, uid, /*start*/ false,
- /*is_hotword_source*/ false);
+bool recordingAllowed(const Identity& identity) {
+ return checkRecordingInternal(identity, String16(), /*start*/ false, AUDIO_SOURCE_DEFAULT);
}
-bool startRecording(const String16& opPackageName, pid_t pid, uid_t uid, bool isHotwordSource) {
- return checkRecordingInternal(opPackageName, pid, uid, /*start*/ true, isHotwordSource);
+bool startRecording(const Identity& identity, const String16& msg, audio_source_t source) {
+ return checkRecordingInternal(identity, msg, /*start*/ true, source);
}
-void finishRecording(const String16& opPackageName, uid_t uid, bool isHotwordSource) {
+void finishRecording(const Identity& identity, audio_source_t source) {
// Okay to not track in app ops as audio server is us and if
// device is rooted security model is considered compromised.
+ uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
if (isAudioServerOrRootUid(uid)) return;
PermissionController permissionController;
String16 resolvedOpPackageName = resolveCallingPackage(
- permissionController, opPackageName, uid);
+ permissionController,
+ VALUE_OR_FATAL(aidl2legacy_string_view_String16(identity.packageName.value_or(""))),
+ VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid)));
if (resolvedOpPackageName.size() == 0) {
return;
}
AppOpsManager appOps;
- const int32_t op = isHotwordSource ? AppOpsManager::OP_RECORD_AUDIO_HOTWORD
- : appOps.permissionToOpCode(sAndroidPermissionRecordAudio);
- appOps.finishOp(op, uid, resolvedOpPackageName);
+
+ const int32_t op = getOpForSource(source);
+ appOps.finishOp(op, identity.uid, resolvedOpPackageName,
+ VALUE_OR_FATAL(aidl2legacy_optional_string_view_optional_String16(
+ identity.attributionTag)));
}
-bool captureAudioOutputAllowed(pid_t pid, uid_t uid) {
+bool captureAudioOutputAllowed(const Identity& identity) {
+ uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
+ pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(identity.pid));
if (isAudioServerOrRootUid(uid)) return true;
static const String16 sCaptureAudioOutput("android.permission.CAPTURE_AUDIO_OUTPUT");
bool ok = PermissionCache::checkPermission(sCaptureAudioOutput, pid, uid);
@@ -144,7 +174,9 @@
return ok;
}
-bool captureMediaOutputAllowed(pid_t pid, uid_t uid) {
+bool captureMediaOutputAllowed(const Identity& identity) {
+ uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
+ pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(identity.pid));
if (isAudioServerOrRootUid(uid)) return true;
static const String16 sCaptureMediaOutput("android.permission.CAPTURE_MEDIA_OUTPUT");
bool ok = PermissionCache::checkPermission(sCaptureMediaOutput, pid, uid);
@@ -152,7 +184,19 @@
return ok;
}
-bool captureVoiceCommunicationOutputAllowed(pid_t pid, uid_t uid) {
+bool captureTunerAudioInputAllowed(const Identity& identity) {
+ uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
+ pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(identity.pid));
+ if (isAudioServerOrRootUid(uid)) return true;
+ static const String16 sCaptureTunerAudioInput("android.permission.CAPTURE_TUNER_AUDIO_INPUT");
+ bool ok = PermissionCache::checkPermission(sCaptureTunerAudioInput, pid, uid);
+ if (!ok) ALOGV("Request requires android.permission.CAPTURE_TUNER_AUDIO_INPUT");
+ return ok;
+}
+
+bool captureVoiceCommunicationOutputAllowed(const Identity& identity) {
+ uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
+ uid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(identity.pid));
if (isAudioServerOrRootUid(uid)) return true;
static const String16 sCaptureVoiceCommOutput(
"android.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT");
@@ -161,13 +205,14 @@
return ok;
}
-bool captureHotwordAllowed(const String16& opPackageName, pid_t pid, uid_t uid) {
+bool captureHotwordAllowed(const Identity& identity) {
+ uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
+ uid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(identity.pid));
// CAPTURE_AUDIO_HOTWORD permission implies RECORD_AUDIO permission
- bool ok = recordingAllowed(opPackageName, pid, uid);
+ bool ok = recordingAllowed(identity);
if (ok) {
static const String16 sCaptureHotwordAllowed("android.permission.CAPTURE_AUDIO_HOTWORD");
- // IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
ok = PermissionCache::checkPermission(sCaptureHotwordAllowed, pid, uid);
}
if (!ok) ALOGV("android.permission.CAPTURE_AUDIO_HOTWORD");
@@ -185,11 +230,12 @@
}
bool modifyAudioRoutingAllowed() {
- return modifyAudioRoutingAllowed(
- IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid());
+ return modifyAudioRoutingAllowed(getCallingIdentity());
}
-bool modifyAudioRoutingAllowed(pid_t pid, uid_t uid) {
+bool modifyAudioRoutingAllowed(const Identity& identity) {
+ uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
+ pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(identity.pid));
if (isAudioServerUid(IPCThreadState::self()->getCallingUid())) return true;
// IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
bool ok = PermissionCache::checkPermission(sModifyAudioRouting, pid, uid);
@@ -199,11 +245,12 @@
}
bool modifyDefaultAudioEffectsAllowed() {
- return modifyDefaultAudioEffectsAllowed(
- IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid());
+ return modifyDefaultAudioEffectsAllowed(getCallingIdentity());
}
-bool modifyDefaultAudioEffectsAllowed(pid_t pid, uid_t uid) {
+bool modifyDefaultAudioEffectsAllowed(const Identity& identity) {
+ uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
+ pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(identity.pid));
if (isAudioServerUid(IPCThreadState::self()->getCallingUid())) return true;
static const String16 sModifyDefaultAudioEffectsAllowed(
@@ -224,14 +271,18 @@
return ok;
}
-bool modifyPhoneStateAllowed(pid_t pid, uid_t uid) {
+bool modifyPhoneStateAllowed(const Identity& identity) {
+ uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
+ pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(identity.pid));
bool ok = PermissionCache::checkPermission(sModifyPhoneState, pid, uid);
ALOGE_IF(!ok, "Request requires %s", String8(sModifyPhoneState).c_str());
return ok;
}
// privileged behavior needed by Dialer, Settings, SetupWizard and CellBroadcastReceiver
-bool bypassInterruptionPolicyAllowed(pid_t pid, uid_t uid) {
+bool bypassInterruptionPolicyAllowed(const Identity& identity) {
+ uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
+ pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(identity.pid));
static const String16 sWriteSecureSettings("android.permission.WRITE_SECURE_SETTINGS");
bool ok = PermissionCache::checkPermission(sModifyPhoneState, pid, uid)
|| PermissionCache::checkPermission(sWriteSecureSettings, pid, uid)
@@ -241,6 +292,17 @@
return ok;
}
+Identity getCallingIdentity() {
+ Identity identity = Identity();
+ identity.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(IPCThreadState::self()->getCallingPid()));
+ identity.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(IPCThreadState::self()->getCallingUid()));
+ return identity;
+}
+
+void purgePermissionCache() {
+ PermissionCache::purgeCache();
+}
+
status_t checkIMemory(const sp<IMemory>& iMemory)
{
if (iMemory == 0) {
diff --git a/media/utils/fuzzers/Android.bp b/media/utils/fuzzers/Android.bp
index 5c03926..b245834 100644
--- a/media/utils/fuzzers/Android.bp
+++ b/media/utils/fuzzers/Android.bp
@@ -16,6 +16,7 @@
"liblog",
"libmediautils",
"libutils",
+ "media_permission-aidl-cpp",
],
cflags: [
diff --git a/media/utils/fuzzers/ServiceUtilitiesFuzz.cpp b/media/utils/fuzzers/ServiceUtilitiesFuzz.cpp
index 20b3713..2f9e780 100644
--- a/media/utils/fuzzers/ServiceUtilitiesFuzz.cpp
+++ b/media/utils/fuzzers/ServiceUtilitiesFuzz.cpp
@@ -17,13 +17,17 @@
#include <fcntl.h>
#include <functional>
+#include <type_traits>
+#include <android/media/permission/Identity.h>
#include "fuzzer/FuzzedDataProvider.h"
#include "mediautils/ServiceUtilities.h"
static constexpr int kMaxOperations = 50;
static constexpr int kMaxStringLen = 256;
+using android::media::permission::Identity;
+
const std::vector<std::function<void(FuzzedDataProvider*, android::MediaPackageManager)>>
operations = {
[](FuzzedDataProvider* data_provider, android::MediaPackageManager pm) -> void {
@@ -42,8 +46,18 @@
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FuzzedDataProvider data_provider(data, size);
- uid_t uid = data_provider.ConsumeIntegral<uid_t>();
- pid_t pid = data_provider.ConsumeIntegral<pid_t>();
+ int32_t uid = data_provider.ConsumeIntegral<int32_t>();
+ int32_t pid = data_provider.ConsumeIntegral<int32_t>();
+ audio_source_t source = static_cast<audio_source_t>(data_provider
+ .ConsumeIntegral<std::underlying_type_t<audio_source_t>>());
+
+ std::string packageNameStr = data_provider.ConsumeRandomLengthString(kMaxStringLen);
+ std::string msgStr = data_provider.ConsumeRandomLengthString(kMaxStringLen);
+ android::String16 msgStr16(packageNameStr.c_str());
+ Identity identity;
+ identity.packageName = packageNameStr;
+ identity.uid = uid;
+ identity.pid = pid;
// There is not state here, and order is not significant,
// so we can simply call all of the target functions
@@ -51,16 +65,14 @@
android::isAudioServerUid(uid);
android::isAudioServerOrSystemServerUid(uid);
android::isAudioServerOrMediaServerUid(uid);
- std::string packageNameStr = data_provider.ConsumeRandomLengthString(kMaxStringLen);
- android::String16 opPackageName(packageNameStr.c_str());
- android::recordingAllowed(opPackageName, pid, uid);
- android::startRecording(opPackageName, pid, uid, false);
- android::finishRecording(opPackageName, uid, false);
- android::captureAudioOutputAllowed(pid, uid);
- android::captureMediaOutputAllowed(pid, uid);
- android::captureHotwordAllowed(opPackageName, pid, uid);
- android::modifyPhoneStateAllowed(uid, pid);
- android::bypassInterruptionPolicyAllowed(uid, pid);
+ android::recordingAllowed(identity);
+ android::startRecording(identity, msgStr16, source);
+ android::finishRecording(identity, source);
+ android::captureAudioOutputAllowed(identity);
+ android::captureMediaOutputAllowed(identity);
+ android::captureHotwordAllowed(identity);
+ android::modifyPhoneStateAllowed(identity);
+ android::bypassInterruptionPolicyAllowed(identity);
android::settingsAllowed();
android::modifyAudioRoutingAllowed();
android::modifyDefaultAudioEffectsAllowed();
diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h
index c7d49fd..6e75746 100644
--- a/media/utils/include/mediautils/ServiceUtilities.h
+++ b/media/utils/include/mediautils/ServiceUtilities.h
@@ -24,6 +24,8 @@
#include <binder/PermissionController.h>
#include <cutils/multiuser.h>
#include <private/android_filesystem_config.h>
+#include <system/audio-hal-enums.h>
+#include <android/media/permission/Identity.h>
#include <map>
#include <optional>
@@ -78,21 +80,26 @@
}
}
-bool recordingAllowed(const String16& opPackageName, pid_t pid, uid_t uid);
-bool startRecording(const String16& opPackageName, pid_t pid, uid_t uid, bool isHotwordSource);
-void finishRecording(const String16& opPackageName, uid_t uid, bool isHotwordSource);
-bool captureAudioOutputAllowed(pid_t pid, uid_t uid);
-bool captureMediaOutputAllowed(pid_t pid, uid_t uid);
-bool captureVoiceCommunicationOutputAllowed(pid_t pid, uid_t uid);
-bool captureHotwordAllowed(const String16& opPackageName, pid_t pid, uid_t uid);
+bool recordingAllowed(const media::permission::Identity& identity);
+bool startRecording(const media::permission::Identity& identity,
+ const String16& msg, audio_source_t source);
+void finishRecording(const media::permission::Identity& identity, audio_source_t source);
+bool captureAudioOutputAllowed(const media::permission::Identity& identity);
+bool captureMediaOutputAllowed(const media::permission::Identity& identity);
+bool captureTunerAudioInputAllowed(const media::permission::Identity& identity);
+bool captureVoiceCommunicationOutputAllowed(const media::permission::Identity& identity);
+bool captureHotwordAllowed(const media::permission::Identity& identity);
bool settingsAllowed();
bool modifyAudioRoutingAllowed();
-bool modifyAudioRoutingAllowed(pid_t pid, uid_t uid);
+bool modifyAudioRoutingAllowed(const media::permission::Identity& identity);
bool modifyDefaultAudioEffectsAllowed();
-bool modifyDefaultAudioEffectsAllowed(pid_t pid, uid_t uid);
+bool modifyDefaultAudioEffectsAllowed(const media::permission::Identity& identity);
bool dumpAllowed();
-bool modifyPhoneStateAllowed(pid_t pid, uid_t uid);
-bool bypassInterruptionPolicyAllowed(pid_t pid, uid_t uid);
+bool modifyPhoneStateAllowed(const media::permission::Identity& identity);
+bool bypassInterruptionPolicyAllowed(const media::permission::Identity& identity);
+void purgePermissionCache();
+
+media::permission::Identity getCallingIdentity();
status_t checkIMemory(const sp<IMemory>& iMemory);
diff --git a/media/utils/include/mediautils/Synchronization.h b/media/utils/include/mediautils/Synchronization.h
new file mode 100644
index 0000000..aef4967
--- /dev/null
+++ b/media/utils/include/mediautils/Synchronization.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <mutex>
+#include <utils/RefBase.h>
+
+namespace android::mediautils {
+
+/**
+ * The LockItem class introduces a simple template which mimics atomic<T>
+ * for non-trivially copyable types. For trivially copyable types,
+ * the LockItem will statically assert that an atomic<T> should be used instead.
+ *
+ * The default lock mutex is std::mutex which is suitable for all but rare cases
+ * e.g. recursive constructors that might be found in tree construction,
+ * setters that might recurse onto the same object.
+ */
+
+template <typename T, typename L = std::mutex, int FLAGS = 0>
+class LockItem {
+protected:
+ mutable L mLock;
+ mutable T mT;
+
+public:
+ enum {
+ // Best practices for smart pointers and complex containers is to move to a temp
+ // and invoke destructor outside of lock. This reduces time under lock and in
+ // some cases eliminates deadlock.
+ FLAG_DTOR_OUT_OF_LOCK = 1,
+ };
+
+ // Check type, suggest std::atomic if possible.
+ static_assert(!std::is_trivially_copyable_v<T>,
+ "type is trivially copyable, please use std::atomic instead");
+
+ // Allow implicit conversions as expected for some types, e.g. sp -> wp.
+ template <typename... Args>
+ LockItem(Args&&... args) : mT(std::forward<Args>(args)...) {
+ }
+
+ // NOT copy or move / assignable or constructible.
+
+ // Do not enable this because it may lead to confusion because it returns
+ // a copy-value not a reference.
+ // operator T() const { return load(); }
+
+ // any conversion done under lock.
+ template <typename U>
+ void operator=(U&& u) {
+ store(std::forward<U>(u));
+ }
+
+ // returns a copy-value not a reference.
+ T load() const {
+ std::lock_guard lock(mLock);
+ return mT;
+ }
+
+ // any conversion done under lock.
+ template <typename U>
+ void store(U&& u) {
+ if constexpr ((FLAGS & FLAG_DTOR_OUT_OF_LOCK) != 0) {
+ std::unique_lock lock(mLock);
+ T temp = std::move(mT);
+ mT = std::forward<U>(u);
+ lock.unlock();
+ } else {
+ std::lock_guard lock(mLock);
+ mT = std::forward<U>(u);
+ }
+ }
+};
+
+/**
+ * atomic_wp<> and atomic_sp<> are used for concurrent access to Android
+ * sp<> and wp<> smart pointers, including their modifiers. We
+ * return a copy of the smart pointer with load().
+ *
+ * Historical: The importance of an atomic<std::shared_ptr<T>> class is described
+ * by Herb Sutter in the following ISO document https://isocpp.org/files/papers/N4162.pdf
+ * and is part of C++20. Lock free versions of atomic smart pointers are available
+ * publicly but usually require specialized smart pointer structs.
+ * See also https://en.cppreference.com/w/cpp/memory/shared_ptr/atomic
+ * and https://en.cppreference.com/w/cpp/memory/shared_ptr/atomic2
+ *
+ * We offer lock based atomic_wp<> and atomic_sp<> objects here. This is useful to
+ * copy the Android smart pointer to a different variable for subsequent local access,
+ * where the change of the original object after copy is acceptable.
+ *
+ * Note: Instead of atomics, it is often preferrable to create an explicit visible lock to
+ * ensure complete transaction consistency. For example, one might want to ensure
+ * that the method called from the smart pointer is also done under lock.
+ * This may not be possible for callbacks due to inverted lock ordering.
+ */
+
+template <typename T>
+using atomic_wp = LockItem<::android::wp<T>>;
+
+template <typename T>
+using atomic_sp = LockItem<
+ ::android::sp<T>, std::mutex, LockItem<::android::sp<T>>::FLAG_DTOR_OUT_OF_LOCK>;
+
+/**
+ * Defers a function to run in the RAII destructor.
+ * A C++ implementation of Go _defer_ https://golangr.com/defer/.
+ */
+class Defer {
+public:
+ template <typename U>
+ explicit Defer(U &&f) : mThunk(std::forward<U>(f)) {}
+ ~Defer() { mThunk(); }
+
+private:
+ const std::function<void()> mThunk;
+};
+
+} // namespace android::mediautils
+
diff --git a/media/utils/tests/Android.bp b/media/utils/tests/Android.bp
new file mode 100644
index 0000000..6593d56
--- /dev/null
+++ b/media/utils/tests/Android.bp
@@ -0,0 +1,28 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_test {
+ name: "media_synchronization_tests",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libmediautils",
+ "libutils",
+ ],
+
+ srcs: [
+ "media_synchronization_tests.cpp",
+ ],
+}
diff --git a/media/utils/tests/media_synchronization_tests.cpp b/media/utils/tests/media_synchronization_tests.cpp
new file mode 100644
index 0000000..169768e
--- /dev/null
+++ b/media/utils/tests/media_synchronization_tests.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "media_synchronization_tests"
+
+#include <mediautils/Synchronization.h>
+
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+using namespace android;
+using namespace android::mediautils;
+
+// Simple Test Class
+template <typename T>
+class MyObject : public RefBase {
+ T value_;
+ public:
+ MyObject(const T& value) : value_(value) {}
+ MyObject(const MyObject<T>& mo) : value_(mo.get()) {}
+ T get() const { return value_; }
+ void set(const T& value) { value_ = value; }
+};
+
+TEST(media_synchronization_tests, atomic_wp) {
+ sp<MyObject<int>> refobj = new MyObject<int>(20);
+ atomic_wp<MyObject<int>> wpobj = refobj;
+
+ // we can promote.
+ ASSERT_EQ(20, wpobj.load().promote()->get());
+
+ // same underlying object for sp and atomic_wp.
+ ASSERT_EQ(refobj.get(), wpobj.load().promote().get());
+
+ // behavior is consistent with same underlying object.
+ wpobj.load().promote()->set(10);
+ ASSERT_EQ(10, refobj->get());
+ refobj->set(5);
+ ASSERT_EQ(5, wpobj.load().promote()->get());
+
+ // we can clear our weak ptr.
+ wpobj = nullptr;
+ ASSERT_EQ(nullptr, wpobj.load().promote());
+
+ // didn't affect our original obj.
+ ASSERT_NE(nullptr, refobj.get());
+}
+
+TEST(media_synchronization_tests, atomic_sp) {
+ sp<MyObject<int>> refobj = new MyObject<int>(20);
+ atomic_sp<MyObject<int>> spobj = refobj;
+
+ // same underlying object for sp and atomic_sp.
+ ASSERT_EQ(refobj.get(), spobj.load().get());
+
+ // behavior is consistent with same underlying object.
+ ASSERT_EQ(20, spobj.load()->get());
+ spobj.load()->set(10);
+ ASSERT_EQ(10, refobj->get());
+ refobj->set(5);
+ ASSERT_EQ(5, spobj.load()->get());
+
+ // we can clear spobj.
+ spobj = nullptr;
+ ASSERT_EQ(nullptr, spobj.load().get());
+
+ // didn't affect our original obj.
+ ASSERT_NE(nullptr, refobj.get());
+}
diff --git a/services/audioflinger/Android.bp b/services/audioflinger/Android.bp
index ff3bfd2..a7d47fb 100644
--- a/services/audioflinger/Android.bp
+++ b/services/audioflinger/Android.bp
@@ -54,6 +54,11 @@
],
shared_libs: [
+ "audioflinger-aidl-cpp",
+ "audioclient-types-aidl-cpp",
+ "av-types-aidl-cpp",
+ "effect-aidl-cpp",
+ "libaudioclient_aidl_conversion",
"libaudiofoundation",
"libaudiohal",
"libaudioprocessing",
@@ -74,7 +79,9 @@
"libmediautils",
"libmemunreachable",
"libmedia_helper",
+ "libshmemcompat",
"libvibrator",
+ "media_permission-aidl-cpp",
],
static_libs: [
@@ -83,12 +90,14 @@
],
header_libs: [
+ "libaudioclient_headers",
"libaudiohal_headers",
"libmedia_headers",
],
export_shared_lib_headers: [
"libpermission",
+ "media_permission-aidl-cpp",
],
cflags: [
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 4ac46b7..3562b00 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -31,6 +31,7 @@
#include <sys/resource.h>
#include <thread>
+#include <android/media/IAudioPolicyService.h>
#include <android/os/IExternalVibratorService.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
@@ -43,6 +44,7 @@
#include <media/AudioParameter.h>
#include <media/MediaMetricsItem.h>
#include <media/TypeConverter.h>
+#include <mediautils/TimeCheck.h>
#include <memunreachable/memunreachable.h>
#include <utils/String16.h>
#include <utils/threads.h>
@@ -61,12 +63,15 @@
#include <system/audio_effects/effect_visualizer.h>
#include <system/audio_effects/effect_ns.h>
#include <system/audio_effects/effect_aec.h>
+#include <system/audio_effects/effect_hapticgenerator.h>
#include <audio_utils/primitives.h>
#include <powermanager/PowerManager.h>
#include <media/IMediaLogService.h>
+#include <media/AidlConversion.h>
+#include <media/AudioValidator.h>
#include <media/nbaio/Pipe.h>
#include <media/nbaio/PipeReader.h>
#include <mediautils/BatteryNotifier.h>
@@ -97,6 +102,9 @@
namespace android {
+using media::IEffectClient;
+using media::permission::Identity;
+
static const char kDeadlockedString[] = "AudioFlinger may be deadlocked\n";
static const char kHardwareLockedString[] = "Hardware lock is taken\n";
static const char kClientLockedString[] = "Client lock is taken\n";
@@ -111,9 +119,6 @@
// we define a minimum time during which a global effect is considered enabled.
static const nsecs_t kMinGlobalEffectEnabletimeNs = seconds(7200);
-Mutex gLock;
-wp<AudioFlinger> gAudioFlinger;
-
// Keep a strong reference to media.log service around forever.
// The service is within our parent process so it can never die in a way that we could observe.
// These two variables are const after initialization.
@@ -158,6 +163,33 @@
}
};
+// TODO b/182392769: use identity util
+/* static */
+media::permission::Identity AudioFlinger::checkIdentityPackage(
+ const media::permission::Identity& identity) {
+ Vector<String16> packages;
+ PermissionController{}.getPackagesForUid(identity.uid, packages);
+
+ Identity checkedIdentity = identity;
+ if (!identity.packageName.has_value() || identity.packageName.value().size() == 0) {
+ if (!packages.isEmpty()) {
+ checkedIdentity.packageName =
+ std::move(legacy2aidl_String16_string(packages[0]).value());
+ }
+ } else {
+ String16 opPackageLegacy = VALUE_OR_FATAL(
+ aidl2legacy_string_view_String16(identity.packageName.value_or("")));
+ if (std::find_if(packages.begin(), packages.end(),
+ [&opPackageLegacy](const auto& package) {
+ return opPackageLegacy == package; }) == packages.end()) {
+ ALOGW("The package name(%s) provided does not correspond to the uid %d",
+ identity.packageName.value_or("").c_str(), identity.uid);
+ checkedIdentity.packageName = std::optional<std::string>();
+ }
+ }
+ return checkedIdentity;
+}
+
// ----------------------------------------------------------------------------
std::string formatToString(audio_format_t format) {
@@ -168,9 +200,15 @@
// ----------------------------------------------------------------------------
+void AudioFlinger::instantiate() {
+ sp<IServiceManager> sm(defaultServiceManager());
+ sm->addService(String16(IAudioFlinger::DEFAULT_SERVICE_NAME),
+ new AudioFlingerServerAdapter(new AudioFlinger()), false,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
+}
+
AudioFlinger::AudioFlinger()
- : BnAudioFlinger(),
- mMediaLogNotifier(new AudioFlinger::MediaLogNotifier()),
+ : mMediaLogNotifier(new AudioFlinger::MediaLogNotifier()),
mPrimaryHardwareDev(NULL),
mAudioHwDevs(NULL),
mHardwareStatus(AUDIO_HW_IDLE),
@@ -188,13 +226,29 @@
mDeviceEffectManager(this),
mSystemReady(false)
{
+ // Move the audio session unique ID generator start base as time passes to limit risk of
+ // generating the same ID again after an audioserver restart.
+ // This is important because clients will reuse previously allocated audio session IDs
+ // when reconnecting after an audioserver restart and newly allocated IDs may conflict with
+ // active clients.
+ // Moving the base by 1 for each elapsed second is a good compromise between avoiding overlap
+ // between allocation ranges and not reaching wrap around too soon.
+ timespec ts{};
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ // zero ID has a special meaning, so start allocation at least at AUDIO_UNIQUE_ID_USE_MAX
+ uint32_t sessionBase = (uint32_t)std::max((long)1, ts.tv_sec);
// unsigned instead of audio_unique_id_use_t, because ++ operator is unavailable for enum
for (unsigned use = AUDIO_UNIQUE_ID_USE_UNSPECIFIED; use < AUDIO_UNIQUE_ID_USE_MAX; use++) {
- // zero ID has a special meaning, so unavailable
- mNextUniqueIds[use] = AUDIO_UNIQUE_ID_USE_MAX;
+ mNextUniqueIds[use] =
+ ((use == AUDIO_UNIQUE_ID_USE_SESSION) ? sessionBase : 1) * AUDIO_UNIQUE_ID_USE_MAX;
}
+#if 1
+ // FIXME See bug 165702394 and bug 168511485
+ const bool doLog = false;
+#else
const bool doLog = property_get_bool("ro.test_harness", false);
+#endif
if (doLog) {
mLogMemoryDealer = new MemoryDealer(kLogMemorySize, "LogWriters",
MemoryHeapBase::READ_ONLY);
@@ -240,7 +294,7 @@
mMode = AUDIO_MODE_NORMAL;
- gAudioFlinger = this;
+ gAudioFlinger = this; // we are already refcounted, store into atomic pointer.
mDevicesFactoryHalCallback = new DevicesFactoryHalCallbackImpl;
mDevicesFactoryHal->setCallbackOnce(mDevicesFactoryHalCallback);
@@ -251,6 +305,21 @@
return NO_ERROR;
}
+status_t AudioFlinger::setVibratorInfos(
+ const std::vector<media::AudioVibratorInfo>& vibratorInfos) {
+ Mutex::Autolock _l(mLock);
+ mAudioVibratorInfos = vibratorInfos;
+ return NO_ERROR;
+}
+
+// getDefaultVibratorInfo_l must be called with AudioFlinger lock held.
+const media::AudioVibratorInfo* AudioFlinger::getDefaultVibratorInfo_l() {
+ if (mAudioVibratorInfos.empty()) {
+ return nullptr;
+ }
+ return &mAudioVibratorInfos.front();
+}
+
AudioFlinger::~AudioFlinger()
{
while (!mRecordThreads.isEmpty()) {
@@ -297,11 +366,9 @@
sp<MmapStreamInterface>& interface,
audio_port_handle_t *handle)
{
- sp<AudioFlinger> af;
- {
- Mutex::Autolock _l(gLock);
- af = gAudioFlinger.promote();
- }
+ // TODO: Use ServiceManager to get IAudioFlinger instead of by atomic pointer.
+ // This allows moving oboeservice (AAudio) to a separate process in the future.
+ sp<AudioFlinger> af = AudioFlinger::gAudioFlinger.load(); // either nullptr or singleton AF.
status_t ret = NO_INIT;
if (af != 0) {
ret = af->openMmapStream(
@@ -342,7 +409,7 @@
ret = AudioSystem::getOutputForAttr(&localAttr, &io,
actualSessionId,
- &streamType, client.clientPid, client.clientUid,
+ &streamType, client.identity,
&fullConfig,
(audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ |
AUDIO_OUTPUT_FLAG_DIRECT),
@@ -353,9 +420,7 @@
ret = AudioSystem::getInputForAttr(&localAttr, &io,
RECORD_RIID_INVALID,
actualSessionId,
- client.clientPid,
- client.clientUid,
- client.packageName,
+ client.identity,
config,
AUDIO_INPUT_FLAG_MMAP_NOIRQ, deviceId, &portId);
}
@@ -391,14 +456,19 @@
/* static */
int AudioFlinger::onExternalVibrationStart(const sp<os::ExternalVibration>& externalVibration) {
sp<os::IExternalVibratorService> evs = getExternalVibratorService();
- if (evs != 0) {
+ if (evs != nullptr) {
int32_t ret;
binder::Status status = evs->onExternalVibrationStart(*externalVibration, &ret);
if (status.isOk()) {
+ ALOGD("%s, start external vibration with intensity as %d", __func__, ret);
return ret;
}
}
- return AudioMixer::HAPTIC_SCALE_MUTE;
+ ALOGD("%s, start external vibration with intensity as MUTE due to %s",
+ __func__,
+ evs == nullptr ? "external vibration service not found"
+ : "error when querying intensity");
+ return static_cast<int>(os::HapticScale::MUTE);
}
/* static */
@@ -739,10 +809,13 @@
// IAudioFlinger interface
-sp<IAudioTrack> AudioFlinger::createTrack(const CreateTrackInput& input,
- CreateTrackOutput& output,
- status_t *status)
+status_t AudioFlinger::createTrack(const media::CreateTrackRequest& _input,
+ media::CreateTrackResponse& _output)
{
+ // Local version of VALUE_OR_RETURN, specific to this method's calling conventions.
+ CreateTrackInput input = VALUE_OR_RETURN_STATUS(CreateTrackInput::fromAidl(_input));
+ CreateTrackOutput output;
+
sp<PlaybackThread::Track> track;
sp<TrackHandle> trackHandle;
sp<Client> client;
@@ -751,27 +824,33 @@
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
std::vector<audio_io_handle_t> secondaryOutputs;
- bool updatePid = (input.clientInfo.clientPid == -1);
+ // TODO b/182392553: refactor or make clearer
+ pid_t clientPid =
+ VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_pid_t(input.clientInfo.identity.pid));
+ bool updatePid = (clientPid == (pid_t)-1);
const uid_t callingUid = IPCThreadState::self()->getCallingUid();
- uid_t clientUid = input.clientInfo.clientUid;
+ uid_t clientUid =
+ VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_uid_t(input.clientInfo.identity.uid));
audio_io_handle_t effectThreadId = AUDIO_IO_HANDLE_NONE;
std::vector<int> effectIds;
audio_attributes_t localAttr = input.attr;
+ Identity adjIdentity = input.clientInfo.identity;
if (!isAudioServerOrMediaServerUid(callingUid)) {
ALOGW_IF(clientUid != callingUid,
"%s uid %d tried to pass itself off as %d",
__FUNCTION__, callingUid, clientUid);
+ adjIdentity.uid = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(callingUid));
clientUid = callingUid;
updatePid = true;
}
- pid_t clientPid = input.clientInfo.clientPid;
const pid_t callingPid = IPCThreadState::self()->getCallingPid();
if (updatePid) {
- ALOGW_IF(clientPid != -1 && clientPid != callingPid,
+ ALOGW_IF(clientPid != (pid_t)-1 && clientPid != callingPid,
"%s uid %d pid %d tried to pass itself off as pid %d",
__func__, callingUid, callingPid, clientPid);
clientPid = callingPid;
+ adjIdentity.pid = VALUE_OR_RETURN_STATUS(legacy2aidl_pid_t_int32_t(callingPid));
}
audio_session_t sessionId = input.sessionId;
@@ -786,7 +865,7 @@
output.outputId = AUDIO_IO_HANDLE_NONE;
output.selectedDeviceId = input.selectedDeviceId;
lStatus = AudioSystem::getOutputForAttr(&localAttr, &output.outputId, sessionId, &streamType,
- clientPid, clientUid, &input.config, input.flags,
+ adjIdentity, &input.config, input.flags,
&output.selectedDeviceId, &portId, &secondaryOutputs);
if (lStatus != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) {
@@ -851,9 +930,8 @@
&output.frameCount, &output.notificationFrameCount,
input.notificationsPerBuffer, input.speed,
input.sharedBuffer, sessionId, &output.flags,
- callingPid, input.clientInfo.clientTid, clientUid,
- &lStatus, portId, input.audioTrackCallback,
- input.opPackageName);
+ callingPid, adjIdentity, input.clientInfo.clientTid,
+ &lStatus, portId, input.audioTrackCallback);
LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (track == 0));
// we don't abort yet if lStatus != NO_ERROR; there is still work to be done regardless
@@ -999,15 +1077,14 @@
AudioSystem::moveEffectsToIo(effectIds, effectThreadId);
}
- // return handle to client
- trackHandle = new TrackHandle(track);
+ output.audioTrack = new TrackHandle(track);
+ _output = VALUE_OR_FATAL(output.toAidl());
Exit:
if (lStatus != NO_ERROR && output.outputId != AUDIO_IO_HANDLE_NONE) {
AudioSystem::releaseOutput(portId);
}
- *status = lStatus;
- return trackHandle;
+ return lStatus;
}
uint32_t AudioFlinger::sampleRate(audio_io_handle_t ioHandle) const
@@ -1431,7 +1508,7 @@
}
-void AudioFlinger::broacastParametersToRecordThreads_l(const String8& keyValuePairs)
+void AudioFlinger::broadcastParametersToRecordThreads_l(const String8& keyValuePairs)
{
for (size_t i = 0; i < mRecordThreads.size(); i++) {
mRecordThreads.valueAt(i)->setParameters(keyValuePairs);
@@ -1603,7 +1680,7 @@
int value;
if ((param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) &&
(value != 0)) {
- broacastParametersToRecordThreads_l(filteredKeyValuePairs);
+ broadcastParametersToRecordThreads_l(filteredKeyValuePairs);
}
}
}
@@ -1780,7 +1857,7 @@
return BAD_VALUE;
}
-void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client)
+void AudioFlinger::registerClient(const sp<media::IAudioFlingerClient>& client)
{
Mutex::Autolock _l(mLock);
if (client == 0) {
@@ -1855,13 +1932,18 @@
void AudioFlinger::ioConfigChanged(audio_io_config_event event,
const sp<AudioIoDescriptor>& ioDesc,
- pid_t pid)
-{
+ pid_t pid) {
+ media::AudioIoDescriptor descAidl = VALUE_OR_FATAL(
+ legacy2aidl_AudioIoDescriptor_AudioIoDescriptor(ioDesc));
+ media::AudioIoConfigEvent eventAidl = VALUE_OR_FATAL(
+ legacy2aidl_audio_io_config_event_AudioIoConfigEvent(event));
+
Mutex::Autolock _l(mClientLock);
size_t size = mNotificationClients.size();
for (size_t i = 0; i < size; i++) {
if ((pid == 0) || (mNotificationClients.keyAt(i) == pid)) {
- mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event, ioDesc);
+ mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(eventAidl,
+ descAidl);
}
}
}
@@ -1935,7 +2017,7 @@
// ----------------------------------------------------------------------------
AudioFlinger::NotificationClient::NotificationClient(const sp<AudioFlinger>& audioFlinger,
- const sp<IAudioFlingerClient>& client,
+ const sp<media::IAudioFlingerClient>& client,
pid_t pid,
uid_t uid)
: mAudioFlinger(audioFlinger), mPid(pid), mUid(uid), mAudioFlingerClient(client)
@@ -1990,10 +2072,12 @@
// ----------------------------------------------------------------------------
-sp<media::IAudioRecord> AudioFlinger::createRecord(const CreateRecordInput& input,
- CreateRecordOutput& output,
- status_t *status)
+status_t AudioFlinger::createRecord(const media::CreateRecordRequest& _input,
+ media::CreateRecordResponse& _output)
{
+ CreateRecordInput input = VALUE_OR_RETURN_STATUS(CreateRecordInput::fromAidl(_input));
+ CreateRecordOutput output;
+
sp<RecordThread::RecordTrack> recordTrack;
sp<RecordHandle> recordHandle;
sp<Client> client;
@@ -2005,23 +2089,25 @@
output.buffers.clear();
output.inputId = AUDIO_IO_HANDLE_NONE;
- bool updatePid = (input.clientInfo.clientPid == -1);
+ // TODO b/182392553: refactor or clean up
+ Identity adjIdentity = input.clientInfo.identity;
+ bool updatePid = (adjIdentity.pid == -1);
const uid_t callingUid = IPCThreadState::self()->getCallingUid();
- uid_t clientUid = input.clientInfo.clientUid;
+ const uid_t currentUid = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(adjIdentity.uid));
if (!isAudioServerOrMediaServerUid(callingUid)) {
- ALOGW_IF(clientUid != callingUid,
+ ALOGW_IF(currentUid != callingUid,
"%s uid %d tried to pass itself off as %d",
- __FUNCTION__, callingUid, clientUid);
- clientUid = callingUid;
+ __FUNCTION__, callingUid, currentUid);
+ adjIdentity.uid = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(callingUid));
updatePid = true;
}
- pid_t clientPid = input.clientInfo.clientPid;
const pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ const pid_t currentPid = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_pid_t(adjIdentity.pid));
if (updatePid) {
- ALOGW_IF(clientPid != -1 && clientPid != callingPid,
+ ALOGW_IF(currentPid != (pid_t)-1 && currentPid != callingPid,
"%s uid %d pid %d tried to pass itself off as pid %d",
- __func__, callingUid, callingPid, clientPid);
- clientPid = callingPid;
+ __func__, callingUid, callingPid, currentPid);
+ adjIdentity.pid = VALUE_OR_RETURN_STATUS(legacy2aidl_pid_t_int32_t(callingPid));
}
// we don't yet support anything other than linear PCM
@@ -2049,7 +2135,7 @@
output.selectedDeviceId = input.selectedDeviceId;
output.flags = input.flags;
- client = registerPid(clientPid);
+ client = registerPid(VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(adjIdentity.pid)));
// Not a conventional loop, but a retry loop for at most two iterations total.
// Try first maybe with FAST flag then try again without FAST flag if that fails.
@@ -2069,9 +2155,7 @@
input.riid,
sessionId,
// FIXME compare to AudioTrack
- clientPid,
- clientUid,
- input.opPackageName,
+ adjIdentity,
&input.config,
output.flags, &output.selectedDeviceId, &portId);
if (lStatus != NO_ERROR) {
@@ -2098,10 +2182,9 @@
input.config.format, input.config.channel_mask,
&output.frameCount, sessionId,
&output.notificationFrameCount,
- callingPid, clientUid, &output.flags,
+ callingPid, adjIdentity, &output.flags,
input.clientInfo.clientTid,
- &lStatus, portId,
- input.opPackageName);
+ &lStatus, portId, input.maxSharedAudioHistoryMs);
LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (recordTrack == 0));
// lStatus == BAD_TYPE means FAST flag was rejected: request a new input from
@@ -2131,8 +2214,8 @@
output.buffers = recordTrack->getBuffers();
output.portId = portId;
- // return handle to client
- recordHandle = new RecordHandle(recordTrack);
+ output.audioRecord = new RecordHandle(recordTrack);
+ _output = VALUE_OR_FATAL(output.toAidl());
Exit:
if (lStatus != NO_ERROR) {
@@ -2150,8 +2233,7 @@
}
}
- *status = lStatus;
- return recordHandle;
+ return lStatus;
}
@@ -2323,6 +2405,11 @@
{
ALOGV(__func__);
+ status_t status = AudioValidator::validateAudioPortConfig(*config);
+ if (status != NO_ERROR) {
+ return status;
+ }
+
audio_module_handle_t module;
if (config->type == AUDIO_PORT_TYPE_DEVICE) {
module = config->ext.device.hw_module;
@@ -2560,20 +2647,28 @@
return 0;
}
-status_t AudioFlinger::openOutput(audio_module_handle_t module,
- audio_io_handle_t *output,
- audio_config_t *config,
- const sp<DeviceDescriptorBase>& device,
- uint32_t *latencyMs,
- audio_output_flags_t flags)
+status_t AudioFlinger::openOutput(const media::OpenOutputRequest& request,
+ media::OpenOutputResponse* response)
{
+ audio_module_handle_t module = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_int32_t_audio_module_handle_t(request.module));
+ audio_config_t config = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_AudioConfig_audio_config_t(request.config));
+ sp<DeviceDescriptorBase> device = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_DeviceDescriptorBase(request.device));
+ audio_output_flags_t flags = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_int32_t_audio_output_flags_t_mask(request.flags));
+
+ audio_io_handle_t output;
+ uint32_t latencyMs;
+
ALOGI("openOutput() this %p, module %d Device %s, SamplingRate %d, Format %#08x, "
"Channels %#x, flags %#x",
this, module,
device->toString().c_str(),
- config->sample_rate,
- config->format,
- config->channel_mask,
+ config.sample_rate,
+ config.format,
+ config.channel_mask,
flags);
audio_devices_t deviceType = device->type();
@@ -2585,11 +2680,11 @@
Mutex::Autolock _l(mLock);
- sp<ThreadBase> thread = openOutput_l(module, output, config, deviceType, address, flags);
+ sp<ThreadBase> thread = openOutput_l(module, &output, &config, deviceType, address, flags);
if (thread != 0) {
if ((flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) == 0) {
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
- *latencyMs = playbackThread->latency();
+ latencyMs = playbackThread->latency();
// notify client processes of the new output creation
playbackThread->ioConfigChanged(AUDIO_OUTPUT_OPENED);
@@ -2609,6 +2704,11 @@
MmapThread *mmapThread = (MmapThread *)thread.get();
mmapThread->ioConfigChanged(AUDIO_OUTPUT_OPENED);
}
+ response->output = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(output));
+ response->config = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_config_t_AudioConfig(config));
+ response->latencyMs = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(latencyMs));
+ response->flags = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_output_flags_t_int32_t_mask(flags));
return NO_ERROR;
}
@@ -2761,22 +2861,36 @@
return NO_ERROR;
}
-status_t AudioFlinger::openInput(audio_module_handle_t module,
- audio_io_handle_t *input,
- audio_config_t *config,
- audio_devices_t *devices,
- const String8& address,
- audio_source_t source,
- audio_input_flags_t flags)
+status_t AudioFlinger::openInput(const media::OpenInputRequest& request,
+ media::OpenInputResponse* response)
{
Mutex::Autolock _l(mLock);
- if (*devices == AUDIO_DEVICE_NONE) {
+ if (request.device.type == AUDIO_DEVICE_NONE) {
return BAD_VALUE;
}
+ audio_io_handle_t input = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_int32_t_audio_io_handle_t(request.input));
+ audio_config_t config = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_AudioConfig_audio_config_t(request.config));
+ AudioDeviceTypeAddr device = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_AudioDeviceTypeAddress(request.device));
+
sp<ThreadBase> thread = openInput_l(
- module, input, config, *devices, address, source, flags, AUDIO_DEVICE_NONE, String8{});
+ VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_module_handle_t(request.module)),
+ &input,
+ &config,
+ device.mType,
+ device.address().c_str(),
+ VALUE_OR_RETURN_STATUS(aidl2legacy_AudioSourceType_audio_source_t(request.source)),
+ VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_input_flags_t_mask(request.flags)),
+ AUDIO_DEVICE_NONE,
+ String8{});
+
+ response->input = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(input));
+ response->config = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_config_t_AudioConfig(config));
+ response->device = request.device;
if (thread != 0) {
// notify client processes of the new input creation
@@ -2790,7 +2904,7 @@
audio_io_handle_t *input,
audio_config_t *config,
audio_devices_t devices,
- const String8& address,
+ const char* address,
audio_source_t source,
audio_input_flags_t flags,
audio_devices_t outputDevice,
@@ -2820,7 +2934,7 @@
sp<DeviceHalInterface> inHwHal = inHwDev->hwDevice();
sp<StreamInHalInterface> inStream;
status_t status = inHwHal->openInputStream(
- *input, devices, &halconfig, flags, address.string(), source,
+ *input, devices, &halconfig, flags, address, source,
outputDevice, outputDeviceAddress, &inStream);
ALOGV("openInput_l() openInputStream returned input %p, devices %#x, SamplingRate %d"
", Format %#x, Channels %#x, flags %#x, status %d addr %s",
@@ -2830,7 +2944,7 @@
halconfig.format,
halconfig.channel_mask,
flags,
- status, address.string());
+ status, address);
// If the input could not be opened with the requested parameters and we can handle the
// conversion internally, try to open again with the proposed parameters.
@@ -2844,7 +2958,7 @@
ALOGV("openInput_l() reopening with proposed sampling rate and channel mask");
inStream.clear();
status = inHwHal->openInputStream(
- *input, devices, &halconfig, flags, address.string(), source,
+ *input, devices, &halconfig, flags, address, source,
outputDevice, outputDeviceAddress, &inStream);
// FIXME log this new status; HAL should not propose any further changes
}
@@ -3317,6 +3431,16 @@
return minThread;
}
+AudioFlinger::ThreadBase *AudioFlinger::hapticPlaybackThread_l() const {
+ for (size_t i = 0; i < mPlaybackThreads.size(); ++i) {
+ PlaybackThread *thread = mPlaybackThreads.valueAt(i).get();
+ if (thread->hapticChannelMask() != AUDIO_CHANNEL_NONE) {
+ return thread;
+ }
+ }
+ return nullptr;
+}
+
sp<AudioFlinger::SyncEvent> AudioFlinger::createSyncEvent(AudioSystem::sync_event_t type,
audio_session_t triggerSession,
audio_session_t listenerSession,
@@ -3447,40 +3571,43 @@
return status;
}
-sp<IEffect> AudioFlinger::createEffect(
- effect_descriptor_t *pDesc,
- const sp<IEffectClient>& effectClient,
- int32_t priority,
- audio_io_handle_t io,
- audio_session_t sessionId,
- const AudioDeviceTypeAddr& device,
- const String16& opPackageName,
- pid_t pid,
- bool probe,
- status_t *status,
- int *id,
- int *enabled)
-{
- status_t lStatus = NO_ERROR;
- sp<EffectHandle> handle;
- effect_descriptor_t desc;
+status_t AudioFlinger::createEffect(const media::CreateEffectRequest& request,
+ media::CreateEffectResponse* response) {
+ const sp<IEffectClient>& effectClient = request.client;
+ const int32_t priority = request.priority;
+ const AudioDeviceTypeAddr device = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_AudioDeviceTypeAddress(request.device));
+ Identity adjIdentity = request.identity;
+ const audio_session_t sessionId = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_int32_t_audio_session_t(request.sessionId));
+ audio_io_handle_t io = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_int32_t_audio_io_handle_t(request.output));
+ const effect_descriptor_t descIn = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_EffectDescriptor_effect_descriptor_t(request.desc));
+ const bool probe = request.probe;
+ sp<EffectHandle> handle;
+ effect_descriptor_t descOut;
+ int enabledOut = 0;
+ int idOut = -1;
+
+ status_t lStatus = NO_ERROR;
+
+ // TODO b/182392553: refactor or make clearer
const uid_t callingUid = IPCThreadState::self()->getCallingUid();
- if (pid == -1 || !isAudioServerOrMediaServerUid(callingUid)) {
+ adjIdentity.uid = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(callingUid));
+ pid_t currentPid = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_pid_t(adjIdentity.pid));
+ if (currentPid == -1 || !isAudioServerOrMediaServerUid(callingUid)) {
const pid_t callingPid = IPCThreadState::self()->getCallingPid();
- ALOGW_IF(pid != -1 && pid != callingPid,
+ ALOGW_IF(currentPid != -1 && currentPid != callingPid,
"%s uid %d pid %d tried to pass itself off as pid %d",
- __func__, callingUid, callingPid, pid);
- pid = callingPid;
+ __func__, callingUid, callingPid, currentPid);
+ adjIdentity.pid = VALUE_OR_RETURN_STATUS(legacy2aidl_pid_t_int32_t(callingPid));
+ currentPid = callingPid;
}
ALOGV("createEffect pid %d, effectClient %p, priority %d, sessionId %d, io %d, factory %p",
- pid, effectClient.get(), priority, sessionId, io, mEffectsFactoryHal.get());
-
- if (pDesc == NULL) {
- lStatus = BAD_VALUE;
- goto Exit;
- }
+ adjIdentity.pid, effectClient.get(), priority, sessionId, io, mEffectsFactoryHal.get());
if (mEffectsFactoryHal == 0) {
ALOGE("%s: no effects factory hal", __func__);
@@ -3508,7 +3635,7 @@
goto Exit;
}
} else if (sessionId == AUDIO_SESSION_DEVICE) {
- if (!modifyDefaultAudioEffectsAllowed(pid, callingUid)) {
+ if (!modifyDefaultAudioEffectsAllowed(adjIdentity)) {
ALOGE("%s: device effect permission denied for uid %d", __func__, callingUid);
lStatus = PERMISSION_DENIED;
goto Exit;
@@ -3537,7 +3664,7 @@
// otherwise no preference.
uint32_t preferredType = (sessionId == AUDIO_SESSION_OUTPUT_MIX ?
EFFECT_FLAG_TYPE_AUXILIARY : EFFECT_FLAG_TYPE_MASK);
- lStatus = getEffectDescriptor(&pDesc->uuid, &pDesc->type, preferredType, &desc);
+ lStatus = getEffectDescriptor(&descIn.uuid, &descIn.type, preferredType, &descOut);
if (lStatus < 0) {
ALOGW("createEffect() error %d from getEffectDescriptor", lStatus);
goto Exit;
@@ -3545,44 +3672,52 @@
// Do not allow auxiliary effects on a session different from 0 (output mix)
if (sessionId != AUDIO_SESSION_OUTPUT_MIX &&
- (desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ (descOut.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
lStatus = INVALID_OPERATION;
goto Exit;
}
// check recording permission for visualizer
- if ((memcmp(&desc.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0) &&
+ if ((memcmp(&descOut.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0) &&
// TODO: Do we need to start/stop op - i.e. is there recording being performed?
- !recordingAllowed(opPackageName, pid, callingUid)) {
+ !recordingAllowed(adjIdentity)) {
lStatus = PERMISSION_DENIED;
goto Exit;
}
- // return effect descriptor
- *pDesc = desc;
+ const bool hapticPlaybackRequired = EffectModule::isHapticGenerator(&descOut.type);
+ if (hapticPlaybackRequired
+ && (sessionId == AUDIO_SESSION_DEVICE
+ || sessionId == AUDIO_SESSION_OUTPUT_MIX
+ || sessionId == AUDIO_SESSION_OUTPUT_STAGE)) {
+ // haptic-generating effect is only valid when the session id is a general session id
+ lStatus = INVALID_OPERATION;
+ goto Exit;
+ }
+
if (io == AUDIO_IO_HANDLE_NONE && sessionId == AUDIO_SESSION_OUTPUT_MIX) {
// if the output returned by getOutputForEffect() is removed before we lock the
// mutex below, the call to checkPlaybackThread_l(io) below will detect it
// and we will exit safely
- io = AudioSystem::getOutputForEffect(&desc);
+ io = AudioSystem::getOutputForEffect(&descOut);
ALOGV("createEffect got output %d", io);
}
Mutex::Autolock _l(mLock);
if (sessionId == AUDIO_SESSION_DEVICE) {
- sp<Client> client = registerPid(pid);
+ sp<Client> client = registerPid(currentPid);
ALOGV("%s device type %#x address %s", __func__, device.mType, device.getAddress());
handle = mDeviceEffectManager.createEffect_l(
- &desc, device, client, effectClient, mPatchPanel.patches_l(),
- enabled, &lStatus, probe);
+ &descOut, device, client, effectClient, mPatchPanel.patches_l(),
+ &enabledOut, &lStatus, probe);
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();
+ if (handle.get() != nullptr) idOut = handle->id();
}
goto Register;
}
@@ -3612,8 +3747,8 @@
// Detect if the effect is created after an AudioRecord is destroyed.
if (getOrphanEffectChain_l(sessionId).get() != nullptr) {
ALOGE("%s: effect %s with no specified io handle is denied because the AudioRecord"
- " for session %d no longer exists",
- __func__, desc.name, sessionId);
+ " for session %d no longer exists",
+ __func__, descOut.name, sessionId);
lStatus = PERMISSION_DENIED;
goto Exit;
}
@@ -3627,17 +3762,27 @@
if (io == AUDIO_IO_HANDLE_NONE && mPlaybackThreads.size() > 0) {
io = mPlaybackThreads.keyAt(0);
}
- ALOGV("createEffect() got io %d for effect %s", io, desc.name);
+ ALOGV("createEffect() got io %d for effect %s", io, descOut.name);
} else if (checkPlaybackThread_l(io) != nullptr) {
// allow only one effect chain per sessionId on mPlaybackThreads.
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
const audio_io_handle_t checkIo = mPlaybackThreads.keyAt(i);
- if (io == checkIo) continue;
+ if (io == checkIo) {
+ if (hapticPlaybackRequired
+ && mPlaybackThreads.valueAt(i)
+ ->hapticChannelMask() == AUDIO_CHANNEL_NONE) {
+ ALOGE("%s: haptic playback thread is required while the required playback "
+ "thread(io=%d) doesn't support", __func__, (int)io);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ continue;
+ }
const uint32_t sessionType =
mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId);
if ((sessionType & ThreadBase::EFFECT_SESSION) != 0) {
ALOGE("%s: effect %s io %d denied because session %d effect exists on io %d",
- __func__, desc.name, (int)io, (int)sessionId, (int)checkIo);
+ __func__, descOut.name, (int) io, (int) sessionId, (int) checkIo);
android_errorWriteLog(0x534e4554, "123237974");
lStatus = BAD_VALUE;
goto Exit;
@@ -3665,19 +3810,38 @@
}
}
- sp<Client> client = registerPid(pid);
+ sp<Client> client = registerPid(currentPid);
// create effect on selected output thread
bool pinned = !audio_is_global_session(sessionId) && isSessionAcquired_l(sessionId);
+ ThreadBase *oriThread = nullptr;
+ if (hapticPlaybackRequired && thread->hapticChannelMask() == AUDIO_CHANNEL_NONE) {
+ ThreadBase *hapticThread = hapticPlaybackThread_l();
+ if (hapticThread == nullptr) {
+ ALOGE("%s haptic thread not found while it is required", __func__);
+ lStatus = INVALID_OPERATION;
+ goto Exit;
+ }
+ if (hapticThread != thread) {
+ // Force to use haptic thread for haptic-generating effect.
+ oriThread = thread;
+ thread = hapticThread;
+ }
+ }
handle = thread->createEffect_l(client, effectClient, priority, sessionId,
- &desc, enabled, &lStatus, pinned, probe);
+ &descOut, &enabledOut, &lStatus, pinned, probe);
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();
+ if (handle.get() != nullptr) idOut = handle->id();
+ // Invalidate audio session when haptic playback is created.
+ if (hapticPlaybackRequired && oriThread != nullptr) {
+ // invalidateTracksForAudioSession will trigger locking the thread.
+ oriThread->invalidateTracksForAudioSession(sessionId);
+ }
}
}
@@ -3695,9 +3859,14 @@
handle.clear();
}
+ response->id = idOut;
+ response->enabled = enabledOut != 0;
+ response->effect = handle;
+ response->desc = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_effect_descriptor_t_EffectDescriptor(descOut));
+
Exit:
- *status = lStatus;
- return handle;
+ return lStatus;
}
status_t AudioFlinger::moveEffects(audio_session_t sessionId, audio_io_handle_t srcOutput,
@@ -3946,10 +4115,116 @@
// ----------------------------------------------------------------------------
-status_t AudioFlinger::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- return BnAudioFlinger::onTransact(code, data, reply, flags);
+status_t AudioFlinger::onTransactWrapper(TransactionCode code,
+ const Parcel& data,
+ uint32_t flags,
+ const std::function<status_t()>& delegate) {
+ (void) data;
+ (void) flags;
+
+ // make sure transactions reserved to AudioPolicyManager do not come from other processes
+ switch (code) {
+ case TransactionCode::SET_STREAM_VOLUME:
+ case TransactionCode::SET_STREAM_MUTE:
+ case TransactionCode::OPEN_OUTPUT:
+ case TransactionCode::OPEN_DUPLICATE_OUTPUT:
+ case TransactionCode::CLOSE_OUTPUT:
+ case TransactionCode::SUSPEND_OUTPUT:
+ case TransactionCode::RESTORE_OUTPUT:
+ case TransactionCode::OPEN_INPUT:
+ case TransactionCode::CLOSE_INPUT:
+ case TransactionCode::INVALIDATE_STREAM:
+ case TransactionCode::SET_VOICE_VOLUME:
+ case TransactionCode::MOVE_EFFECTS:
+ case TransactionCode::SET_EFFECT_SUSPENDED:
+ case TransactionCode::LOAD_HW_MODULE:
+ case TransactionCode::GET_AUDIO_PORT:
+ case TransactionCode::CREATE_AUDIO_PATCH:
+ case TransactionCode::RELEASE_AUDIO_PATCH:
+ case TransactionCode::LIST_AUDIO_PATCHES:
+ case TransactionCode::SET_AUDIO_PORT_CONFIG:
+ case TransactionCode::SET_RECORD_SILENCED:
+ ALOGW("%s: transaction %d received from PID %d",
+ __func__, code, IPCThreadState::self()->getCallingPid());
+ // return status only for non void methods
+ switch (code) {
+ case TransactionCode::SET_RECORD_SILENCED:
+ case TransactionCode::SET_EFFECT_SUSPENDED:
+ break;
+ default:
+ return INVALID_OPERATION;
+ }
+ // Fail silently in these cases.
+ return OK;
+ default:
+ break;
+ }
+
+ // make sure the following transactions come from system components
+ switch (code) {
+ case TransactionCode::SET_MASTER_VOLUME:
+ case TransactionCode::SET_MASTER_MUTE:
+ case TransactionCode::MASTER_MUTE:
+ case TransactionCode::SET_MODE:
+ case TransactionCode::SET_MIC_MUTE:
+ case TransactionCode::SET_LOW_RAM_DEVICE:
+ case TransactionCode::SYSTEM_READY:
+ case TransactionCode::SET_AUDIO_HAL_PIDS:
+ case TransactionCode::SET_VIBRATOR_INFOS: {
+ if (!isServiceUid(IPCThreadState::self()->getCallingUid())) {
+ ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
+ __func__, code, IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid());
+ // return status only for non void methods
+ switch (code) {
+ case TransactionCode::SYSTEM_READY:
+ break;
+ default:
+ return INVALID_OPERATION;
+ }
+ // Fail silently in these cases.
+ return OK;
+ }
+ } break;
+ default:
+ break;
+ }
+
+ // List of relevant events that trigger log merging.
+ // Log merging should activate during audio activity of any kind. This are considered the
+ // most relevant events.
+ // TODO should select more wisely the items from the list
+ switch (code) {
+ case TransactionCode::CREATE_TRACK:
+ case TransactionCode::CREATE_RECORD:
+ case TransactionCode::SET_MASTER_VOLUME:
+ case TransactionCode::SET_MASTER_MUTE:
+ case TransactionCode::SET_MIC_MUTE:
+ case TransactionCode::SET_PARAMETERS:
+ case TransactionCode::CREATE_EFFECT:
+ case TransactionCode::SYSTEM_READY: {
+ requestLogMerge();
+ break;
+ }
+ default:
+ break;
+ }
+
+ std::string tag("IAudioFlinger command " +
+ std::to_string(static_cast<std::underlying_type_t<TransactionCode>>(code)));
+ TimeCheck check(tag.c_str());
+
+ // Make sure we connect to Audio Policy Service before calling into AudioFlinger:
+ // - AudioFlinger can call into Audio Policy Service with its global mutex held
+ // - If this is the first time Audio Policy Service is queried from inside audioserver process
+ // this will trigger Audio Policy Manager initialization.
+ // - Audio Policy Manager initialization calls into AudioFlinger which will try to lock
+ // its global mutex and a deadlock will occur.
+ if (IPCThreadState::self()->getCallingPid() != getpid()) {
+ AudioSystem::get_audio_policy_service();
+ }
+
+ return delegate();
}
} // namespace android
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 10d4029..4b03d10 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -33,17 +33,17 @@
#include <sys/types.h>
#include <limits.h>
+#include <android/media/BnAudioTrack.h>
+#include <android/media/IAudioFlingerClient.h>
#include <android/media/IAudioTrackCallback.h>
#include <android/os/BnExternalVibrationController.h>
-#include <android-base/macros.h>
+#include <android-base/macros.h>
#include <cutils/atomic.h>
#include <cutils/compiler.h>
-#include <cutils/properties.h>
+#include <cutils/properties.h>
#include <media/IAudioFlinger.h>
-#include <media/IAudioFlingerClient.h>
-#include <media/IAudioTrack.h>
#include <media/AudioSystem.h>
#include <media/AudioTrack.h>
#include <media/MmapStreamInterface.h>
@@ -73,6 +73,7 @@
#include <media/ExtendedAudioBufferProvider.h>
#include <media/VolumeShaper.h>
#include <mediautils/ServiceUtilities.h>
+#include <mediautils/Synchronization.h>
#include <audio_utils/clock.h>
#include <audio_utils/FdToString.h>
@@ -91,15 +92,17 @@
#include "ThreadMetrics.h"
#include "TrackMetrics.h"
-#include <powermanager/IPowerManager.h>
+#include <android/os/IPowerManager.h>
#include <media/nblog/NBLog.h>
#include <private/media/AudioEffectShared.h>
#include <private/media/AudioTrackShared.h>
#include <vibrator/ExternalVibration.h>
+#include <vibrator/ExternalVibrationUtils.h>
#include "android/media/BnAudioRecord.h"
+#include "android/media/BnEffect.h"
namespace android {
@@ -121,25 +124,22 @@
#define INCLUDING_FROM_AUDIOFLINGER_H
-class AudioFlinger :
- public BinderService<AudioFlinger>,
- public BnAudioFlinger
+class AudioFlinger : public AudioFlingerServerAdapter::Delegate
{
- friend class BinderService<AudioFlinger>; // for AudioFlinger()
-
public:
- static const char* getServiceName() ANDROID_API { return "media.audio_flinger"; }
+ static void instantiate() ANDROID_API;
- virtual status_t dump(int fd, const Vector<String16>& args);
+ static media::permission::Identity checkIdentityPackage(
+ const media::permission::Identity& identity);
+
+ status_t dump(int fd, const Vector<String16>& args) override;
// IAudioFlinger interface, in binder opcode order
- virtual sp<IAudioTrack> createTrack(const CreateTrackInput& input,
- CreateTrackOutput& output,
- status_t *status);
+ status_t createTrack(const media::CreateTrackRequest& input,
+ media::CreateTrackResponse& output) override;
- virtual sp<media::IAudioRecord> createRecord(const CreateRecordInput& input,
- CreateRecordOutput& output,
- status_t *status);
+ status_t createRecord(const media::CreateRecordRequest& input,
+ media::CreateRecordResponse& output) override;
virtual uint32_t sampleRate(audio_io_handle_t ioHandle) const;
virtual audio_format_t format(audio_io_handle_t output) const;
@@ -175,17 +175,13 @@
virtual status_t setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs);
virtual String8 getParameters(audio_io_handle_t ioHandle, const String8& keys) const;
- virtual void registerClient(const sp<IAudioFlingerClient>& client);
+ virtual void registerClient(const sp<media::IAudioFlingerClient>& client);
virtual size_t getInputBufferSize(uint32_t sampleRate, audio_format_t format,
audio_channel_mask_t channelMask) const;
- virtual status_t openOutput(audio_module_handle_t module,
- audio_io_handle_t *output,
- audio_config_t *config,
- const sp<DeviceDescriptorBase>& device,
- uint32_t *latencyMs,
- audio_output_flags_t flags);
+ virtual status_t openOutput(const media::OpenOutputRequest& request,
+ media::OpenOutputResponse* response);
virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1,
audio_io_handle_t output2);
@@ -196,13 +192,8 @@
virtual status_t restoreOutput(audio_io_handle_t output);
- virtual status_t openInput(audio_module_handle_t module,
- audio_io_handle_t *input,
- audio_config_t *config,
- audio_devices_t *device,
- const String8& address,
- audio_source_t source,
- audio_input_flags_t flags);
+ virtual status_t openInput(const media::OpenInputRequest& request,
+ media::OpenInputResponse* response);
virtual status_t closeInput(audio_io_handle_t input);
@@ -231,19 +222,8 @@
uint32_t preferredTypeFlag,
effect_descriptor_t *descriptor) const;
- virtual sp<IEffect> createEffect(
- effect_descriptor_t *pDesc,
- const sp<IEffectClient>& effectClient,
- int32_t priority,
- audio_io_handle_t io,
- audio_session_t sessionId,
- const AudioDeviceTypeAddr& device,
- const String16& opPackageName,
- pid_t pid,
- bool probe,
- status_t *status /*non-NULL*/,
- int *id,
- int *enabled);
+ virtual status_t createEffect(const media::CreateEffectRequest& request,
+ media::CreateEffectResponse* response);
virtual status_t moveEffects(audio_session_t sessionId, audio_io_handle_t srcOutput,
audio_io_handle_t dstOutput);
@@ -264,7 +244,7 @@
struct audio_port *ports);
/* Get attributes for a given audio port */
- virtual status_t getAudioPort(struct audio_port *port);
+ virtual status_t getAudioPort(struct audio_port_v7 *port);
/* Create an audio patch between several source and sink ports */
virtual status_t createAudioPatch(const struct audio_patch *patch,
@@ -290,11 +270,10 @@
virtual status_t setAudioHalPids(const std::vector<pid_t>& pids);
- virtual status_t onTransact(
- uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags);
+ virtual status_t setVibratorInfos(const std::vector<media::AudioVibratorInfo>& vibratorInfos);
+
+ status_t onTransactWrapper(TransactionCode code, const Parcel& data, uint32_t flags,
+ const std::function<status_t()>& delegate) override;
// end of IAudioFlinger interface
@@ -323,6 +302,8 @@
void updateDownStreamPatches_l(const struct audio_patch *patch,
const std::set<audio_io_handle_t> streams);
+ const media::AudioVibratorInfo* getDefaultVibratorInfo_l();
+
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;
@@ -333,6 +314,24 @@
Mutex mUnregisteredWritersLock;
public:
+ // Life cycle of gAudioFlinger and AudioFlinger:
+ //
+ // AudioFlinger is created once and survives until audioserver crashes
+ // irrespective of sp<> and wp<> as it is refcounted by ServiceManager and we
+ // don't issue a ServiceManager::tryUnregisterService().
+ //
+ // gAudioFlinger is an atomic pointer set on AudioFlinger::onFirstRef().
+ // After this is set, it is safe to obtain a wp<> or sp<> from it as the
+ // underlying object does not go away.
+ //
+ // Note: For most inner classes, it is acceptable to hold a reference to the outer
+ // AudioFlinger instance as creation requires AudioFlinger to exist in the first place.
+ //
+ // An atomic here ensures underlying writes have completed before setting
+ // the pointer. Access by memory_order_seq_cst.
+ //
+
+ static inline std::atomic<AudioFlinger *> gAudioFlinger = nullptr;
class SyncEvent;
@@ -491,12 +490,12 @@
class NotificationClient : public IBinder::DeathRecipient {
public:
NotificationClient(const sp<AudioFlinger>& audioFlinger,
- const sp<IAudioFlingerClient>& client,
+ const sp<media::IAudioFlingerClient>& client,
pid_t pid,
uid_t uid);
virtual ~NotificationClient();
- sp<IAudioFlingerClient> audioFlingerClient() const { return mAudioFlingerClient; }
+ sp<media::IAudioFlingerClient> audioFlingerClient() const { return mAudioFlingerClient; }
pid_t getPid() const { return mPid; }
uid_t getUid() const { return mUid; }
@@ -509,7 +508,7 @@
const sp<AudioFlinger> mAudioFlinger;
const pid_t mPid;
const uid_t mUid;
- const sp<IAudioFlingerClient> mAudioFlingerClient;
+ const sp<media::IAudioFlingerClient> mAudioFlingerClient;
};
// --- MediaLogNotifier ---
@@ -542,6 +541,7 @@
const sp<MediaLogNotifier> mMediaLogNotifier;
// This is a helper that is called during incoming binder calls.
+ // Requests media.log to start merging log buffers
void requestLogMerge();
class TrackHandle;
@@ -627,34 +627,38 @@
}
// server side of the client's IAudioTrack
- class TrackHandle : public android::BnAudioTrack {
+ class TrackHandle : public android::media::BnAudioTrack {
public:
explicit TrackHandle(const sp<PlaybackThread::Track>& track);
virtual ~TrackHandle();
- virtual sp<IMemory> getCblk() const;
- virtual status_t start();
- virtual void stop();
- virtual void flush();
- virtual void pause();
- virtual status_t attachAuxEffect(int effectId);
- virtual status_t setParameters(const String8& keyValuePairs);
- virtual status_t selectPresentation(int presentationId, int programId);
- virtual media::VolumeShaper::Status applyVolumeShaper(
- const sp<media::VolumeShaper::Configuration>& configuration,
- const sp<media::VolumeShaper::Operation>& operation) override;
- virtual sp<media::VolumeShaper::State> getVolumeShaperState(int id) override;
- virtual status_t getTimestamp(AudioTimestamp& timestamp);
- virtual void signal(); // signal playback thread for a change in control block
- status_t getDualMonoMode(audio_dual_mono_mode_t* mode) override;
- status_t setDualMonoMode(audio_dual_mono_mode_t mode) override;
- status_t getAudioDescriptionMixLevel(float* leveldB) override;
- status_t setAudioDescriptionMixLevel(float leveldB) override;
- status_t getPlaybackRateParameters(audio_playback_rate_t* playbackRate) override;
- status_t setPlaybackRateParameters(
- const audio_playback_rate_t& playbackRate) override;
- virtual status_t onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
+ binder::Status getCblk(std::optional<media::SharedFileRegion>* _aidl_return) override;
+ binder::Status start(int32_t* _aidl_return) override;
+ binder::Status stop() override;
+ binder::Status flush() override;
+ binder::Status pause() override;
+ binder::Status attachAuxEffect(int32_t effectId, int32_t* _aidl_return) override;
+ binder::Status setParameters(const std::string& keyValuePairs,
+ int32_t* _aidl_return) override;
+ binder::Status selectPresentation(int32_t presentationId, int32_t programId,
+ int32_t* _aidl_return) override;
+ binder::Status getTimestamp(media::AudioTimestampInternal* timestamp,
+ int32_t* _aidl_return) override;
+ binder::Status signal() override;
+ binder::Status applyVolumeShaper(const media::VolumeShaperConfiguration& configuration,
+ const media::VolumeShaperOperation& operation,
+ int32_t* _aidl_return) override;
+ binder::Status getVolumeShaperState(
+ int32_t id,
+ std::optional<media::VolumeShaperState>* _aidl_return) override;
+ binder::Status getDualMonoMode(media::AudioDualMonoMode* _aidl_return) override;
+ binder::Status setDualMonoMode(media::AudioDualMonoMode mode) override;
+ binder::Status getAudioDescriptionMixLevel(float* _aidl_return) override;
+ binder::Status setAudioDescriptionMixLevel(float leveldB) override;
+ binder::Status getPlaybackRateParameters(
+ media::AudioPlaybackRate* _aidl_return) override;
+ binder::Status setPlaybackRateParameters(
+ const media::AudioPlaybackRate& playbackRate) override;
private:
const sp<PlaybackThread::Track> mTrack;
@@ -669,10 +673,12 @@
int /*audio_session_t*/ triggerSession);
virtual binder::Status stop();
virtual binder::Status getActiveMicrophones(
- std::vector<media::MicrophoneInfo>* activeMicrophones);
+ std::vector<media::MicrophoneInfoData>* activeMicrophones);
virtual binder::Status setPreferredMicrophoneDirection(
int /*audio_microphone_direction_t*/ direction);
virtual binder::Status setPreferredMicrophoneFieldDimension(float zoom);
+ virtual binder::Status shareAudioHistory(const std::string& sharedAudioPackageName,
+ int64_t sharedAudioStartMs);
private:
const sp<RecordThread::RecordTrack> mRecordTrack;
@@ -692,6 +698,7 @@
virtual status_t createMmapBuffer(int32_t minSizeFrames,
struct audio_mmap_buffer_info *info);
virtual status_t getMmapPosition(struct audio_mmap_position *position);
+ virtual status_t getExternalPosition(uint64_t *position, int64_t *timeNanos);
virtual status_t start(const AudioClient& client,
const audio_attributes_t *attr,
audio_port_handle_t *handle);
@@ -714,7 +721,7 @@
audio_io_handle_t *input,
audio_config_t *config,
audio_devices_t device,
- const String8& address,
+ const char* address,
audio_source_t source,
audio_input_flags_t flags,
audio_devices_t outputDevice,
@@ -766,6 +773,8 @@
sp<ThreadBase> getEffectThread_l(audio_session_t sessionId, int effectId);
+ ThreadBase *hapticPlaybackThread_l() const;
+
void removeClient_l(pid_t pid);
void removeNotificationClient(pid_t pid);
@@ -792,7 +801,7 @@
std::vector< sp<EffectModule> > purgeStaleEffects_l();
- void broacastParametersToRecordThreads_l(const String8& keyValuePairs);
+ void broadcastParametersToRecordThreads_l(const String8& keyValuePairs);
void updateOutDevicesForRecordThreads_l(const DeviceDescriptorBaseVector& devices);
void forwardParametersToDownstreamPatches_l(
audio_io_handle_t upStream, const String8& keyValuePairs,
@@ -972,7 +981,12 @@
SimpleLog mAppSetParameterLog;
SimpleLog mSystemSetParameterLog;
+ std::vector<media::AudioVibratorInfo> mAudioVibratorInfos;
+
static inline constexpr const char *mMetricsId = AMEDIAMETRICS_KEY_AUDIO_FLINGER;
+
+ // Keep in sync with java definition in media/java/android/media/AudioRecord.java
+ static constexpr int32_t kMaxSharedAudioHistoryMs = 5000;
};
#undef INCLUDING_FROM_AUDIOFLINGER_H
diff --git a/services/audioflinger/AudioHwDevice.cpp b/services/audioflinger/AudioHwDevice.cpp
index dda164c..16b25f6 100644
--- a/services/audioflinger/AudioHwDevice.cpp
+++ b/services/audioflinger/AudioHwDevice.cpp
@@ -98,5 +98,9 @@
return mHwDevice->supportsAudioPatches(&result) == OK ? result : false;
}
+status_t AudioHwDevice::getAudioPort(struct audio_port_v7 *port) const {
+ return mHwDevice->getAudioPort(port);
+}
+
}; // namespace android
diff --git a/services/audioflinger/AudioHwDevice.h b/services/audioflinger/AudioHwDevice.h
index 6709d17..fc2c693 100644
--- a/services/audioflinger/AudioHwDevice.h
+++ b/services/audioflinger/AudioHwDevice.h
@@ -83,6 +83,8 @@
bool supportsAudioPatches() const;
+ status_t getAudioPort(struct audio_port_v7 *port) const;
+
private:
const audio_module_handle_t mHandle;
const char * const mModuleName;
diff --git a/services/audioflinger/DeviceEffectManager.cpp b/services/audioflinger/DeviceEffectManager.cpp
index 5ff7215..cecd52b 100644
--- a/services/audioflinger/DeviceEffectManager.cpp
+++ b/services/audioflinger/DeviceEffectManager.cpp
@@ -30,6 +30,8 @@
namespace android {
+using media::IEffectClient;
+
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",
@@ -115,10 +117,19 @@
status_t AudioFlinger::DeviceEffectManager::checkEffectCompatibility(
const effect_descriptor_t *desc) {
+ sp<EffectsFactoryHalInterface> effectsFactory = mAudioFlinger.getEffectsFactory();
+ if (effectsFactory == nullptr) {
+ return BAD_VALUE;
+ }
- 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);
+ static const float sMinDeviceEffectHalVersion = 6.0;
+ float halVersion = effectsFactory->getHalVersion();
+
+ if (((desc->flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_PRE_PROC
+ && (desc->flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_POST_PROC)
+ || halVersion < sMinDeviceEffectHalVersion) {
+ ALOGW("%s() non pre/post processing device effect %s or incompatible API version %f",
+ __func__, desc->name, halVersion);
return BAD_VALUE;
}
diff --git a/services/audioflinger/DeviceEffectManager.h b/services/audioflinger/DeviceEffectManager.h
index 81e6065..a05f5fe 100644
--- a/services/audioflinger/DeviceEffectManager.h
+++ b/services/audioflinger/DeviceEffectManager.h
@@ -33,7 +33,7 @@
sp<EffectHandle> createEffect_l(effect_descriptor_t *descriptor,
const AudioDeviceTypeAddr& device,
const sp<AudioFlinger::Client>& client,
- const sp<IEffectClient>& effectClient,
+ const sp<media::IEffectClient>& effectClient,
const std::map<audio_patch_handle_t, PatchPanel::Patch>& patches,
int *enabled,
status_t *status,
@@ -165,6 +165,7 @@
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; }
+ audio_channel_mask_t hapticChannelMask() const override { return AUDIO_CHANNEL_NONE; }
size_t frameCount() const override { return 0; }
uint32_t latency() const override { return 0; }
@@ -182,7 +183,7 @@
void checkSuspendOnEffectEnabled(const sp<EffectBase>& effect __unused,
bool enabled __unused, bool threadLocked __unused) override {}
void resetVolume() override {}
- uint32_t strategy() const override { return 0; }
+ product_strategy_t strategy() const override { return static_cast<product_strategy_t>(0); }
int32_t activeTrackCnt() const override { return 0; }
void onEffectEnable(const sp<EffectBase>& effect __unused) override {}
void onEffectDisable(const sp<EffectBase>& effect __unused) override {}
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index eebaa7e..d75b13b 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -25,6 +25,7 @@
#include <utils/Log.h>
#include <system/audio_effects/effect_aec.h>
#include <system/audio_effects/effect_dynamicsprocessing.h>
+#include <system/audio_effects/effect_hapticgenerator.h>
#include <system/audio_effects/effect_ns.h>
#include <system/audio_effects/effect_visualizer.h>
#include <audio_utils/channels.h>
@@ -33,6 +34,7 @@
#include <media/AudioContainers.h>
#include <media/AudioEffect.h>
#include <media/AudioDeviceTypeAddr.h>
+#include <media/ShmemCompat.h>
#include <media/audiohal/EffectHalInterface.h>
#include <media/audiohal/EffectsFactoryHalInterface.h>
#include <mediautils/ServiceUtilities.h>
@@ -58,6 +60,28 @@
namespace android {
+using aidl_utils::statusTFromBinderStatus;
+using binder::Status;
+
+namespace {
+
+// Append a POD value into a vector of bytes.
+template<typename T>
+void appendToBuffer(const T& value, std::vector<uint8_t>* buffer) {
+ const uint8_t* ar(reinterpret_cast<const uint8_t*>(&value));
+ buffer->insert(buffer->end(), ar, ar + sizeof(T));
+}
+
+// Write a POD value into a vector of bytes (clears the previous buffer
+// content).
+template<typename T>
+void writeToBuffer(const T& value, std::vector<uint8_t>* buffer) {
+ buffer->clear();
+ appendToBuffer(value, buffer);
+}
+
+} // namespace
+
// ----------------------------------------------------------------------------
// EffectBase implementation
// ----------------------------------------------------------------------------
@@ -214,7 +238,7 @@
bool doEnable = false;
bool enabled = false;
audio_io_handle_t io = AUDIO_IO_HANDLE_NONE;
- uint32_t strategy = PRODUCT_STRATEGY_NONE;
+ product_strategy_t strategy = PRODUCT_STRATEGY_NONE;
{
Mutex::Autolock _l(mLock);
@@ -237,6 +261,12 @@
}
registered = mPolicyRegistered;
enabled = mPolicyEnabled;
+ // The simultaneous release of two EffectHandles with the same EffectModule
+ // may cause us to call this method at the same time.
+ // This may deadlock under some circumstances (b/180941720). Avoid this.
+ if (!doRegister && !(registered && doEnable)) {
+ return NO_ERROR;
+ }
mPolicyLock.lock();
}
ALOGV("%s name %s id %d session %d doRegister %d registered %d doEnable %d enabled %d",
@@ -868,6 +898,11 @@
}
#endif
}
+ if (isHapticGenerator()) {
+ audio_channel_mask_t hapticChannelMask = mCallback->hapticChannelMask();
+ mConfig.inputCfg.channels |= hapticChannelMask;
+ mConfig.outputCfg.channels |= hapticChannelMask;
+ }
mInChannelCountRequested =
audio_channel_count_from_out_mask(mConfig.inputCfg.channels);
mOutChannelCountRequested =
@@ -1149,11 +1184,10 @@
return remainder == 0 ? 0 : divisor - remainder;
}
-status_t AudioFlinger::EffectModule::command(uint32_t cmdCode,
- uint32_t cmdSize,
- void *pCmdData,
- uint32_t *replySize,
- void *pReplyData)
+status_t AudioFlinger::EffectModule::command(int32_t cmdCode,
+ const std::vector<uint8_t>& cmdData,
+ int32_t maxReplySize,
+ std::vector<uint8_t>* reply)
{
Mutex::Autolock _l(mLock);
ALOGVV("command(), cmdCode: %d, mEffectInterface: %p", cmdCode, mEffectInterface.get());
@@ -1164,63 +1198,68 @@
if (mStatus != NO_ERROR) {
return mStatus;
}
+ if (maxReplySize < 0 || maxReplySize > EFFECT_PARAM_SIZE_MAX) {
+ return -EINVAL;
+ }
+ size_t cmdSize = cmdData.size();
+ const effect_param_t* param = cmdSize >= sizeof(effect_param_t)
+ ? reinterpret_cast<const effect_param_t*>(cmdData.data())
+ : nullptr;
if (cmdCode == EFFECT_CMD_GET_PARAM &&
- (sizeof(effect_param_t) > cmdSize ||
- ((effect_param_t *)pCmdData)->psize > cmdSize
- - sizeof(effect_param_t))) {
+ (param == nullptr || param->psize > cmdSize - sizeof(effect_param_t))) {
android_errorWriteLog(0x534e4554, "32438594");
android_errorWriteLog(0x534e4554, "33003822");
return -EINVAL;
}
if (cmdCode == EFFECT_CMD_GET_PARAM &&
- (*replySize < sizeof(effect_param_t) ||
- ((effect_param_t *)pCmdData)->psize > *replySize - sizeof(effect_param_t))) {
+ (maxReplySize < sizeof(effect_param_t) ||
+ param->psize > maxReplySize - sizeof(effect_param_t))) {
android_errorWriteLog(0x534e4554, "29251553");
return -EINVAL;
}
if (cmdCode == EFFECT_CMD_GET_PARAM &&
- (sizeof(effect_param_t) > *replySize
- || ((effect_param_t *)pCmdData)->psize > *replySize
- - sizeof(effect_param_t)
- || ((effect_param_t *)pCmdData)->vsize > *replySize
- - sizeof(effect_param_t)
- - ((effect_param_t *)pCmdData)->psize
- || roundUpDelta(((effect_param_t *)pCmdData)->psize, (uint32_t)sizeof(int)) >
- *replySize
- - sizeof(effect_param_t)
- - ((effect_param_t *)pCmdData)->psize
- - ((effect_param_t *)pCmdData)->vsize)) {
+ (sizeof(effect_param_t) > maxReplySize
+ || param->psize > maxReplySize - sizeof(effect_param_t)
+ || param->vsize > maxReplySize - sizeof(effect_param_t)
+ - param->psize
+ || roundUpDelta(param->psize, (uint32_t) sizeof(int)) >
+ maxReplySize
+ - sizeof(effect_param_t)
+ - param->psize
+ - param->vsize)) {
ALOGV("\tLVM_ERROR : EFFECT_CMD_GET_PARAM: reply size inconsistent");
android_errorWriteLog(0x534e4554, "32705438");
return -EINVAL;
}
if ((cmdCode == EFFECT_CMD_SET_PARAM
- || cmdCode == EFFECT_CMD_SET_PARAM_DEFERRED) && // DEFERRED not generally used
- (sizeof(effect_param_t) > cmdSize
- || ((effect_param_t *)pCmdData)->psize > cmdSize
- - sizeof(effect_param_t)
- || ((effect_param_t *)pCmdData)->vsize > cmdSize
- - sizeof(effect_param_t)
- - ((effect_param_t *)pCmdData)->psize
- || roundUpDelta(((effect_param_t *)pCmdData)->psize, (uint32_t)sizeof(int)) >
- cmdSize
- - sizeof(effect_param_t)
- - ((effect_param_t *)pCmdData)->psize
- - ((effect_param_t *)pCmdData)->vsize)) {
+ || cmdCode == EFFECT_CMD_SET_PARAM_DEFERRED)
+ && // DEFERRED not generally used
+ (param == nullptr
+ || param->psize > cmdSize - sizeof(effect_param_t)
+ || param->vsize > cmdSize - sizeof(effect_param_t)
+ - param->psize
+ || roundUpDelta(param->psize,
+ (uint32_t) sizeof(int)) >
+ cmdSize
+ - sizeof(effect_param_t)
+ - param->psize
+ - param->vsize)) {
android_errorWriteLog(0x534e4554, "30204301");
return -EINVAL;
}
+ uint32_t replySize = maxReplySize;
+ reply->resize(replySize);
status_t status = mEffectInterface->command(cmdCode,
cmdSize,
- pCmdData,
- replySize,
- pReplyData);
+ const_cast<uint8_t*>(cmdData.data()),
+ &replySize,
+ reply->data());
+ reply->resize(status == NO_ERROR ? replySize : 0);
if (cmdCode != EFFECT_CMD_GET_PARAM && status == NO_ERROR) {
- uint32_t size = (replySize == NULL) ? 0 : *replySize;
for (size_t i = 1; i < mHandles.size(); i++) {
EffectHandle *h = mHandles[i];
if (h != NULL && !h->disconnected()) {
- h->commandExecuted(cmdCode, cmdSize, pCmdData, size, pReplyData);
+ h->commandExecuted(cmdCode, cmdData, *reply);
}
}
}
@@ -1511,6 +1550,69 @@
return mOffloaded;
}
+/*static*/
+bool AudioFlinger::EffectModule::isHapticGenerator(const effect_uuid_t *type) {
+ return memcmp(type, FX_IID_HAPTICGENERATOR, sizeof(effect_uuid_t)) == 0;
+}
+
+bool AudioFlinger::EffectModule::isHapticGenerator() const {
+ return isHapticGenerator(&mDescriptor.type);
+}
+
+status_t AudioFlinger::EffectModule::setHapticIntensity(int id, int intensity)
+{
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ if (!isHapticGenerator()) {
+ ALOGW("Should not set haptic intensity for effects that are not HapticGenerator");
+ return INVALID_OPERATION;
+ }
+
+ std::vector<uint8_t> request(sizeof(effect_param_t) + 3 * sizeof(uint32_t));
+ effect_param_t *param = (effect_param_t*) request.data();
+ param->psize = sizeof(int32_t);
+ param->vsize = sizeof(int32_t) * 2;
+ *(int32_t*)param->data = HG_PARAM_HAPTIC_INTENSITY;
+ *((int32_t*)param->data + 1) = id;
+ *((int32_t*)param->data + 2) = intensity;
+ std::vector<uint8_t> response;
+ status_t status = command(EFFECT_CMD_SET_PARAM, request, sizeof(int32_t), &response);
+ if (status == NO_ERROR) {
+ LOG_ALWAYS_FATAL_IF(response.size() != 4);
+ status = *reinterpret_cast<const status_t*>(response.data());
+ }
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::setVibratorInfo(const media::AudioVibratorInfo* vibratorInfo)
+{
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ if (!isHapticGenerator()) {
+ ALOGW("Should not set vibrator info for effects that are not HapticGenerator");
+ return INVALID_OPERATION;
+ }
+
+ std::vector<uint8_t> request(
+ sizeof(effect_param_t) + sizeof(int32_t) + 2 * sizeof(float));
+ effect_param_t *param = (effect_param_t*) request.data();
+ param->psize = sizeof(int32_t);
+ param->vsize = 2 * sizeof(float);
+ *(int32_t*)param->data = HG_PARAM_VIBRATOR_INFO;
+ float* vibratorInfoPtr = reinterpret_cast<float*>(param->data + sizeof(int32_t));
+ vibratorInfoPtr[0] = vibratorInfo->resonantFrequency;
+ vibratorInfoPtr[1] = vibratorInfo->qFactor;
+ std::vector<uint8_t> response;
+ status_t status = command(EFFECT_CMD_SET_PARAM, request, sizeof(int32_t), &response);
+ if (status == NO_ERROR) {
+ LOG_ALWAYS_FATAL_IF(response.size() != sizeof(status_t));
+ status = *reinterpret_cast<const status_t*>(response.data());
+ }
+ return status;
+}
+
static std::string dumpInOutBuffer(bool isInput, const sp<EffectBufferHalInterface> &buffer) {
std::stringstream ss;
@@ -1589,9 +1691,9 @@
#define LOG_TAG "AudioFlinger::EffectHandle"
AudioFlinger::EffectHandle::EffectHandle(const sp<EffectBase>& effect,
- const sp<AudioFlinger::Client>& client,
- const sp<IEffectClient>& effectClient,
- int32_t priority)
+ const sp<AudioFlinger::Client>& client,
+ const sp<media::IEffectClient>& effectClient,
+ int32_t priority)
: BnEffect(),
mEffect(effect), mEffectClient(effectClient), mClient(client), mCblk(NULL),
mPriority(priority), mHasControl(false), mEnabled(false), mDisconnected(false)
@@ -1625,20 +1727,24 @@
return mClient == 0 || mCblkMemory != 0 ? OK : NO_MEMORY;
}
-status_t AudioFlinger::EffectHandle::enable()
+#define RETURN(code) \
+ *_aidl_return = (code); \
+ return Status::ok();
+
+Status AudioFlinger::EffectHandle::enable(int32_t* _aidl_return)
{
AutoMutex _l(mLock);
ALOGV("enable %p", this);
sp<EffectBase> effect = mEffect.promote();
if (effect == 0 || mDisconnected) {
- return DEAD_OBJECT;
+ RETURN(DEAD_OBJECT);
}
if (!mHasControl) {
- return INVALID_OPERATION;
+ RETURN(INVALID_OPERATION);
}
if (mEnabled) {
- return NO_ERROR;
+ RETURN(NO_ERROR);
}
mEnabled = true;
@@ -1646,54 +1752,55 @@
status_t status = effect->updatePolicyState();
if (status != NO_ERROR) {
mEnabled = false;
- return status;
+ RETURN(status);
}
effect->checkSuspendOnEffectEnabled(true, false /*threadLocked*/);
// checkSuspendOnEffectEnabled() can suspend this same effect when enabled
if (effect->suspended()) {
- return NO_ERROR;
+ RETURN(NO_ERROR);
}
status = effect->setEnabled(true, true /*fromHandle*/);
if (status != NO_ERROR) {
mEnabled = false;
}
- return status;
+ RETURN(status);
}
-status_t AudioFlinger::EffectHandle::disable()
+Status AudioFlinger::EffectHandle::disable(int32_t* _aidl_return)
{
ALOGV("disable %p", this);
AutoMutex _l(mLock);
sp<EffectBase> effect = mEffect.promote();
if (effect == 0 || mDisconnected) {
- return DEAD_OBJECT;
+ RETURN(DEAD_OBJECT);
}
if (!mHasControl) {
- return INVALID_OPERATION;
+ RETURN(INVALID_OPERATION);
}
if (!mEnabled) {
- return NO_ERROR;
+ RETURN(NO_ERROR);
}
mEnabled = false;
effect->updatePolicyState();
if (effect->suspended()) {
- return NO_ERROR;
+ RETURN(NO_ERROR);
}
status_t status = effect->setEnabled(false, true /*fromHandle*/);
- return status;
+ RETURN(status);
}
-void AudioFlinger::EffectHandle::disconnect()
+Status AudioFlinger::EffectHandle::disconnect()
{
ALOGV("%s %p", __FUNCTION__, this);
disconnect(true);
+ return Status::ok();
}
void AudioFlinger::EffectHandle::disconnect(bool unpinIfLast)
@@ -1730,11 +1837,16 @@
}
}
-status_t AudioFlinger::EffectHandle::command(uint32_t cmdCode,
- uint32_t cmdSize,
- void *pCmdData,
- uint32_t *replySize,
- void *pReplyData)
+Status AudioFlinger::EffectHandle::getCblk(media::SharedFileRegion* _aidl_return) {
+ LOG_ALWAYS_FATAL_IF(!convertIMemoryToSharedFileRegion(mCblkMemory, _aidl_return));
+ return Status::ok();
+}
+
+Status AudioFlinger::EffectHandle::command(int32_t cmdCode,
+ const std::vector<uint8_t>& cmdData,
+ int32_t maxResponseSize,
+ std::vector<uint8_t>* response,
+ int32_t* _aidl_return)
{
ALOGVV("command(), cmdCode: %d, mHasControl: %d, mEffect: %p",
cmdCode, mHasControl, mEffect.unsafe_get());
@@ -1754,49 +1866,46 @@
break;
}
android_errorWriteLog(0x534e4554, "62019992");
- return BAD_VALUE;
+ RETURN(BAD_VALUE);
}
if (cmdCode == EFFECT_CMD_ENABLE) {
- if (*replySize < sizeof(int)) {
+ if (maxResponseSize < sizeof(int)) {
android_errorWriteLog(0x534e4554, "32095713");
- return BAD_VALUE;
+ RETURN(BAD_VALUE);
}
- *(int *)pReplyData = NO_ERROR;
- *replySize = sizeof(int);
- return enable();
+ writeToBuffer(NO_ERROR, response);
+ return enable(_aidl_return);
} else if (cmdCode == EFFECT_CMD_DISABLE) {
- if (*replySize < sizeof(int)) {
+ if (maxResponseSize < sizeof(int)) {
android_errorWriteLog(0x534e4554, "32095713");
- return BAD_VALUE;
+ RETURN(BAD_VALUE);
}
- *(int *)pReplyData = NO_ERROR;
- *replySize = sizeof(int);
- return disable();
+ writeToBuffer(NO_ERROR, response);
+ return disable(_aidl_return);
}
AutoMutex _l(mLock);
sp<EffectBase> effect = mEffect.promote();
if (effect == 0 || mDisconnected) {
- return DEAD_OBJECT;
+ RETURN(DEAD_OBJECT);
}
// only get parameter command is permitted for applications not controlling the effect
if (!mHasControl && cmdCode != EFFECT_CMD_GET_PARAM) {
- return INVALID_OPERATION;
+ 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;
+ RETURN(INVALID_OPERATION);
}
- if (*replySize < sizeof(int)) {
+ if (maxResponseSize < sizeof(int)) {
android_errorWriteLog(0x534e4554, "32095713");
- return BAD_VALUE;
+ RETURN(BAD_VALUE);
}
- *(int *)pReplyData = NO_ERROR;
- *replySize = sizeof(int);
+ writeToBuffer(NO_ERROR, response);
// No need to trylock() here as this function is executed in the binder thread serving a
// particular client process: no risk to block the whole media server process or mixer
@@ -1809,10 +1918,10 @@
serverIndex > EFFECT_PARAM_BUFFER_SIZE) {
mCblk->serverIndex = 0;
mCblk->clientIndex = 0;
- return BAD_VALUE;
+ RETURN(BAD_VALUE);
}
status_t status = NO_ERROR;
- effect_param_t *param = NULL;
+ std::vector<uint8_t> param;
for (uint32_t index = serverIndex; index < clientIndex;) {
int *p = (int *)(mBuffer + index);
const int size = *p++;
@@ -1824,23 +1933,16 @@
break;
}
- // copy to local memory in case of client corruption b/32220769
- auto *newParam = (effect_param_t *)realloc(param, size);
- if (newParam == NULL) {
- ALOGW("command(): out of memory");
- status = NO_MEMORY;
- break;
- }
- param = newParam;
- memcpy(param, p, size);
+ std::copy(reinterpret_cast<const uint8_t*>(p),
+ reinterpret_cast<const uint8_t*>(p) + size,
+ std::back_inserter(param));
- int reply = 0;
- uint32_t rsize = sizeof(reply);
+ std::vector<uint8_t> replyBuffer;
status_t ret = effect->command(EFFECT_CMD_SET_PARAM,
- size,
param,
- &rsize,
- &reply);
+ sizeof(int),
+ &replyBuffer);
+ int reply = *reinterpret_cast<const int*>(replyBuffer.data());
// verify shared memory: server index shouldn't change; client index can't go back.
if (serverIndex != mCblk->serverIndex
@@ -1853,21 +1955,24 @@
// stop at first error encountered
if (ret != NO_ERROR) {
status = ret;
- *(int *)pReplyData = reply;
+ writeToBuffer(reply, response);
break;
} else if (reply != NO_ERROR) {
- *(int *)pReplyData = reply;
+ writeToBuffer(reply, response);
break;
}
index += size;
}
- free(param);
mCblk->serverIndex = 0;
mCblk->clientIndex = 0;
- return status;
+ RETURN(status);
}
- return effect->command(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
+ status_t status = effect->command(cmdCode,
+ cmdData,
+ maxResponseSize,
+ response);
+ RETURN(status);
}
void AudioFlinger::EffectHandle::setControl(bool hasControl, bool signal, bool enabled)
@@ -1883,13 +1988,11 @@
}
void AudioFlinger::EffectHandle::commandExecuted(uint32_t cmdCode,
- uint32_t cmdSize,
- void *pCmdData,
- uint32_t replySize,
- void *pReplyData)
+ const std::vector<uint8_t>& cmdData,
+ const std::vector<uint8_t>& replyData)
{
if (mEffectClient != 0) {
- mEffectClient->commandExecuted(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
+ mEffectClient->commandExecuted(cmdCode, cmdData, replyData);
}
}
@@ -1902,13 +2005,6 @@
}
}
-status_t AudioFlinger::EffectHandle::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- return BnEffect::onTransact(code, data, reply, flags);
-}
-
-
void AudioFlinger::EffectHandle::dumpToBuffer(char* buffer, size_t size)
{
bool locked = mCblk != NULL && AudioFlinger::dumpTryLock(mCblk->lock);
@@ -2374,6 +2470,25 @@
}
}
+// containsHapticGeneratingEffect_l must be called with ThreadBase::mLock or EffectChain::mLock held
+bool AudioFlinger::EffectChain::containsHapticGeneratingEffect_l()
+{
+ for (size_t i = 0; i < mEffects.size(); ++i) {
+ if (mEffects[i]->isHapticGenerator()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void AudioFlinger::EffectChain::setHapticIntensity_l(int id, int intensity)
+{
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i < mEffects.size(); ++i) {
+ mEffects[i]->setHapticIntensity(id, intensity);
+ }
+}
+
void AudioFlinger::EffectChain::syncHalEffectsState()
{
Mutex::Autolock _l(mLock);
@@ -2704,11 +2819,7 @@
const effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t deviceId,
sp<EffectHalInterface> *effect) {
status_t status = NO_INIT;
- sp<AudioFlinger> af = mAudioFlinger.promote();
- if (af == nullptr) {
- return status;
- }
- sp<EffectsFactoryHalInterface> effectsFactory = af->getEffectsFactory();
+ sp<EffectsFactoryHalInterface> effectsFactory = mAudioFlinger.getEffectsFactory();
if (effectsFactory != 0) {
status = effectsFactory->createEffect(pEffectUuid, sessionId, io(), deviceId, effect);
}
@@ -2717,25 +2828,19 @@
bool AudioFlinger::EffectChain::EffectCallback::updateOrphanEffectChains(
const sp<AudioFlinger::EffectBase>& effect) {
- sp<AudioFlinger> af = mAudioFlinger.promote();
- if (af == nullptr) {
- return false;
- }
// in EffectChain context, an EffectBase is always from an EffectModule so static cast is safe
- return af->updateOrphanEffectChains(effect->asEffectModule());
+ return mAudioFlinger.updateOrphanEffectChains(effect->asEffectModule());
}
status_t AudioFlinger::EffectChain::EffectCallback::allocateHalBuffer(
size_t size, sp<EffectBufferHalInterface>* buffer) {
- sp<AudioFlinger> af = mAudioFlinger.promote();
- LOG_ALWAYS_FATAL_IF(af == nullptr, "allocateHalBuffer() could not retrieved audio flinger");
- return af->mEffectsFactoryHal->allocateBuffer(size, buffer);
+ return mAudioFlinger.mEffectsFactoryHal->allocateBuffer(size, buffer);
}
status_t AudioFlinger::EffectChain::EffectCallback::addEffectToHal(
sp<EffectHalInterface> effect) {
status_t result = NO_INIT;
- sp<ThreadBase> t = mThread.promote();
+ sp<ThreadBase> t = thread().promote();
if (t == nullptr) {
return result;
}
@@ -2751,7 +2856,7 @@
status_t AudioFlinger::EffectChain::EffectCallback::removeEffectFromHal(
sp<EffectHalInterface> effect) {
status_t result = NO_INIT;
- sp<ThreadBase> t = mThread.promote();
+ sp<ThreadBase> t = thread().promote();
if (t == nullptr) {
return result;
}
@@ -2765,7 +2870,7 @@
}
audio_io_handle_t AudioFlinger::EffectChain::EffectCallback::io() const {
- sp<ThreadBase> t = mThread.promote();
+ sp<ThreadBase> t = thread().promote();
if (t == nullptr) {
return AUDIO_IO_HANDLE_NONE;
}
@@ -2773,7 +2878,7 @@
}
bool AudioFlinger::EffectChain::EffectCallback::isOutput() const {
- sp<ThreadBase> t = mThread.promote();
+ sp<ThreadBase> t = thread().promote();
if (t == nullptr) {
return true;
}
@@ -2781,7 +2886,7 @@
}
bool AudioFlinger::EffectChain::EffectCallback::isOffload() const {
- sp<ThreadBase> t = mThread.promote();
+ sp<ThreadBase> t = thread().promote();
if (t == nullptr) {
return false;
}
@@ -2789,7 +2894,7 @@
}
bool AudioFlinger::EffectChain::EffectCallback::isOffloadOrDirect() const {
- sp<ThreadBase> t = mThread.promote();
+ sp<ThreadBase> t = thread().promote();
if (t == nullptr) {
return false;
}
@@ -2797,7 +2902,7 @@
}
bool AudioFlinger::EffectChain::EffectCallback::isOffloadOrMmap() const {
- sp<ThreadBase> t = mThread.promote();
+ sp<ThreadBase> t = thread().promote();
if (t == nullptr) {
return false;
}
@@ -2805,7 +2910,7 @@
}
uint32_t AudioFlinger::EffectChain::EffectCallback::sampleRate() const {
- sp<ThreadBase> t = mThread.promote();
+ sp<ThreadBase> t = thread().promote();
if (t == nullptr) {
return 0;
}
@@ -2813,7 +2918,7 @@
}
audio_channel_mask_t AudioFlinger::EffectChain::EffectCallback::channelMask() const {
- sp<ThreadBase> t = mThread.promote();
+ sp<ThreadBase> t = thread().promote();
if (t == nullptr) {
return AUDIO_CHANNEL_NONE;
}
@@ -2821,15 +2926,23 @@
}
uint32_t AudioFlinger::EffectChain::EffectCallback::channelCount() const {
- sp<ThreadBase> t = mThread.promote();
+ sp<ThreadBase> t = thread().promote();
if (t == nullptr) {
return 0;
}
return t->channelCount();
}
+audio_channel_mask_t AudioFlinger::EffectChain::EffectCallback::hapticChannelMask() const {
+ sp<ThreadBase> t = thread().promote();
+ if (t == nullptr) {
+ return AUDIO_CHANNEL_NONE;
+ }
+ return t->hapticChannelMask();
+}
+
size_t AudioFlinger::EffectChain::EffectCallback::frameCount() const {
- sp<ThreadBase> t = mThread.promote();
+ sp<ThreadBase> t = thread().promote();
if (t == nullptr) {
return 0;
}
@@ -2837,7 +2950,7 @@
}
uint32_t AudioFlinger::EffectChain::EffectCallback::latency() const {
- sp<ThreadBase> t = mThread.promote();
+ sp<ThreadBase> t = thread().promote();
if (t == nullptr) {
return 0;
}
@@ -2845,7 +2958,7 @@
}
void AudioFlinger::EffectChain::EffectCallback::setVolumeForOutput(float left, float right) const {
- sp<ThreadBase> t = mThread.promote();
+ sp<ThreadBase> t = thread().promote();
if (t == nullptr) {
return;
}
@@ -2854,13 +2967,13 @@
void AudioFlinger::EffectChain::EffectCallback::checkSuspendOnEffectEnabled(
const sp<EffectBase>& effect, bool enabled, bool threadLocked) {
- sp<ThreadBase> t = mThread.promote();
+ sp<ThreadBase> t = thread().promote();
if (t == nullptr) {
return;
}
t->checkSuspendOnEffectEnabled(enabled, effect->sessionId(), threadLocked);
- sp<EffectChain> c = mChain.promote();
+ sp<EffectChain> c = chain().promote();
if (c == nullptr) {
return;
}
@@ -2869,7 +2982,7 @@
}
void AudioFlinger::EffectChain::EffectCallback::onEffectEnable(const sp<EffectBase>& effect) {
- sp<ThreadBase> t = mThread.promote();
+ sp<ThreadBase> t = thread().promote();
if (t == nullptr) {
return;
}
@@ -2880,7 +2993,7 @@
void AudioFlinger::EffectChain::EffectCallback::onEffectDisable(const sp<EffectBase>& effect) {
checkSuspendOnEffectEnabled(effect, false, false /*threadLocked*/);
- sp<ThreadBase> t = mThread.promote();
+ sp<ThreadBase> t = thread().promote();
if (t == nullptr) {
return;
}
@@ -2889,7 +3002,7 @@
bool AudioFlinger::EffectChain::EffectCallback::disconnectEffectHandle(EffectHandle *handle,
bool unpinIfLast) {
- sp<ThreadBase> t = mThread.promote();
+ sp<ThreadBase> t = thread().promote();
if (t == nullptr) {
return false;
}
@@ -2898,7 +3011,7 @@
}
void AudioFlinger::EffectChain::EffectCallback::resetVolume() {
- sp<EffectChain> c = mChain.promote();
+ sp<EffectChain> c = chain().promote();
if (c == nullptr) {
return;
}
@@ -2906,8 +3019,8 @@
}
-uint32_t AudioFlinger::EffectChain::EffectCallback::strategy() const {
- sp<EffectChain> c = mChain.promote();
+product_strategy_t AudioFlinger::EffectChain::EffectCallback::strategy() const {
+ sp<EffectChain> c = chain().promote();
if (c == nullptr) {
return PRODUCT_STRATEGY_NONE;
}
@@ -2915,7 +3028,7 @@
}
int32_t AudioFlinger::EffectChain::EffectCallback::activeTrackCnt() const {
- sp<EffectChain> c = mChain.promote();
+ sp<EffectChain> c = chain().promote();
if (c == nullptr) {
return 0;
}
@@ -2932,10 +3045,14 @@
Mutex::Autolock _l(mProxyLock);
if (status == NO_ERROR) {
for (auto& handle : mEffectHandles) {
+ Status bs;
if (enabled) {
- status = handle.second->enable();
+ bs = handle.second->enable(&status);
} else {
- status = handle.second->disable();
+ bs = handle.second->disable(&status);
+ }
+ if (!bs.isOk()) {
+ status = statusTFromBinderStatus(bs);
}
}
}
@@ -3043,10 +3160,14 @@
status = BAD_VALUE;
}
if (status == NO_ERROR || status == ALREADY_EXISTS) {
+ Status bs;
if (isEnabled()) {
- (*handle)->enable();
+ bs = (*handle)->enable(&status);
} else {
- (*handle)->disable();
+ bs = (*handle)->disable(&status);
+ }
+ if (!bs.isOk()) {
+ status = statusTFromBinderStatus(bs);
}
}
return status;
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 2c79ac5..9da95bc 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -36,6 +36,7 @@
virtual uint32_t sampleRate() const = 0;
virtual audio_channel_mask_t channelMask() const = 0;
virtual uint32_t channelCount() const = 0;
+ virtual audio_channel_mask_t hapticChannelMask() const = 0;
virtual size_t frameCount() const = 0;
// Non trivial methods usually implemented with help from ThreadBase:
@@ -58,7 +59,7 @@
virtual bool updateOrphanEffectChains(const sp<EffectBase>& effect) = 0;
// Methods usually implemented with help from EffectChain: pay attention to mutex locking order
- virtual uint32_t strategy() const = 0;
+ virtual product_strategy_t strategy() const = 0;
virtual int32_t activeTrackCnt() const = 0;
virtual void resetVolume() = 0;
@@ -132,11 +133,10 @@
void setSuspended(bool suspended);
bool suspended() const;
- virtual status_t command(uint32_t cmdCode __unused,
- uint32_t cmdSize __unused,
- void *pCmdData __unused,
- uint32_t *replySize __unused,
- void *pReplyData __unused) { return NO_ERROR; };
+ virtual status_t command(int32_t __unused,
+ const std::vector<uint8_t>& __unused,
+ int32_t __unused,
+ std::vector<uint8_t>* __unused) { return NO_ERROR; };
void setCallback(const sp<EffectCallbackInterface>& callback) { mCallback = callback; }
sp<EffectCallbackInterface>& callback() { return mCallback; }
@@ -213,11 +213,10 @@
void process();
bool updateState();
- status_t command(uint32_t cmdCode,
- uint32_t cmdSize,
- void *pCmdData,
- uint32_t *replySize,
- void *pReplyData) override;
+ status_t command(int32_t cmdCode,
+ const std::vector<uint8_t>& cmdData,
+ int32_t maxReplySize,
+ std::vector<uint8_t>* reply) override;
void reset_l();
status_t configure();
@@ -255,6 +254,12 @@
sp<EffectModule> asEffectModule() override { return this; }
+ static bool isHapticGenerator(const effect_uuid_t* type);
+ bool isHapticGenerator() const;
+
+ status_t setHapticIntensity(int id, int intensity);
+ status_t setVibratorInfo(const media::AudioVibratorInfo* vibratorInfo);
+
void dump(int fd, const Vector<String16>& args);
private:
@@ -314,32 +319,29 @@
// There is one EffectHandle object for each application controlling (or using)
// an effect module.
// The EffectHandle is obtained by calling AudioFlinger::createEffect().
-class EffectHandle: public android::BnEffect {
+class EffectHandle: public android::media::BnEffect {
public:
EffectHandle(const sp<EffectBase>& effect,
const sp<AudioFlinger::Client>& client,
- const sp<IEffectClient>& effectClient,
+ const sp<media::IEffectClient>& effectClient,
int32_t priority);
virtual ~EffectHandle();
virtual status_t initCheck();
// IEffect
- virtual status_t enable();
- virtual status_t disable();
- virtual status_t command(uint32_t cmdCode,
- uint32_t cmdSize,
- void *pCmdData,
- uint32_t *replySize,
- void *pReplyData);
- virtual void disconnect();
-private:
- void disconnect(bool unpinIfLast);
-public:
- virtual sp<IMemory> getCblk() const { return mCblkMemory; }
- virtual status_t onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags);
+ android::binder::Status enable(int32_t* _aidl_return) override;
+ android::binder::Status disable(int32_t* _aidl_return) override;
+ android::binder::Status command(int32_t cmdCode,
+ const std::vector<uint8_t>& cmdData,
+ int32_t maxResponseSize,
+ std::vector<uint8_t>* response,
+ int32_t* _aidl_return) override;
+ android::binder::Status disconnect() override;
+ android::binder::Status getCblk(media::SharedFileRegion* _aidl_return) override;
+private:
+ void disconnect(bool unpinIfLast);
// Give or take control of effect module
// - hasControl: true if control is given, false if removed
@@ -347,10 +349,8 @@
// - enabled: state of the effect when control is passed
void setControl(bool hasControl, bool signal, bool enabled);
void commandExecuted(uint32_t cmdCode,
- uint32_t cmdSize,
- void *pCmdData,
- uint32_t replySize,
- void *pReplyData);
+ const std::vector<uint8_t>& cmdData,
+ const std::vector<uint8_t>& replyData);
void setEnabled(bool enabled);
bool enabled() const { return mEnabled; }
@@ -373,19 +373,20 @@
friend class AudioFlinger; // for mEffect, mHasControl, mEnabled
DISALLOW_COPY_AND_ASSIGN(EffectHandle);
- Mutex mLock; // protects IEffect method calls
- wp<EffectBase> mEffect; // pointer to controlled EffectModule
- sp<IEffectClient> mEffectClient; // callback interface for client notifications
- /*const*/ sp<Client> mClient; // client for shared memory allocation, see disconnect()
- sp<IMemory> mCblkMemory; // shared memory for control block
- effect_param_cblk_t* mCblk; // control block for deferred parameter setting via
- // shared memory
- uint8_t* mBuffer; // pointer to parameter area in shared memory
- int mPriority; // client application priority to control the effect
- bool mHasControl; // true if this handle is controlling the effect
- bool mEnabled; // cached enable state: needed when the effect is
- // restored after being suspended
- bool mDisconnected; // Set to true by disconnect()
+ Mutex mLock; // protects IEffect method calls
+ const wp<EffectBase> mEffect; // pointer to controlled EffectModule
+ const sp<media::IEffectClient> mEffectClient; // callback interface for client notifications
+ /*const*/ sp<Client> mClient; // client for shared memory allocation, see
+ // disconnect()
+ sp<IMemory> mCblkMemory; // shared memory for control block
+ effect_param_cblk_t* mCblk; // control block for deferred parameter setting via
+ // shared memory
+ uint8_t* mBuffer; // pointer to parameter area in shared memory
+ int mPriority; // client application priority to control the effect
+ bool mHasControl; // true if this handle is controlling the effect
+ bool mEnabled; // cached enable state: needed when the effect is
+ // restored after being suspended
+ bool mDisconnected; // Set to true by disconnect()
};
// the EffectChain class represents a group of effects associated to one audio session.
@@ -465,8 +466,8 @@
void decActiveTrackCnt() { android_atomic_dec(&mActiveTrackCnt); }
int32_t activeTrackCnt() const { return android_atomic_acquire_load(&mActiveTrackCnt); }
- uint32_t strategy() const { return mStrategy; }
- void setStrategy(uint32_t strategy)
+ product_strategy_t strategy() const { return mStrategy; }
+ void setStrategy(product_strategy_t strategy)
{ mStrategy = strategy; }
// suspend or restore effects of the specified type. The number of suspend requests is counted
@@ -501,6 +502,10 @@
// isCompatibleWithThread_l() must be called with thread->mLock held
bool isCompatibleWithThread_l(const sp<ThreadBase>& thread) const;
+ bool containsHapticGeneratingEffect_l();
+
+ void setHapticIntensity_l(int id, int intensity);
+
sp<EffectCallbackInterface> effectCallback() const { return mEffectCallback; }
wp<ThreadBase> thread() const { return mEffectCallback->thread(); }
@@ -508,14 +513,21 @@
private:
+ // For transaction consistency, please consider holding the EffectChain lock before
+ // calling the EffectChain::EffectCallback methods, excepting
+ // createEffectHal and allocateHalBuffer.
+ //
+ // This prevents migration of the EffectChain to another PlaybackThread
+ // for the purposes of the EffectCallback.
class EffectCallback : public EffectCallbackInterface {
public:
// Note: ctors taking a weak pointer to their owner must not promote it
// during construction (but may keep a reference for later promotion).
EffectCallback(const wp<EffectChain>& owner,
const wp<ThreadBase>& thread)
- : mChain(owner) {
- setThread(thread);
+ : mChain(owner)
+ , mThread(thread)
+ , mAudioFlinger(*gAudioFlinger) {
}
status_t createEffectHal(const effect_uuid_t *pEffectUuid,
@@ -532,6 +544,7 @@
uint32_t sampleRate() const override;
audio_channel_mask_t channelMask() const override;
uint32_t channelCount() const override;
+ audio_channel_mask_t hapticChannelMask() const override;
size_t frameCount() const override;
uint32_t latency() const override;
@@ -544,25 +557,23 @@
void checkSuspendOnEffectEnabled(const sp<EffectBase>& effect,
bool enabled, bool threadLocked) override;
void resetVolume() override;
- uint32_t strategy() const override;
+ product_strategy_t strategy() const override;
int32_t activeTrackCnt() const override;
void onEffectEnable(const sp<EffectBase>& effect) override;
void onEffectDisable(const sp<EffectBase>& effect) override;
wp<EffectChain> chain() const override { return mChain; }
- wp<ThreadBase> thread() { return mThread; }
+ wp<ThreadBase> thread() const { return mThread.load(); }
void setThread(const wp<ThreadBase>& thread) {
mThread = thread;
- sp<ThreadBase> p = thread.promote();
- mAudioFlinger = p ? p->mAudioFlinger : nullptr;
}
private:
- wp<EffectChain> mChain;
- wp<ThreadBase> mThread;
- wp<AudioFlinger> mAudioFlinger;
+ const wp<EffectChain> mChain;
+ mediautils::atomic_wp<ThreadBase> mThread;
+ AudioFlinger &mAudioFlinger; // implementation detail: outer instance always exists.
};
friend class AudioFlinger; // for mThread, mEffects
@@ -616,7 +627,7 @@
uint32_t mRightVolume; // previous volume on right channel
uint32_t mNewLeftVolume; // new volume on left channel
uint32_t mNewRightVolume; // new volume on right channel
- uint32_t mStrategy; // strategy for this effect chain
+ product_strategy_t mStrategy; // strategy for this effect chain
// mSuspendedEffects lists all effects currently suspended in the chain.
// Use effect type UUID timelow field as key. There is no real risk of identical
// timeLow fields among effect type UUIDs.
@@ -683,6 +694,7 @@
uint32_t sampleRate() const override;
audio_channel_mask_t channelMask() const override;
uint32_t channelCount() const override;
+ audio_channel_mask_t hapticChannelMask() const override { return AUDIO_CHANNEL_NONE; }
size_t frameCount() const override { return 0; }
uint32_t latency() const override { return 0; }
@@ -695,7 +707,7 @@
void checkSuspendOnEffectEnabled(const sp<EffectBase>& effect __unused,
bool enabled __unused, bool threadLocked __unused) override {}
void resetVolume() override {}
- uint32_t strategy() const override { return 0; }
+ product_strategy_t strategy() const override { return static_cast<product_strategy_t>(0); }
int32_t activeTrackCnt() const override { return 0; }
void onEffectEnable(const sp<EffectBase>& effect __unused) override {}
void onEffectDisable(const sp<EffectBase>& effect __unused) override {}
diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h
index 396c797..857d3de 100644
--- a/services/audioflinger/FastMixerState.h
+++ b/services/audioflinger/FastMixerState.h
@@ -23,6 +23,7 @@
#include <media/ExtendedAudioBufferProvider.h>
#include <media/nbaio/NBAIO.h>
#include <media/nblog/NBLog.h>
+#include <vibrator/ExternalVibrationUtils.h>
#include "FastThreadState.h"
namespace android {
@@ -49,8 +50,7 @@
audio_format_t mFormat; // track format
int mGeneration; // increment when any field is assigned
bool mHapticPlaybackEnabled = false; // haptic playback is enabled or not
- AudioMixer::haptic_intensity_t mHapticIntensity = AudioMixer::HAPTIC_SCALE_MUTE; // intensity of
- // haptic data
+ os::HapticScale mHapticIntensity = os::HapticScale::MUTE; // intensity of haptic data
};
// Represents a single state of the fast mixer
diff --git a/services/audioflinger/MmapTracks.h b/services/audioflinger/MmapTracks.h
index b83f6b5..ba868d7 100644
--- a/services/audioflinger/MmapTracks.h
+++ b/services/audioflinger/MmapTracks.h
@@ -29,8 +29,7 @@
audio_channel_mask_t channelMask,
audio_session_t sessionId,
bool isOut,
- uid_t uid,
- pid_t pid,
+ const media::permission::Identity& identity,
pid_t creatorPid,
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE);
virtual ~MmapTrack();
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index 030c929..a381c7d 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -25,6 +25,7 @@
#include "AudioFlinger.h"
#include <media/AudioParameter.h>
+#include <media/AudioValidator.h>
#include <media/DeviceDescriptorBase.h>
#include <media/PatchBuilder.h>
#include <mediautils/ServiceUtilities.h>
@@ -55,8 +56,12 @@
}
/* Get supported attributes for a given audio port */
-status_t AudioFlinger::getAudioPort(struct audio_port *port)
-{
+status_t AudioFlinger::getAudioPort(struct audio_port_v7 *port) {
+ status_t status = AudioValidator::validateAudioPort(*port);
+ if (status != NO_ERROR) {
+ return status;
+ }
+
Mutex::Autolock _l(mLock);
return mPatchPanel.getAudioPort(port);
}
@@ -65,6 +70,11 @@
status_t AudioFlinger::createAudioPatch(const struct audio_patch *patch,
audio_patch_handle_t *handle)
{
+ status_t status = AudioValidator::validateAudioPatch(*patch);
+ if (status != NO_ERROR) {
+ return status;
+ }
+
Mutex::Autolock _l(mLock);
return mPatchPanel.createAudioPatch(patch, handle);
}
@@ -103,10 +113,22 @@
}
/* Get supported attributes for a given audio port */
-status_t AudioFlinger::PatchPanel::getAudioPort(struct audio_port *port __unused)
+status_t AudioFlinger::PatchPanel::getAudioPort(struct audio_port_v7 *port)
{
- ALOGV(__func__);
- return NO_ERROR;
+ if (port->type != AUDIO_PORT_TYPE_DEVICE) {
+ // Only query the HAL when the port is a device.
+ // TODO: implement getAudioPort for mix.
+ return INVALID_OPERATION;
+ }
+ AudioHwDevice* hwDevice = findAudioHwDeviceByModule(port->ext.device.hw_module);
+ if (hwDevice == nullptr) {
+ ALOGW("%s cannot find hw module %d", __func__, port->ext.device.hw_module);
+ return BAD_VALUE;
+ }
+ if (!hwDevice->supportsAudioPatches()) {
+ return INVALID_OPERATION;
+ }
+ return hwDevice->getAudioPort(port);
}
/* Connect a patch between several source and sink ports */
diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h
index 38f0615..93593a3 100644
--- a/services/audioflinger/PatchPanel.h
+++ b/services/audioflinger/PatchPanel.h
@@ -52,7 +52,7 @@
struct audio_port *ports);
/* Get supported attributes for a given audio port */
- status_t getAudioPort(struct audio_port *port);
+ status_t getAudioPort(struct audio_port_v7 *port);
/* Create a patch between several source and sink ports */
status_t createAudioPatch(const struct audio_patch *patch,
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 9770054..2436248 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -15,6 +15,8 @@
** limitations under the License.
*/
+#include <android/media/permission/Identity.h>
+
#ifndef INCLUDING_FROM_AUDIOFLINGER_H
#error This header file should only be included from AudioFlinger.h
#endif
@@ -26,11 +28,13 @@
bool hasOpPlayAudio() const;
static sp<OpPlayAudioMonitor> createIfNeeded(
- uid_t uid, const audio_attributes_t& attr, int id, audio_stream_type_t streamType,
- const std::string& opPackageName);
+ const android::media::permission::Identity& identity,
+ const audio_attributes_t& attr, int id,
+ audio_stream_type_t streamType);
private:
- OpPlayAudioMonitor(uid_t uid, audio_usage_t usage, int id, const String16& opPackageName);
+ OpPlayAudioMonitor(const android::media::permission::Identity& identity,
+ audio_usage_t usage, int id);
void onFirstRef() override;
static void getPackagesForUid(uid_t uid, Vector<String16>& packages);
@@ -50,10 +54,9 @@
void checkPlayAudioForUsage();
std::atomic_bool mHasOpPlayAudio;
- const uid_t mUid;
+ const android::media::permission::Identity mIdentity;
const int32_t mUsage; // on purpose not audio_usage_t because always checked in appOps as int32_t
const int mId; // for logging purposes only
- const String16 mOpPackageName;
};
// playback track
@@ -72,14 +75,13 @@
const sp<IMemory>& sharedBuffer,
audio_session_t sessionId,
pid_t creatorPid,
- uid_t uid,
+ const media::permission::Identity& identity,
audio_output_flags_t flags,
track_type type,
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE,
/** default behaviour is to start when there are as many frames
* ready as possible (aka. Buffer is full). */
- size_t frameCountToBeReady = SIZE_MAX,
- const std::string opPackageName = "");
+ size_t frameCountToBeReady = SIZE_MAX);
virtual ~Track();
virtual status_t initCheck() const;
@@ -167,12 +169,12 @@
mHapticPlaybackEnabled = hapticPlaybackEnabled;
}
/** Return at what intensity to play haptics, used in mixer. */
- AudioMixer::haptic_intensity_t getHapticIntensity() const { return mHapticIntensity; }
+ os::HapticScale getHapticIntensity() const { return mHapticIntensity; }
/** Set intensity of haptic playback, should be set after querying vibrator service. */
- void setHapticIntensity(AudioMixer::haptic_intensity_t hapticIntensity) {
- if (AudioMixer::isValidHapticIntensity(hapticIntensity)) {
+ void setHapticIntensity(os::HapticScale hapticIntensity) {
+ if (os::isValidHapticScale(hapticIntensity)) {
mHapticIntensity = hapticIntensity;
- setHapticPlaybackEnabled(mHapticIntensity != AudioMixer::HAPTIC_SCALE_MUTE);
+ setHapticPlaybackEnabled(mHapticIntensity != os::HapticScale::MUTE);
}
}
sp<os::ExternalVibration> getExternalVibration() const { return mExternalVibration; }
@@ -277,7 +279,7 @@
bool mHapticPlaybackEnabled = false; // indicates haptic playback enabled or not
// intensity to play haptic data
- AudioMixer::haptic_intensity_t mHapticIntensity = AudioMixer::HAPTIC_SCALE_MUTE;
+ os::HapticScale mHapticIntensity = os::HapticScale::MUTE;
class AudioVibrationController : public os::BnExternalVibrationController {
public:
explicit AudioVibrationController(Track* track) : mTrack(track) {}
@@ -339,7 +341,7 @@
audio_format_t format,
audio_channel_mask_t channelMask,
size_t frameCount,
- uid_t uid);
+ const android::media::permission::Identity& identity);
virtual ~OutputTrack();
virtual status_t start(AudioSystem::sync_event_t event =
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index d87239d..451c198 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -26,10 +26,10 @@
bool hasOpRecordAudio() const;
static sp<OpRecordAudioMonitor> createIfNeeded
- (uid_t uid, const audio_attributes_t& attr, const String16& opPackageName);
+ (const media::permission::Identity& identity, const audio_attributes_t& attr);
private:
- OpRecordAudioMonitor(uid_t uid, const String16& opPackageName);
+ explicit OpRecordAudioMonitor(const media::permission::Identity& identity);
void onFirstRef() override;
AppOpsManager mAppOpsManager;
@@ -49,8 +49,7 @@
void checkRecordAudio();
std::atomic_bool mHasOpRecordAudio;
- const uid_t mUid;
- const String16 mPackage;
+ const media::permission::Identity mIdentity;
};
// record track
@@ -67,11 +66,11 @@
size_t bufferSize,
audio_session_t sessionId,
pid_t creatorPid,
- uid_t uid,
+ const media::permission::Identity& identity,
audio_input_flags_t flags,
track_type type,
- const String16& opPackageName,
- audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE);
+ audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE,
+ int32_t startFrames = -1);
virtual ~RecordTrack();
virtual status_t initCheck() const;
@@ -109,6 +108,9 @@
status_t setPreferredMicrophoneDirection(audio_microphone_direction_t direction);
status_t setPreferredMicrophoneFieldDimension(float zoom);
+ status_t shareAudioHistory(const std::string& sharedAudioPackageName,
+ int64_t sharedAudioStartMs);
+ int32_t startFrames() { return mStartFrames; }
static bool checkServerLatencySupported(
audio_format_t format, audio_input_flags_t flags) {
@@ -148,9 +150,9 @@
bool mSilenced;
// used to enforce OP_RECORD_AUDIO
- uid_t mUid;
- String16 mOpPackageName;
sp<OpRecordAudioMonitor> mOpRecordAudioMonitor;
+ std::string mSharedAudioPackageName = {};
+ int32_t mStartFrames = -1;
};
// playback track, used by PatchPanel
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 590b379..d42a6ca 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -108,6 +108,7 @@
// TODO: Move these macro/inlines to a header file.
#define max(a, b) ((a) > (b) ? (a) : (b))
+
template <typename T>
static inline T min(const T& a, const T& b)
{
@@ -116,6 +117,9 @@
namespace android {
+using media::IEffectClient;
+using media::permission::Identity;
+
// retry counts for buffer fill timeout
// 50 * ~20msecs = 1 second
static const int8_t kMaxTrackRetries = 50;
@@ -710,6 +714,13 @@
return sendConfigEvent_l(configEvent);
}
+void AudioFlinger::ThreadBase::sendResizeBufferConfigEvent_l(int32_t maxSharedAudioHistoryMs)
+{
+ ALOG_ASSERT(type() == RECORD, "sendResizeBufferConfigEvent_l() called on non record thread");
+ sp<ConfigEvent> configEvent =
+ (ConfigEvent *)new ResizeBufferConfigEvent(maxSharedAudioHistoryMs);
+ sendConfigEvent_l(configEvent);
+}
// post condition: mConfigEvents.isEmpty()
void AudioFlinger::ThreadBase::processConfigEvents_l()
@@ -768,6 +779,11 @@
(UpdateOutDevicesConfigEventData *)event->mData.get();
updateOutDevices(data->mOutDevices);
} break;
+ case CFG_EVENT_RESIZE_BUFFER: {
+ ResizeBufferConfigEventData *data =
+ (ResizeBufferConfigEventData *)event->mData.get();
+ resizeInputBuffer_l(data->mMaxSharedAudioHistoryMs);
+ } break;
default:
ALOG_ASSERT(false, "processConfigEvents_l() unknown event type %d", event->mType);
break;
@@ -1004,15 +1020,16 @@
if (mPowerManager != 0) {
sp<IBinder> binder = new BBinder();
// Uses AID_AUDIOSERVER for wakelock. updateWakeLockUids_l() updates with client uids.
- status_t status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK,
- binder,
+ binder::Status status = mPowerManager->acquireWakeLockAsync(binder,
+ POWERMANAGER_PARTIAL_WAKE_LOCK,
getWakeLockTag(),
String16("audioserver"),
- true /* FIXME force oneway contrary to .aidl */);
- if (status == NO_ERROR) {
+ {} /* workSource */,
+ {} /* historyTag */);
+ if (status.isOk()) {
mWakeLockToken = binder;
}
- ALOGV("acquireWakeLock_l() %s status %d", mThreadName, status);
+ ALOGV("acquireWakeLock_l() %s status %d", mThreadName, status.exceptionCode());
}
gBoottime.acquire(mWakeLockToken);
@@ -1032,8 +1049,7 @@
if (mWakeLockToken != 0) {
ALOGV("releaseWakeLock_l() %s", mThreadName);
if (mPowerManager != 0) {
- mPowerManager->releaseWakeLock(mWakeLockToken, 0,
- true /* FIXME force oneway contrary to .aidl */);
+ mPowerManager->releaseWakeLockAsync(mWakeLockToken, 0);
}
mWakeLockToken.clear();
}
@@ -1047,7 +1063,7 @@
if (binder == 0) {
ALOGW("Thread %s cannot connect to the power manager service", mThreadName);
} else {
- mPowerManager = interface_cast<IPowerManager>(binder);
+ mPowerManager = interface_cast<os::IPowerManager>(binder);
binder->linkToDeath(mDeathRecipient);
}
}
@@ -1074,10 +1090,9 @@
}
if (mPowerManager != 0) {
std::vector<int> uidsAsInt(uids.begin(), uids.end()); // powermanager expects uids as ints
- status_t status = mPowerManager->updateWakeLockUids(
- mWakeLockToken, uidsAsInt.size(), uidsAsInt.data(),
- true /* FIXME force oneway contrary to .aidl */);
- ALOGV("updateWakeLockUids_l() %s status %d", mThreadName, status);
+ binder::Status status = mPowerManager->updateWakeLockUidsAsync(
+ mWakeLockToken, uidsAsInt);
+ ALOGV("updateWakeLockUids_l() %s status %d", mThreadName, status.exceptionCode());
}
}
@@ -1094,6 +1109,11 @@
ALOGE("%s should only be called in RecordThread", __func__);
}
+void AudioFlinger::ThreadBase::resizeInputBuffer_l(int32_t maxSharedAudioHistoryMs __unused)
+{
+ ALOGE("%s should only be called in RecordThread", __func__);
+}
+
void AudioFlinger::ThreadBase::PMDeathRecipient::binderDied(const wp<IBinder>& who __unused)
{
sp<ThreadBase> thread = mThread.promote();
@@ -1262,6 +1282,11 @@
return BAD_VALUE;
}
}
+
+ if (EffectModule::isHapticGenerator(&desc->type)) {
+ ALOGE("%s(): HapticGenerator is not supported in RecordThread", __func__);
+ return BAD_VALUE;
+ }
return NO_ERROR;
}
@@ -1281,6 +1306,12 @@
return NO_ERROR;
}
+ if (EffectModule::isHapticGenerator(&desc->type) && mHapticChannelCount == 0) {
+ ALOGW("%s: thread doesn't support haptic playback while the effect is HapticGenerator",
+ __func__);
+ return BAD_VALUE;
+ }
+
switch (mType) {
case MIXER: {
#ifndef MULTICHANNEL_EFFECT_CHAIN
@@ -1443,6 +1474,16 @@
effect->setMode(mAudioFlinger->getMode());
effect->setAudioSource(mAudioSource);
}
+ if (effect->isHapticGenerator()) {
+ // TODO(b/184194057): Use the vibrator information from the vibrator that will be used
+ // for the HapticGenerator.
+ const media::AudioVibratorInfo* defaultVibratorInfo =
+ mAudioFlinger->getDefaultVibratorInfo_l();
+ if (defaultVibratorInfo != nullptr) {
+ // Only set the vibrator info when it is a valid one.
+ effect->setVibratorInfo(defaultVibratorInfo);
+ }
+ }
// create effect handle and connect it to effect module
handle = new EffectHandle(effect, client, effectClient, priority);
lStatus = handle->initCheck();
@@ -1952,7 +1993,7 @@
// here instead of constructor of PlaybackThread so that the onFirstRef
// callback would not be made on an incompletely constructed object.
if (mOutput->stream->setEventCallback(this) != OK) {
- ALOGE("Failed to add event callback");
+ ALOGD("Failed to add event callback");
}
}
run(mThreadName, ANDROID_PRIORITY_URGENT_AUDIO);
@@ -2082,12 +2123,11 @@
audio_session_t sessionId,
audio_output_flags_t *flags,
pid_t creatorPid,
+ const Identity& identity,
pid_t tid,
- uid_t uid,
status_t *status,
audio_port_handle_t portId,
- const sp<media::IAudioTrackCallback>& callback,
- const std::string& opPackageName)
+ const sp<media::IAudioTrackCallback>& callback)
{
size_t frameCount = *pFrameCount;
size_t notificationFrameCount = *pNotificationFrameCount;
@@ -2178,8 +2218,8 @@
"sampleRate=%u mSampleRate=%u "
"hasFastMixer=%d tid=%d fastTrackAvailMask=%#x",
sharedBuffer.get(), frameCount, mFrameCount, format, mFormat,
- audio_is_linear_pcm(format),
- channelMask, sampleRate, mSampleRate, hasFastMixer(), tid, mFastTrackAvailMask);
+ audio_is_linear_pcm(format), channelMask, sampleRate,
+ mSampleRate, hasFastMixer(), tid, mFastTrackAvailMask);
*flags = (audio_output_flags_t)(*flags & ~AUDIO_OUTPUT_FLAG_FAST);
}
}
@@ -2351,11 +2391,11 @@
// all tracks in same audio session must share the same routing strategy otherwise
// conflicts will happen when tracks are moved from one output to another by audio policy
// manager
- uint32_t strategy = AudioSystem::getStrategyForStream(streamType);
+ product_strategy_t strategy = AudioSystem::getStrategyForStream(streamType);
for (size_t i = 0; i < mTracks.size(); ++i) {
sp<Track> t = mTracks[i];
if (t != 0 && t->isExternalTrack()) {
- uint32_t actual = AudioSystem::getStrategyForStream(t->streamType());
+ product_strategy_t actual = AudioSystem::getStrategyForStream(t->streamType());
if (sessionId == t->sessionId() && strategy != actual) {
ALOGE("createTrack_l() mismatched strategy; expected %u but found %u",
strategy, actual);
@@ -2365,11 +2405,21 @@
}
}
+ // Set DIRECT flag if current thread is DirectOutputThread. This can
+ // happen when the playback is rerouted to direct output thread by
+ // dynamic audio policy.
+ // Do NOT report the flag changes back to client, since the client
+ // doesn't explicitly request a direct flag.
+ audio_output_flags_t trackFlags = *flags;
+ if (mType == DIRECT) {
+ trackFlags = static_cast<audio_output_flags_t>(trackFlags | AUDIO_OUTPUT_FLAG_DIRECT);
+ }
+
track = new Track(this, client, streamType, attr, sampleRate, format,
channelMask, frameCount,
nullptr /* buffer */, (size_t)0 /* bufferSize */, sharedBuffer,
- sessionId, creatorPid, uid, *flags, TrackBase::TYPE_DEFAULT, portId,
- SIZE_MAX /*frameCountToBeReady*/, opPackageName);
+ sessionId, creatorPid, identity, trackFlags, TrackBase::TYPE_DEFAULT,
+ portId, SIZE_MAX /*frameCountToBeReady*/);
lStatus = track != 0 ? track->initCheck() : (status_t) NO_MEMORY;
if (lStatus != NO_ERROR) {
@@ -2548,15 +2598,17 @@
track->sharedBuffer() != 0 ? Track::FS_FILLED : Track::FS_FILLING;
}
- if ((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
- && mHapticChannelMask != AUDIO_CHANNEL_NONE) {
+ sp<EffectChain> chain = getEffectChain_l(track->sessionId());
+ if (mHapticChannelMask != AUDIO_CHANNEL_NONE
+ && ((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
+ || (chain != nullptr && chain->containsHapticGeneratingEffect_l()))) {
// Unlock due to VibratorService will lock for this call and will
// call Tracks.mute/unmute which also require thread's lock.
mLock.unlock();
const int intensity = AudioFlinger::onExternalVibrationStart(
track->getExternalVibration());
mLock.lock();
- track->setHapticIntensity(static_cast<AudioMixer::haptic_intensity_t>(intensity));
+ track->setHapticIntensity(static_cast<os::HapticScale>(intensity));
// Haptic playback should be enabled by vibrator service.
if (track->getHapticPlaybackEnabled()) {
// Disable haptic playback of all active track to ensure only
@@ -2565,12 +2617,16 @@
t->setHapticPlaybackEnabled(false);
}
}
+
+ // Set haptic intensity for effect
+ if (chain != nullptr) {
+ chain->setHapticIntensity_l(track->id(), intensity);
+ }
}
track->mResetDone = false;
track->mPresentationCompleteFrames = 0;
mActiveTracks.add(track);
- sp<EffectChain> chain = getEffectChain_l(track->sessionId());
if (chain != 0) {
ALOGV("addTrack_l() starting track on chain %p for session %d", chain.get(),
track->sessionId());
@@ -2952,7 +3008,10 @@
auto backInserter = std::back_inserter(metadata.tracks);
for (const sp<Track> &track : mActiveTracks) {
// No track is invalid as this is called after prepareTrack_l in the same critical section
- track->copyMetadataTo(backInserter);
+ // Do not forward metadata for PatchTrack with unspecified stream type
+ if (track->streamType() != AUDIO_STREAM_PATCH) {
+ track->copyMetadataTo(backInserter);
+ }
}
sendMetadataToBackend_l(metadata);
}
@@ -2990,7 +3049,7 @@
}
}
-uint32_t AudioFlinger::PlaybackThread::getStrategyForSession_l(audio_session_t sessionId)
+product_strategy_t AudioFlinger::PlaybackThread::getStrategyForSession_l(audio_session_t sessionId)
{
// session AUDIO_SESSION_OUTPUT_MIX is placed in same strategy as MUSIC stream so that
// it is moved to correct output by audio policy manager when A2DP is connected or disconnected
@@ -3626,9 +3685,15 @@
// Determine which session to pick up haptic data.
// This must be done under the same lock as prepareTracks_l().
+ // The haptic data from the effect is at a higher priority than the one from track.
// TODO: Write haptic data directly to sink buffer when mixing.
if (mHapticChannelCount > 0 && effectChains.size() > 0) {
for (const auto& track : mActiveTracks) {
+ sp<EffectChain> effectChain = getEffectChain_l(track->sessionId());
+ if (effectChain != nullptr && effectChain->containsHapticGeneratingEffect_l()) {
+ activeHapticSessionId = track->sessionId();
+ break;
+ }
if (track->getHapticPlaybackEnabled()) {
activeHapticSessionId = track->sessionId();
break;
@@ -4140,13 +4205,20 @@
// remove from our tracks vector
removeTrack_l(track);
}
- if ((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
- && mHapticChannelCount > 0) {
+ if (mHapticChannelCount > 0 &&
+ ((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
+ || (chain != nullptr && chain->containsHapticGeneratingEffect_l()))) {
mLock.unlock();
// Unlock due to VibratorService will lock for this call and will
// call Tracks.mute/unmute which also require thread's lock.
AudioFlinger::onExternalVibrationStop(track->getExternalVibration());
mLock.lock();
+
+ // When the track is stop, set the haptic intensity as MUTE
+ // for the HapticGenerator effect.
+ if (chain != nullptr) {
+ chain->setHapticIntensity_l(track->id(), static_cast<int>(os::HapticScale::MUTE));
+ }
}
}
}
@@ -4485,7 +4557,7 @@
// audio to FastMixer
fastTrack->mFormat = mFormat; // mPipeSink format for audio to FastMixer
fastTrack->mHapticPlaybackEnabled = mHapticChannelMask != AUDIO_CHANNEL_NONE;
- fastTrack->mHapticIntensity = AudioMixer::HAPTIC_SCALE_NONE;
+ fastTrack->mHapticIntensity = os::HapticScale::NONE;
fastTrack->mGeneration++;
state->mFastTracksGen++;
state->mTrackMask = 1;
@@ -6806,13 +6878,19 @@
// from different OutputTracks and their associated MixerThreads (e.g. one may
// nearly empty and the other may be dropping data).
+ // TODO b/182392769: use identity util, move to server edge
+ Identity identity = Identity();
+ identity.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(
+ IPCThreadState::self()->getCallingUid()));
+ identity.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(
+ IPCThreadState::self()->getCallingPid()));
sp<OutputTrack> outputTrack = new OutputTrack(thread,
this,
mSampleRate,
mFormat,
mChannelMask,
frameCount,
- IPCThreadState::self()->getCallingUid());
+ identity);
status_t status = outputTrack != 0 ? outputTrack->initCheck() : (status_t) NO_MEMORY;
if (status != NO_ERROR) {
ALOGE("addOutputTrack() initCheck failed %d", status);
@@ -7227,19 +7305,29 @@
// the only active track
// 2) invalidate this track: this will cause the client to reconnect and possibly
// be invalidated again until unsilenced
+ bool invalidate = false;
if (activeTrack->isSilenced()) {
if (size > 1) {
- activeTrack->invalidate();
- ALOG_ASSERT(fastTrackToRemove == 0);
- fastTrackToRemove = activeTrack;
- removeTrack_l(activeTrack);
- mActiveTracks.remove(activeTrack);
- size--;
- continue;
+ invalidate = true;
} else {
silenceFastCapture = true;
}
}
+ // Invalidate fast tracks if access to audio history is required as this is not
+ // possible with fast tracks. Once the fast track has been invalidated, no new
+ // fast track will be created until mMaxSharedAudioHistoryMs is cleared.
+ if (mMaxSharedAudioHistoryMs != 0) {
+ invalidate = true;
+ }
+ if (invalidate) {
+ activeTrack->invalidate();
+ ALOG_ASSERT(fastTrackToRemove == 0);
+ fastTrackToRemove = activeTrack;
+ removeTrack_l(activeTrack);
+ mActiveTracks.remove(activeTrack);
+ size--;
+ continue;
+ }
fastTrack = activeTrack;
}
@@ -7726,12 +7814,12 @@
audio_session_t sessionId,
size_t *pNotificationFrameCount,
pid_t creatorPid,
- uid_t uid,
+ const Identity& identity,
audio_input_flags_t *flags,
pid_t tid,
status_t *status,
audio_port_handle_t portId,
- const String16& opPackageName)
+ int32_t maxSharedAudioHistoryMs)
{
size_t frameCount = *pFrameCount;
size_t notificationFrameCount = *pNotificationFrameCount;
@@ -7740,6 +7828,7 @@
audio_input_flags_t inputFlags = mInput->flags;
audio_input_flags_t requestedFlags = *flags;
uint32_t sampleRate;
+ Identity checkedIdentity = AudioFlinger::checkIdentityPackage(identity);
lStatus = initCheck();
if (lStatus != NO_ERROR) {
@@ -7753,13 +7842,25 @@
goto Exit;
}
+ if (maxSharedAudioHistoryMs != 0) {
+ if (!captureHotwordAllowed(checkedIdentity)) {
+ lStatus = PERMISSION_DENIED;
+ goto Exit;
+ }
+ if (maxSharedAudioHistoryMs < 0
+ || maxSharedAudioHistoryMs > AudioFlinger::kMaxSharedAudioHistoryMs) {
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ }
if (*pSampleRate == 0) {
*pSampleRate = mSampleRate;
}
sampleRate = *pSampleRate;
- // special case for FAST flag considered OK if fast capture is present
- if (hasFastCapture()) {
+ // special case for FAST flag considered OK if fast capture is present and access to
+ // audio history is not required
+ if (hasFastCapture() && mMaxSharedAudioHistoryMs == 0) {
inputFlags = (audio_input_flags_t)(inputFlags | AUDIO_INPUT_FLAG_FAST);
}
@@ -7771,8 +7872,9 @@
*flags = (audio_input_flags_t)(*flags & inputFlags);
}
- // client expresses a preference for FAST, but we get the final say
- if (*flags & AUDIO_INPUT_FLAG_FAST) {
+ // client expresses a preference for FAST and no access to audio history,
+ // but we get the final say
+ if (*flags & AUDIO_INPUT_FLAG_FAST && maxSharedAudioHistoryMs == 0) {
if (
// we formerly checked for a callback handler (non-0 tid),
// but that is no longer required for TRANSFER_OBTAIN mode
@@ -7861,11 +7963,18 @@
{ // scope for mLock
Mutex::Autolock _l(mLock);
+ int32_t startFrames = -1;
+ if (!mSharedAudioPackageName.empty()
+ && mSharedAudioPackageName == checkedIdentity.packageName
+ && mSharedAudioSessionId == sessionId
+ && captureHotwordAllowed(checkedIdentity)) {
+ startFrames = mSharedAudioStartFrames;
+ }
track = new RecordTrack(this, client, attr, sampleRate,
format, channelMask, frameCount,
- nullptr /* buffer */, (size_t)0 /* bufferSize */, sessionId, creatorPid, uid,
- *flags, TrackBase::TYPE_DEFAULT, opPackageName, portId);
+ nullptr /* buffer */, (size_t)0 /* bufferSize */, sessionId, creatorPid,
+ checkedIdentity, *flags, TrackBase::TYPE_DEFAULT, portId, startFrames);
lStatus = track->initCheck();
if (lStatus != NO_ERROR) {
@@ -7881,6 +7990,10 @@
// so ask activity manager to do this on our behalf
sendPrioConfigEvent_l(callingPid, tid, kPriorityAudioApp, true /*forApp*/);
}
+
+ if (maxSharedAudioHistoryMs != 0) {
+ sendResizeBufferConfigEvent_l(maxSharedAudioHistoryMs);
+ }
}
lStatus = NO_ERROR;
@@ -8073,6 +8186,9 @@
{
ALOGV("RecordThread::getActiveMicrophones");
AutoMutex _l(mLock);
+ if (mInput == nullptr || mInput->stream == nullptr) {
+ return NO_INIT;
+ }
status_t status = mInput->stream->getActiveMicrophones(activeMicrophones);
return status;
}
@@ -8082,6 +8198,9 @@
{
ALOGV("setPreferredMicrophoneDirection(%d)", direction);
AutoMutex _l(mLock);
+ if (mInput == nullptr || mInput->stream == nullptr) {
+ return NO_INIT;
+ }
return mInput->stream->setPreferredMicrophoneDirection(direction);
}
@@ -8089,9 +8208,55 @@
{
ALOGV("setPreferredMicrophoneFieldDimension(%f)", zoom);
AutoMutex _l(mLock);
+ if (mInput == nullptr || mInput->stream == nullptr) {
+ return NO_INIT;
+ }
return mInput->stream->setPreferredMicrophoneFieldDimension(zoom);
}
+status_t AudioFlinger::RecordThread::shareAudioHistory(
+ const std::string& sharedAudioPackageName, audio_session_t sharedSessionId,
+ int64_t sharedAudioStartMs) {
+ AutoMutex _l(mLock);
+ return shareAudioHistory_l(sharedAudioPackageName, sharedSessionId, sharedAudioStartMs);
+}
+
+status_t AudioFlinger::RecordThread::shareAudioHistory_l(
+ const std::string& sharedAudioPackageName, audio_session_t sharedSessionId,
+ int64_t sharedAudioStartMs) {
+ if ((hasAudioSession_l(sharedSessionId) & ThreadBase::TRACK_SESSION) == 0) {
+ return BAD_VALUE;
+ }
+
+ if (sharedAudioStartMs < 0
+ || sharedAudioStartMs > INT64_MAX / mSampleRate) {
+ return BAD_VALUE;
+ }
+
+ // Current implementation of the input resampling buffer wraps around indexes at 32 bit.
+ // As we cannot detect more than one wraparound, only accept values up current write position
+ // after one wraparound
+ // We assume recent wraparounds on mRsmpInRear only given it is unlikely that the requesting
+ // app waits several hours after the start time was computed.
+ const int64_t sharedAudioStartFrames = sharedAudioStartMs * mSampleRate / 1000;
+ const int32_t sharedOffset = audio_utils::safe_sub_overflow(mRsmpInRear,
+ (int32_t)sharedAudioStartFrames);
+ if (sharedOffset < 0
+ || sharedOffset > mRsmpInFrames) {
+ return BAD_VALUE;
+ }
+
+ mSharedAudioPackageName = sharedAudioPackageName;
+ if (mSharedAudioPackageName.empty()) {
+ mSharedAudioSessionId = AUDIO_SESSION_NONE;
+ mSharedAudioStartFrames = -1;
+ } else {
+ mSharedAudioSessionId = sharedSessionId;
+ mSharedAudioStartFrames = (int32_t)sharedAudioStartFrames;
+ }
+ return NO_ERROR;
+}
+
void AudioFlinger::RecordThread::updateMetadata_l()
{
if (mInput == nullptr || mInput->stream == nullptr ||
@@ -8100,6 +8265,10 @@
}
StreamInHalInterface::SinkMetadata metadata;
for (const sp<RecordTrack> &track : mActiveTracks) {
+ // Do not forward PatchRecord metadata to audio HAL
+ if (track->isPatchTrack()) {
+ continue;
+ }
// No track is invalid as this is called after prepareTrack_l in the same critical section
record_track_metadata_v7_t trackMetadata;
trackMetadata.base = {
@@ -8119,6 +8288,7 @@
{
track->terminate();
track->mState = TrackBase::STOPPED;
+
// active tracks are removed by threadLoop()
if (mActiveTracks.indexOf(track) < 0) {
removeTrack_l(track);
@@ -8226,8 +8396,23 @@
{
sp<ThreadBase> threadBase = mRecordTrack->mThread.promote();
RecordThread *recordThread = (RecordThread *) threadBase.get();
- mRsmpInFront = recordThread->mRsmpInRear;
mRsmpInUnrel = 0;
+ const int32_t rear = recordThread->mRsmpInRear;
+ ssize_t deltaFrames = 0;
+ if (mRecordTrack->startFrames() >= 0) {
+ int32_t startFrames = mRecordTrack->startFrames();
+ // Accept a recent wraparound of mRsmpInRear
+ if (startFrames <= rear) {
+ deltaFrames = rear - startFrames;
+ } else {
+ deltaFrames = (int32_t)((int64_t)rear + UINT32_MAX + 1 - startFrames);
+ }
+ // start frame cannot be further in the past than start of resampling buffer
+ if ((size_t) deltaFrames > recordThread->mRsmpInFrames) {
+ deltaFrames = recordThread->mRsmpInFrames;
+ }
+ }
+ mRsmpInFront = audio_utils::safe_sub_overflow(rear, static_cast<int32_t>(deltaFrames));
}
void AudioFlinger::RecordThread::ResamplerBufferProvider::sync(
@@ -8281,6 +8466,7 @@
// FIXME if client not keeping up, discard
LOG_ALWAYS_FATAL_IF(!(0 <= filled && (size_t) filled <= recordThread->mRsmpInFrames));
// 'filled' may be non-contiguous, so return only the first contiguous chunk
+
front &= recordThread->mRsmpInFramesP2 - 1;
size_t part1 = recordThread->mRsmpInFramesP2 - front;
if (part1 > (size_t) filled) {
@@ -8492,31 +8678,10 @@
ALOGV("%p RecordThread params: mChannelCount=%u, mFormat=%#x, mFrameSize=%zu, "
"mBufferSize=%zu, mFrameCount=%zu",
this, mChannelCount, mFormat, mFrameSize, mBufferSize, mFrameCount);
- // This is the formula for calculating the temporary buffer size.
- // With 7 HAL buffers, we can guarantee ability to down-sample the input by ratio of 6:1 to
- // 1 full output buffer, regardless of the alignment of the available input.
- // The value is somewhat arbitrary, and could probably be even larger.
- // A larger value should allow more old data to be read after a track calls start(),
- // without increasing latency.
- //
- // Note this is independent of the maximum downsampling ratio permitted for capture.
- mRsmpInFrames = mFrameCount * 7;
- mRsmpInFramesP2 = roundup(mRsmpInFrames);
- free(mRsmpInBuffer);
- mRsmpInBuffer = NULL;
- // TODO optimize audio capture buffer sizes ...
- // Here we calculate the size of the sliding buffer used as a source
- // for resampling. mRsmpInFramesP2 is currently roundup(mFrameCount * 7).
- // For current HAL frame counts, this is usually 2048 = 40 ms. It would
- // be better to have it derived from the pipe depth in the long term.
- // The current value is higher than necessary. However it should not add to latency.
-
- // Over-allocate beyond mRsmpInFramesP2 to permit a HAL read past end of buffer
- mRsmpInFramesOA = mRsmpInFramesP2 + mFrameCount - 1;
- (void)posix_memalign(&mRsmpInBuffer, 32, mRsmpInFramesOA * mFrameSize);
- // if posix_memalign fails, will segv here.
- memset(mRsmpInBuffer, 0, mRsmpInFramesOA * mFrameSize);
+ // mRsmpInFrames must be 0 before calling resizeInputBuffer_l for the first time
+ mRsmpInFrames = 0;
+ resizeInputBuffer_l(0 /*maxSharedAudioHistoryMs*/);
// AudioRecord mSampleRate and mChannelCount are constant due to AudioRecord API constraints.
// But if thread's mSampleRate or mChannelCount changes, how will that affect active tracks?
@@ -8699,6 +8864,134 @@
}
}
+int32_t AudioFlinger::RecordThread::getOldestFront_l()
+{
+ if (mTracks.size() == 0) {
+ return 0;
+ }
+ int32_t oldestFront = mRsmpInRear;
+ int32_t maxFilled = 0;
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ int32_t front = mTracks[i]->mResamplerBufferProvider->getFront();
+ int32_t filled;
+ if (front <= mRsmpInRear) {
+ filled = mRsmpInRear - front;
+ } else {
+ filled = (int32_t)((int64_t)mRsmpInRear + UINT32_MAX + 1 - front);
+ }
+ if (filled > maxFilled) {
+ oldestFront = front;
+ maxFilled = filled;
+ }
+ }
+ return oldestFront;
+}
+
+void AudioFlinger::RecordThread::updateFronts_l(int32_t offset)
+{
+ if (offset == 0) {
+ return;
+ }
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ int32_t front = mTracks[i]->mResamplerBufferProvider->getFront();
+ front = audio_utils::safe_sub_overflow(front, offset);
+ mTracks[i]->mResamplerBufferProvider->setFront(front);
+ }
+}
+
+void AudioFlinger::RecordThread::resizeInputBuffer_l(int32_t maxSharedAudioHistoryMs)
+{
+ // This is the formula for calculating the temporary buffer size.
+ // With 7 HAL buffers, we can guarantee ability to down-sample the input by ratio of 6:1 to
+ // 1 full output buffer, regardless of the alignment of the available input.
+ // The value is somewhat arbitrary, and could probably be even larger.
+ // A larger value should allow more old data to be read after a track calls start(),
+ // without increasing latency.
+ //
+ // Note this is independent of the maximum downsampling ratio permitted for capture.
+ size_t minRsmpInFrames = mFrameCount * 7;
+
+ // maxSharedAudioHistoryMs != 0 indicates a request to possibly make some part of the audio
+ // capture history available to another client using the same session ID:
+ // dimension the resampler input buffer accordingly.
+
+ // Get oldest client read position: getOldestFront_l() must be called before altering
+ // mRsmpInRear, or mRsmpInFrames
+ int32_t previousFront = getOldestFront_l();
+ size_t previousRsmpInFramesP2 = mRsmpInFramesP2;
+ int32_t previousRear = mRsmpInRear;
+ mRsmpInRear = 0;
+
+ ALOG_ASSERT(maxSharedAudioHistoryMs >= 0
+ && maxSharedAudioHistoryMs <= AudioFlinger::kMaxSharedAudioHistoryMs,
+ "resizeInputBuffer_l() called with invalid max shared history %d",
+ maxSharedAudioHistoryMs);
+ if (maxSharedAudioHistoryMs != 0) {
+ // resizeInputBuffer_l should never be called with a non zero shared history if the
+ // buffer was not already allocated
+ ALOG_ASSERT(mRsmpInBuffer != nullptr && mRsmpInFrames != 0,
+ "resizeInputBuffer_l() called with shared history and unallocated buffer");
+ size_t rsmpInFrames = (size_t)maxSharedAudioHistoryMs * mSampleRate / 1000;
+ // never reduce resampler input buffer size
+ if (rsmpInFrames < mRsmpInFrames) {
+ return;
+ }
+ mRsmpInFrames = rsmpInFrames;
+ }
+ mMaxSharedAudioHistoryMs = maxSharedAudioHistoryMs;
+ // Note: mRsmpInFrames is 0 when called with maxSharedAudioHistoryMs equals to 0 so it is always
+ // initialized
+ if (mRsmpInFrames < minRsmpInFrames) {
+ mRsmpInFrames = minRsmpInFrames;
+ }
+ mRsmpInFramesP2 = roundup(mRsmpInFrames);
+
+ // TODO optimize audio capture buffer sizes ...
+ // Here we calculate the size of the sliding buffer used as a source
+ // for resampling. mRsmpInFramesP2 is currently roundup(mFrameCount * 7).
+ // For current HAL frame counts, this is usually 2048 = 40 ms. It would
+ // be better to have it derived from the pipe depth in the long term.
+ // The current value is higher than necessary. However it should not add to latency.
+
+ // Over-allocate beyond mRsmpInFramesP2 to permit a HAL read past end of buffer
+ mRsmpInFramesOA = mRsmpInFramesP2 + mFrameCount - 1;
+
+ void *rsmpInBuffer;
+ (void)posix_memalign(&rsmpInBuffer, 32, mRsmpInFramesOA * mFrameSize);
+ // if posix_memalign fails, will segv here.
+ memset(rsmpInBuffer, 0, mRsmpInFramesOA * mFrameSize);
+
+ // Copy audio history if any from old buffer before freeing it
+ if (previousRear != 0) {
+ ALOG_ASSERT(mRsmpInBuffer != nullptr,
+ "resizeInputBuffer_l() called with null buffer but frames already read from HAL");
+
+ ssize_t unread = audio_utils::safe_sub_overflow(previousRear, previousFront);
+ previousFront &= previousRsmpInFramesP2 - 1;
+ size_t part1 = previousRsmpInFramesP2 - previousFront;
+ if (part1 > (size_t) unread) {
+ part1 = unread;
+ }
+ if (part1 != 0) {
+ memcpy(rsmpInBuffer, (const uint8_t*)mRsmpInBuffer + previousFront * mFrameSize,
+ part1 * mFrameSize);
+ mRsmpInRear = part1;
+ part1 = unread - part1;
+ if (part1 != 0) {
+ memcpy((uint8_t*)rsmpInBuffer + mRsmpInRear * mFrameSize,
+ (const uint8_t*)mRsmpInBuffer, part1 * mFrameSize);
+ mRsmpInRear += part1;
+ }
+ }
+ // Update front for all clients according to new rear
+ updateFronts_l(audio_utils::safe_sub_overflow(previousRear, mRsmpInRear));
+ } else {
+ mRsmpInRear = 0;
+ }
+ free(mRsmpInBuffer);
+ mRsmpInBuffer = rsmpInBuffer;
+}
+
void AudioFlinger::RecordThread::addPatchTrack(const sp<PatchRecord>& record)
{
Mutex::Autolock _l(mLock);
@@ -8755,6 +9048,11 @@
return mThread->getMmapPosition(position);
}
+status_t AudioFlinger::MmapThreadHandle::getExternalPosition(uint64_t *position,
+ int64_t *timeNanos) {
+ return mThread->getExternalPosition(position, timeNanos);
+}
+
status_t AudioFlinger::MmapThreadHandle::start(const AudioClient& client,
const audio_attributes_t *attr, audio_port_handle_t *handle)
@@ -8790,7 +9088,6 @@
AudioFlinger::MmapThread::~MmapThread()
{
- releaseWakeLock_l();
}
void AudioFlinger::MmapThread::onFirstRef()
@@ -8840,7 +9137,6 @@
return NO_INIT;
}
mStandby = true;
- acquireWakeLock();
return mHalStream->createMmapBuffer(minSizeFrames, info);
}
@@ -8871,7 +9167,7 @@
audio_port_handle_t *handle)
{
ALOGV("%s clientUid %d mStandby %d mPortId %d *handle %d", __FUNCTION__,
- client.clientUid, mStandby, mPortId, *handle);
+ client.identity.uid, mStandby, mPortId, *handle);
if (mHalStream == 0) {
return NO_INIT;
}
@@ -8879,8 +9175,12 @@
status_t ret;
if (*handle == mPortId) {
- // for the first track, reuse portId and session allocated when the stream was opened
- return exitStandby();
+ // For the first track, reuse portId and session allocated when the stream was opened.
+ ret = exitStandby();
+ if (ret == NO_ERROR) {
+ acquireWakeLock();
+ }
+ return ret;
}
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
@@ -8899,8 +9199,7 @@
ret = AudioSystem::getOutputForAttr(&mAttr, &io,
mSessionId,
&stream,
- client.clientPid,
- client.clientUid,
+ client.identity,
&config,
flags,
&deviceId,
@@ -8917,9 +9216,7 @@
ret = AudioSystem::getInputForAttr(&mAttr, &io,
RECORD_RIID_INVALID,
mSessionId,
- client.clientPid,
- client.clientUid,
- client.packageName,
+ client.identity,
&config,
AUDIO_INPUT_FLAG_MMAP_NOIRQ,
&deviceId,
@@ -8959,16 +9256,15 @@
// Given that MmapThread::mAttr is mutable, should a MmapTrack have attributes ?
sp<MmapTrack> track = new MmapTrack(this, attr == nullptr ? mAttr : *attr, mSampleRate, mFormat,
- mChannelMask, mSessionId, isOutput(), client.clientUid,
- client.clientPid, IPCThreadState::self()->getCallingPid(),
- portId);
+ mChannelMask, mSessionId, isOutput(), client.identity,
+ IPCThreadState::self()->getCallingPid(), portId);
if (isOutput()) {
// force volume update when a new track is added
mHalVolFloat = -1.0f;
} else if (!track->isSilenced_l()) {
for (const sp<MmapTrack> &t : mActiveTracks) {
- if (t->isSilenced_l() && t->uid() != client.clientUid)
+ if (t->isSilenced_l() && t->uid() != client.identity.uid)
t->invalidate();
}
}
@@ -9001,6 +9297,7 @@
if (handle == mPortId) {
mHalStream->stop();
+ releaseWakeLock();
return NO_ERROR;
}
@@ -9452,6 +9749,11 @@
return BAD_VALUE;
}
+ if (EffectModule::isHapticGenerator(&desc->type)) {
+ ALOGE("%s(): HapticGenerator is not supported for MmapThread", __func__);
+ return BAD_VALUE;
+ }
+
return NO_ERROR;
}
@@ -9708,6 +10010,20 @@
}
}
+status_t AudioFlinger::MmapPlaybackThread::getExternalPosition(uint64_t *position,
+ int64_t *timeNanos)
+{
+ if (mOutput == nullptr) {
+ return NO_INIT;
+ }
+ struct timespec timestamp;
+ status_t status = mOutput->getPresentationPosition(position, ×tamp);
+ if (status == NO_ERROR) {
+ *timeNanos = timestamp.tv_sec * NANOS_PER_SECOND + timestamp.tv_nsec;
+ }
+ return status;
+}
+
void AudioFlinger::MmapPlaybackThread::dumpInternals_l(int fd, const Vector<String16>& args)
{
MmapThread::dumpInternals_l(fd, args);
@@ -9816,4 +10132,13 @@
}
}
+status_t AudioFlinger::MmapCaptureThread::getExternalPosition(
+ uint64_t *position, int64_t *timeNanos)
+{
+ if (mInput == nullptr) {
+ return NO_INIT;
+ }
+ return mInput->getCapturePosition((int64_t*)position, timeNanos);
+}
+
} // namespace android
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 9f65562..65db986 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -53,6 +53,7 @@
CFG_EVENT_CREATE_AUDIO_PATCH,
CFG_EVENT_RELEASE_AUDIO_PATCH,
CFG_EVENT_UPDATE_OUT_DEVICE,
+ CFG_EVENT_RESIZE_BUFFER
};
class ConfigEventData: public RefBase {
@@ -242,6 +243,28 @@
virtual ~UpdateOutDevicesConfigEvent();
};
+ class ResizeBufferConfigEventData : public ConfigEventData {
+ public:
+ explicit ResizeBufferConfigEventData(int32_t maxSharedAudioHistoryMs) :
+ mMaxSharedAudioHistoryMs(maxSharedAudioHistoryMs) {}
+
+ virtual void dump(char *buffer, size_t size) {
+ snprintf(buffer, size, "mMaxSharedAudioHistoryMs: %d", mMaxSharedAudioHistoryMs);
+ }
+
+ int32_t mMaxSharedAudioHistoryMs;
+ };
+
+ class ResizeBufferConfigEvent : public ConfigEvent {
+ public:
+ explicit ResizeBufferConfigEvent(int32_t maxSharedAudioHistoryMs) :
+ ConfigEvent(CFG_EVENT_RESIZE_BUFFER) {
+ mData = new ResizeBufferConfigEventData(maxSharedAudioHistoryMs);
+ }
+
+ virtual ~ResizeBufferConfigEvent() {}
+ };
+
class PMDeathRecipient : public IBinder::DeathRecipient {
public:
explicit PMDeathRecipient(const wp<ThreadBase>& thread) : mThread(thread) {}
@@ -272,6 +295,7 @@
// Called by AudioFlinger::frameCount(audio_io_handle_t output) and effects,
// and returns the [normal mix] buffer's frame count.
virtual size_t frameCount() const = 0;
+ virtual audio_channel_mask_t hapticChannelMask() const { return AUDIO_CHANNEL_NONE; }
virtual uint32_t latency_l() const { return 0; }
virtual void setVolumeForOutput_l(float left __unused, float right __unused) const {}
@@ -305,6 +329,7 @@
status_t sendReleaseAudioPatchConfigEvent(audio_patch_handle_t handle);
status_t sendUpdateOutDeviceConfigEvent(
const DeviceDescriptorBaseVector& outDevices);
+ void sendResizeBufferConfigEvent_l(int32_t maxSharedAudioHistoryMs);
void processConfigEvents_l();
virtual void cacheParameters_l() = 0;
virtual status_t createAudioPatch_l(const struct audio_patch *patch,
@@ -313,6 +338,9 @@
virtual void updateOutDevices(const DeviceDescriptorBaseVector& outDevices);
virtual void toAudioPortConfig(struct audio_port_config *config) = 0;
+ virtual void resizeInputBuffer_l(int32_t maxSharedAudioHistoryMs);
+
+
// see note at declaration of mStandby, mOutDevice and mInDevice
bool standby() const { return mStandby; }
@@ -348,7 +376,7 @@
sp<EffectHandle> createEffect_l(
const sp<AudioFlinger::Client>& client,
- const sp<IEffectClient>& effectClient,
+ const sp<media::IEffectClient>& effectClient,
int32_t priority,
audio_session_t sessionId,
effect_descriptor_t *desc,
@@ -434,8 +462,10 @@
// the value returned by default implementation is not important as the
// strategy is only meaningful for PlaybackThread which implements this method
- virtual uint32_t getStrategyForSession_l(audio_session_t sessionId __unused)
- { return 0; }
+ virtual product_strategy_t getStrategyForSession_l(
+ audio_session_t sessionId __unused) {
+ return static_cast<product_strategy_t>(0);
+ }
// check if some effects must be suspended/restored when an effect is enabled
// or disabled
@@ -478,6 +508,25 @@
void onEffectEnable(const sp<EffectModule>& effect);
void onEffectDisable();
+ // invalidateTracksForAudioSession_l must be called with holding mLock.
+ virtual void invalidateTracksForAudioSession_l(audio_session_t sessionId __unused) const { }
+ // Invalidate all the tracks with the given audio session.
+ void invalidateTracksForAudioSession(audio_session_t sessionId) const {
+ Mutex::Autolock _l(mLock);
+ invalidateTracksForAudioSession_l(sessionId);
+ }
+
+ template <typename T>
+ void invalidateTracksForAudioSession_l(audio_session_t sessionId,
+ const T& tracks) const {
+ for (size_t i = 0; i < tracks.size(); ++i) {
+ const sp<TrackBase>& track = tracks[i];
+ if (sessionId == track->sessionId()) {
+ track->invalidate();
+ }
+ }
+ }
+
protected:
// entry describing an effect being suspended in mSuspendedSessions keyed vector
@@ -575,7 +624,7 @@
static const int kThreadNameLength = 16; // prctl(PR_SET_NAME) limit
char mThreadName[kThreadNameLength]; // guaranteed NUL-terminated
- sp<IPowerManager> mPowerManager;
+ sp<os::IPowerManager> mPowerManager;
sp<IBinder> mWakeLockToken;
const sp<PMDeathRecipient> mDeathRecipient;
// list of suspended effects per session and per type. The first (outer) vector is
@@ -865,12 +914,11 @@
audio_session_t sessionId,
audio_output_flags_t *flags,
pid_t creatorPid,
+ const media::permission::Identity& identity,
pid_t tid,
- uid_t uid,
status_t *status /*non-NULL*/,
audio_port_handle_t portId,
- const sp<media::IAudioTrackCallback>& callback,
- const std::string& opPackageName);
+ const sp<media::IAudioTrackCallback>& callback);
AudioStreamOut* getOutput() const;
AudioStreamOut* clearOutput();
@@ -909,7 +957,7 @@
uint32_t hasAudioSession_l(audio_session_t sessionId) const override {
return ThreadBase::hasAudioSession_l(sessionId, mTracks);
}
- virtual uint32_t getStrategyForSession_l(audio_session_t sessionId);
+ virtual product_strategy_t getStrategyForSession_l(audio_session_t sessionId);
virtual status_t setSyncEvent(const sp<SyncEvent>& event);
@@ -945,6 +993,13 @@
&& outDeviceTypes().count(mTimestampCorrectedDevice) != 0;
}
+ audio_channel_mask_t hapticChannelMask() const override {
+ return mHapticChannelMask;
+ }
+ bool supportsHapticPlayback() const {
+ return (mHapticChannelMask & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE;
+ }
+
void setDownStreamPatch(const struct audio_patch *patch) {
Mutex::Autolock _l(mLock);
mDownStreamPatch = *patch;
@@ -1082,6 +1137,11 @@
uint32_t trackCountForUid_l(uid_t uid) const;
+ void invalidateTracksForAudioSession_l(
+ audio_session_t sessionId) const override {
+ ThreadBase::invalidateTracksForAudioSession_l(sessionId, mTracks);
+ }
+
private:
friend class AudioFlinger; // for numerous
@@ -1580,6 +1640,9 @@
// AudioBufferProvider interface
virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
+
+ int32_t getFront() const { return mRsmpInFront; }
+ void setFront(int32_t front) { mRsmpInFront = front; }
private:
RecordTrack * const mRecordTrack;
size_t mRsmpInUnrel; // unreleased frames remaining from
@@ -1625,12 +1688,12 @@
audio_session_t sessionId,
size_t *pNotificationFrameCount,
pid_t creatorPid,
- uid_t uid,
+ const media::permission::Identity& identity,
audio_input_flags_t *flags,
pid_t tid,
status_t *status /*non-NULL*/,
audio_port_handle_t portId,
- const String16& opPackageName);
+ int32_t maxSharedAudioHistoryMs);
status_t start(RecordTrack* recordTrack,
AudioSystem::sync_event_t event,
@@ -1654,6 +1717,7 @@
audio_patch_handle_t *handle);
virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle);
void updateOutDevices(const DeviceDescriptorBaseVector& outDevices) override;
+ void resizeInputBuffer_l(int32_t maxSharedAudioHistoryMs) override;
void addPatchTrack(const sp<PatchRecord>& record);
void deletePatchTrack(const sp<PatchRecord>& record);
@@ -1709,6 +1773,13 @@
&& inDeviceType() == mTimestampCorrectedDevice;
}
+ status_t shareAudioHistory(const std::string& sharedAudioPackageName,
+ audio_session_t sharedSessionId = AUDIO_SESSION_NONE,
+ int64_t sharedAudioStartMs = -1);
+ status_t shareAudioHistory_l(const std::string& sharedAudioPackageName,
+ audio_session_t sharedSessionId = AUDIO_SESSION_NONE,
+ int64_t sharedAudioStartMs = -1);
+
protected:
void dumpInternals_l(int fd, const Vector<String16>& args) override;
void dumpTracks_l(int fd, const Vector<String16>& args) override;
@@ -1722,6 +1793,9 @@
void checkBtNrec_l();
+ int32_t getOldestFront_l();
+ void updateFronts_l(int32_t offset);
+
AudioStreamIn *mInput;
Source *mSource;
SortedVector < sp<RecordTrack> > mTracks;
@@ -1787,6 +1861,11 @@
int64_t mFramesRead = 0; // continuous running counter.
DeviceDescriptorBaseVector mOutDevices;
+
+ int32_t mMaxSharedAudioHistoryMs = 0;
+ std::string mSharedAudioPackageName = {};
+ int32_t mSharedAudioStartFrames = -1;
+ audio_session_t mSharedAudioSessionId = AUDIO_SESSION_NONE;
};
class MmapThread : public ThreadBase
@@ -1818,6 +1897,7 @@
audio_port_handle_t *handle);
status_t stop(audio_port_handle_t handle);
status_t standby();
+ virtual status_t getExternalPosition(uint64_t *position, int64_t *timeNaos) = 0;
// RefBase
virtual void onFirstRef();
@@ -1929,6 +2009,8 @@
virtual void toAudioPortConfig(struct audio_port_config *config);
+ status_t getExternalPosition(uint64_t *position, int64_t *timeNanos) override;
+
protected:
void dumpInternals_l(int fd, const Vector<String16>& args) override;
@@ -1959,6 +2041,8 @@
virtual void toAudioPortConfig(struct audio_port_config *config);
+ status_t getExternalPosition(uint64_t *position, int64_t *timeNanos) override;
+
protected:
AudioStreamIn* mInput;
diff --git a/services/audioflinger/TrackMetrics.h b/services/audioflinger/TrackMetrics.h
index af16448..7fb69be 100644
--- a/services/audioflinger/TrackMetrics.h
+++ b/services/audioflinger/TrackMetrics.h
@@ -67,7 +67,7 @@
mIntervalStartTimeNs = systemTime();
}
- void logConstructor(pid_t creatorPid, uid_t creatorUid,
+ void logConstructor(pid_t creatorPid, uid_t creatorUid, int32_t internalTrackId,
const std::string& traits = {},
audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT) const {
// Once this item is logged by the server, the client can add properties.
@@ -78,6 +78,7 @@
.set(AMEDIAMETRICS_PROP_ALLOWUID, (int32_t)creatorUid)
.set(AMEDIAMETRICS_PROP_EVENT,
AMEDIAMETRICS_PROP_PREFIX_SERVER AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR)
+ .set(AMEDIAMETRICS_PROP_INTERNALTRACKID, internalTrackId)
.set(AMEDIAMETRICS_PROP_TRAITS, traits);
// log streamType from the service, since client doesn't know chosen streamType.
if (streamType != AUDIO_STREAM_DEFAULT) {
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 4353b3d..3e04804 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -33,6 +33,7 @@
#include <media/nbaio/Pipe.h>
#include <media/nbaio/PipeReader.h>
+#include <media/AudioValidator.h>
#include <media/RecordBufferConverter.h>
#include <mediautils/ServiceUtilities.h>
#include <audio_utils/minifloat.h>
@@ -52,62 +53,19 @@
#define ALOGVV(a...) do { } while(0)
#endif
+// TODO: Remove when this is put into AidlConversionUtil.h
+#define VALUE_OR_RETURN_BINDER_STATUS(x) \
+ ({ \
+ auto _tmp = (x); \
+ if (!_tmp.ok()) return ::android::aidl_utils::binderStatusFromStatusT(_tmp.error()); \
+ std::move(_tmp.value()); \
+ })
+
namespace android {
-// Validation methods for input
-namespace {
-
-status_t validateAudioDescriptionMixLevel(float leveldB)
-{
- constexpr float MAX_AUDIO_DESCRIPTION_MIX_LEVEL = 48.f;
- return std::isnan(leveldB) || leveldB > MAX_AUDIO_DESCRIPTION_MIX_LEVEL ? BAD_VALUE : OK;
-}
-
-status_t validateDualMonoMode(audio_dual_mono_mode_t dualMonoMode)
-{
- switch (dualMonoMode) {
- case AUDIO_DUAL_MONO_MODE_OFF:
- case AUDIO_DUAL_MONO_MODE_LR:
- case AUDIO_DUAL_MONO_MODE_LL:
- case AUDIO_DUAL_MONO_MODE_RR:
- return OK;
- }
- return BAD_VALUE;
-}
-
-status_t validatePlaybackRateFallbackMode(
- audio_timestretch_fallback_mode_t fallbackMode)
-{
- switch (fallbackMode) {
- case AUDIO_TIMESTRETCH_FALLBACK_CUT_REPEAT:
- break; // warning if not listed.
- case AUDIO_TIMESTRETCH_FALLBACK_DEFAULT:
- case AUDIO_TIMESTRETCH_FALLBACK_MUTE:
- case AUDIO_TIMESTRETCH_FALLBACK_FAIL:
- return OK;
- }
- return BAD_VALUE;
-}
-
-status_t validatePlaybackRateStretchMode(audio_timestretch_stretch_mode_t stretchMode)
-{
- switch (stretchMode) {
- case AUDIO_TIMESTRETCH_STRETCH_DEFAULT:
- case AUDIO_TIMESTRETCH_STRETCH_VOICE:
- return OK;
- }
- return BAD_VALUE;
-}
-
-status_t validatePlaybackRate(const audio_playback_rate_t& playbackRate)
-{
- if (playbackRate.mSpeed < 0.f || playbackRate.mPitch < 0.f) return BAD_VALUE;
- return validatePlaybackRateFallbackMode(playbackRate.mFallbackMode) ?:
- validatePlaybackRateStretchMode(playbackRate.mStretchMode);
-}
-
-} // namespace
-
+using ::android::aidl_utils::binderStatusFromStatusT;
+using binder::Status;
+using media::permission::Identity;
using media::VolumeShaper;
// ----------------------------------------------------------------------------
// TrackBase
@@ -280,6 +238,14 @@
}
}
+// TODO b/182392769: use identity util
+static Identity audioServerIdentity(pid_t pid) {
+ Identity i{};
+ i.uid = AID_AUDIOSERVER;
+ i.pid = pid;
+ return i;
+}
+
status_t AudioFlinger::ThreadBase::TrackBase::initCheck() const
{
status_t status;
@@ -373,97 +339,156 @@
mTrack->destroy();
}
-sp<IMemory> AudioFlinger::TrackHandle::getCblk() const {
- return mTrack->getCblk();
+Status AudioFlinger::TrackHandle::getCblk(
+ std::optional<media::SharedFileRegion>* _aidl_return) {
+ *_aidl_return = legacy2aidl_NullableIMemory_SharedFileRegion(mTrack->getCblk()).value();
+ return Status::ok();
}
-status_t AudioFlinger::TrackHandle::start() {
- return mTrack->start();
+Status AudioFlinger::TrackHandle::start(int32_t* _aidl_return) {
+ *_aidl_return = mTrack->start();
+ return Status::ok();
}
-void AudioFlinger::TrackHandle::stop() {
+Status AudioFlinger::TrackHandle::stop() {
mTrack->stop();
+ return Status::ok();
}
-void AudioFlinger::TrackHandle::flush() {
+Status AudioFlinger::TrackHandle::flush() {
mTrack->flush();
+ return Status::ok();
}
-void AudioFlinger::TrackHandle::pause() {
+Status AudioFlinger::TrackHandle::pause() {
mTrack->pause();
+ return Status::ok();
}
-status_t AudioFlinger::TrackHandle::attachAuxEffect(int EffectId)
+Status AudioFlinger::TrackHandle::attachAuxEffect(int32_t effectId,
+ int32_t* _aidl_return) {
+ *_aidl_return = mTrack->attachAuxEffect(effectId);
+ return Status::ok();
+}
+
+Status AudioFlinger::TrackHandle::setParameters(const std::string& keyValuePairs,
+ int32_t* _aidl_return) {
+ *_aidl_return = mTrack->setParameters(String8(keyValuePairs.c_str()));
+ return Status::ok();
+}
+
+Status AudioFlinger::TrackHandle::selectPresentation(int32_t presentationId, int32_t programId,
+ int32_t* _aidl_return) {
+ *_aidl_return = mTrack->selectPresentation(presentationId, programId);
+ return Status::ok();
+}
+
+Status AudioFlinger::TrackHandle::getTimestamp(media::AudioTimestampInternal* timestamp,
+ int32_t* _aidl_return) {
+ AudioTimestamp legacy;
+ *_aidl_return = mTrack->getTimestamp(legacy);
+ if (*_aidl_return != OK) {
+ return Status::ok();
+ }
+ *timestamp = legacy2aidl_AudioTimestamp_AudioTimestampInternal(legacy).value();
+ return Status::ok();
+}
+
+Status AudioFlinger::TrackHandle::signal() {
+ mTrack->signal();
+ return Status::ok();
+}
+
+Status AudioFlinger::TrackHandle::applyVolumeShaper(
+ const media::VolumeShaperConfiguration& configuration,
+ const media::VolumeShaperOperation& operation,
+ int32_t* _aidl_return) {
+ sp<VolumeShaper::Configuration> conf = new VolumeShaper::Configuration();
+ *_aidl_return = conf->readFromParcelable(configuration);
+ if (*_aidl_return != OK) {
+ return Status::ok();
+ }
+
+ sp<VolumeShaper::Operation> op = new VolumeShaper::Operation();
+ *_aidl_return = op->readFromParcelable(operation);
+ if (*_aidl_return != OK) {
+ return Status::ok();
+ }
+
+ *_aidl_return = mTrack->applyVolumeShaper(conf, op);
+ return Status::ok();
+}
+
+Status AudioFlinger::TrackHandle::getVolumeShaperState(
+ int32_t id,
+ std::optional<media::VolumeShaperState>* _aidl_return) {
+ sp<VolumeShaper::State> legacy = mTrack->getVolumeShaperState(id);
+ if (legacy == nullptr) {
+ _aidl_return->reset();
+ return Status::ok();
+ }
+ media::VolumeShaperState aidl;
+ legacy->writeToParcelable(&aidl);
+ *_aidl_return = aidl;
+ return Status::ok();
+}
+
+Status AudioFlinger::TrackHandle::getDualMonoMode(media::AudioDualMonoMode* _aidl_return)
{
- return mTrack->attachAuxEffect(EffectId);
+ audio_dual_mono_mode_t mode = AUDIO_DUAL_MONO_MODE_OFF;
+ const status_t status = mTrack->getDualMonoMode(&mode)
+ ?: AudioValidator::validateDualMonoMode(mode);
+ if (status == OK) {
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_dual_mono_mode_t_AudioDualMonoMode(mode));
+ }
+ return binderStatusFromStatusT(status);
}
-status_t AudioFlinger::TrackHandle::setParameters(const String8& keyValuePairs) {
- return mTrack->setParameters(keyValuePairs);
-}
-
-status_t AudioFlinger::TrackHandle::selectPresentation(int presentationId, int programId) {
- return mTrack->selectPresentation(presentationId, programId);
-}
-
-VolumeShaper::Status AudioFlinger::TrackHandle::applyVolumeShaper(
- const sp<VolumeShaper::Configuration>& configuration,
- const sp<VolumeShaper::Operation>& operation) {
- return mTrack->applyVolumeShaper(configuration, operation);
-}
-
-sp<VolumeShaper::State> AudioFlinger::TrackHandle::getVolumeShaperState(int id) {
- return mTrack->getVolumeShaperState(id);
-}
-
-status_t AudioFlinger::TrackHandle::getTimestamp(AudioTimestamp& timestamp)
+Status AudioFlinger::TrackHandle::setDualMonoMode(
+ media::AudioDualMonoMode mode)
{
- return mTrack->getTimestamp(timestamp);
+ const auto localMonoMode = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioDualMonoMode_audio_dual_mono_mode_t(mode));
+ return binderStatusFromStatusT(AudioValidator::validateDualMonoMode(localMonoMode)
+ ?: mTrack->setDualMonoMode(localMonoMode));
}
-void AudioFlinger::TrackHandle::signal()
+Status AudioFlinger::TrackHandle::getAudioDescriptionMixLevel(float* _aidl_return)
{
- return mTrack->signal();
+ float leveldB = -std::numeric_limits<float>::infinity();
+ const status_t status = mTrack->getAudioDescriptionMixLevel(&leveldB)
+ ?: AudioValidator::validateAudioDescriptionMixLevel(leveldB);
+ if (status == OK) *_aidl_return = leveldB;
+ return binderStatusFromStatusT(status);
}
-status_t AudioFlinger::TrackHandle::getDualMonoMode(audio_dual_mono_mode_t* mode)
+Status AudioFlinger::TrackHandle::setAudioDescriptionMixLevel(float leveldB)
{
- return mTrack->getDualMonoMode(mode);
+ return binderStatusFromStatusT(AudioValidator::validateAudioDescriptionMixLevel(leveldB)
+ ?: mTrack->setAudioDescriptionMixLevel(leveldB));
}
-status_t AudioFlinger::TrackHandle::setDualMonoMode(audio_dual_mono_mode_t mode)
+Status AudioFlinger::TrackHandle::getPlaybackRateParameters(
+ media::AudioPlaybackRate* _aidl_return)
{
- return validateDualMonoMode(mode) ?: mTrack->setDualMonoMode(mode);
+ audio_playback_rate_t localPlaybackRate{};
+ status_t status = mTrack->getPlaybackRateParameters(&localPlaybackRate)
+ ?: AudioValidator::validatePlaybackRate(localPlaybackRate);
+ if (status == NO_ERROR) {
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_playback_rate_t_AudioPlaybackRate(localPlaybackRate));
+ }
+ return binderStatusFromStatusT(status);
}
-status_t AudioFlinger::TrackHandle::getAudioDescriptionMixLevel(float* leveldB)
+Status AudioFlinger::TrackHandle::setPlaybackRateParameters(
+ const media::AudioPlaybackRate& playbackRate)
{
- return mTrack->getAudioDescriptionMixLevel(leveldB);
-}
-
-status_t AudioFlinger::TrackHandle::setAudioDescriptionMixLevel(float leveldB)
-{
- return validateAudioDescriptionMixLevel(leveldB)
- ?: mTrack->setAudioDescriptionMixLevel(leveldB);
-}
-
-status_t AudioFlinger::TrackHandle::getPlaybackRateParameters(
- audio_playback_rate_t* playbackRate)
-{
- return mTrack->getPlaybackRateParameters(playbackRate);
-}
-
-status_t AudioFlinger::TrackHandle::setPlaybackRateParameters(
- const audio_playback_rate_t& playbackRate)
-{
- return validatePlaybackRate(playbackRate)
- ?: mTrack->setPlaybackRateParameters(playbackRate);
-}
-
-status_t AudioFlinger::TrackHandle::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- return BnAudioTrack::onTransact(code, data, reply, flags);
+ const audio_playback_rate_t localPlaybackRate = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioPlaybackRate_audio_playback_rate_t(playbackRate));
+ return binderStatusFromStatusT(AudioValidator::validatePlaybackRate(localPlaybackRate)
+ ?: mTrack->setPlaybackRateParameters(localPlaybackRate));
}
// ----------------------------------------------------------------------------
@@ -473,10 +498,11 @@
// static
sp<AudioFlinger::PlaybackThread::OpPlayAudioMonitor>
AudioFlinger::PlaybackThread::OpPlayAudioMonitor::createIfNeeded(
- uid_t uid, const audio_attributes_t& attr, int id, audio_stream_type_t streamType,
- const std::string& opPackageName)
+ const Identity& identity, const audio_attributes_t& attr, int id,
+ audio_stream_type_t streamType)
{
Vector <String16> packages;
+ uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
getPackagesForUid(uid, packages);
if (isServiceUid(uid)) {
if (packages.isEmpty()) {
@@ -499,31 +525,13 @@
return nullptr;
}
- String16 opPackageNameStr(opPackageName.c_str());
- if (opPackageName.empty()) {
- // If no package name is provided by the client, use the first associated with the uid
- if (!packages.isEmpty()) {
- opPackageNameStr = packages[0];
- }
- } else {
- // If the provided package name is invalid, we force app ops denial by clearing the package
- // name passed to OpPlayAudioMonitor
- if (std::find_if(packages.begin(), packages.end(),
- [&opPackageNameStr](const auto& package) {
- return opPackageNameStr == package; }) == packages.end()) {
- ALOGW("The package name(%s) provided does not correspond to the uid %d, "
- "force muting the track", opPackageName.c_str(), uid);
- // Set package name as an empty string so that hasOpPlayAudio will always return false.
- opPackageNameStr = String16("");
- }
- }
- return new OpPlayAudioMonitor(uid, attr.usage, id, opPackageNameStr);
+ Identity checkedIdentity = AudioFlinger::checkIdentityPackage(identity);
+ return new OpPlayAudioMonitor(checkedIdentity, attr.usage, id);
}
AudioFlinger::PlaybackThread::OpPlayAudioMonitor::OpPlayAudioMonitor(
- uid_t uid, audio_usage_t usage, int id, const String16& opPackageName)
- : mHasOpPlayAudio(true), mUid(uid), mUsage((int32_t) usage), mId(id),
- mOpPackageName(opPackageName)
+ const Identity& identity, audio_usage_t usage, int id)
+ : mHasOpPlayAudio(true), mIdentity(identity), mUsage((int32_t) usage), mId(id)
{
}
@@ -538,9 +546,11 @@
void AudioFlinger::PlaybackThread::OpPlayAudioMonitor::onFirstRef()
{
checkPlayAudioForUsage();
- if (mOpPackageName.size() != 0) {
+ if (mIdentity.packageName.has_value()) {
mOpCallback = new PlayAudioOpCallback(this);
- mAppOpsManager.startWatchingMode(AppOpsManager::OP_PLAY_AUDIO, mOpPackageName, mOpCallback);
+ mAppOpsManager.startWatchingMode(AppOpsManager::OP_PLAY_AUDIO,
+ VALUE_OR_FATAL(aidl2legacy_string_view_String16(mIdentity.packageName.value_or("")))
+ , mOpCallback);
}
}
@@ -553,11 +563,14 @@
// - not called from PlayAudioOpCallback because the callback is not installed in this case
void AudioFlinger::PlaybackThread::OpPlayAudioMonitor::checkPlayAudioForUsage()
{
- if (mOpPackageName.size() == 0) {
+ if (!mIdentity.packageName.has_value()) {
mHasOpPlayAudio.store(false);
} else {
+ uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(mIdentity.uid));
+ String16 packageName = VALUE_OR_FATAL(
+ aidl2legacy_string_view_String16(mIdentity.packageName.value_or("")));
bool hasIt = mAppOpsManager.checkAudioOpNoThrow(AppOpsManager::OP_PLAY_AUDIO,
- mUsage, mUid, mOpPackageName) == AppOpsManager::MODE_ALLOWED;
+ mUsage, uid, packageName) == AppOpsManager::MODE_ALLOWED;
ALOGD("OpPlayAudio: track:%d usage:%d %smuted", mId, mUsage, hasIt ? "not " : "");
mHasOpPlayAudio.store(hasIt);
}
@@ -607,12 +620,11 @@
const sp<IMemory>& sharedBuffer,
audio_session_t sessionId,
pid_t creatorPid,
- uid_t uid,
+ const Identity& identity,
audio_output_flags_t flags,
track_type type,
audio_port_handle_t portId,
- size_t frameCountToBeReady,
- const std::string opPackageName)
+ size_t frameCountToBeReady)
: TrackBase(thread, client, attr, sampleRate, format, channelMask, frameCount,
// TODO: Using unsecurePointer() has some associated security pitfalls
// (see declaration for details).
@@ -620,7 +632,8 @@
// issue (e.g. by copying).
(sharedBuffer != 0) ? sharedBuffer->unsecurePointer() : buffer,
(sharedBuffer != 0) ? sharedBuffer->size() : bufferSize,
- sessionId, creatorPid, uid, true /*isOut*/,
+ sessionId, creatorPid,
+ VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid)), true /*isOut*/,
(type == TYPE_PATCH) ? ( buffer == NULL ? ALLOC_LOCAL : ALLOC_NONE) : ALLOC_CBLK,
type,
portId,
@@ -635,8 +648,8 @@
mPresentationCompleteFrames(0),
mFrameMap(16 /* sink-frame-to-track-frame map memory */),
mVolumeHandler(new media::VolumeHandler(sampleRate)),
- mOpPlayAudioMonitor(OpPlayAudioMonitor::createIfNeeded(
- uid, attr, id(), streamType, opPackageName)),
+ mOpPlayAudioMonitor(OpPlayAudioMonitor::createIfNeeded(identity, attr, id(),
+ streamType)),
// mSinkTimestamp
mFastIndex(-1),
mCachedVolume(1.0),
@@ -657,6 +670,7 @@
return;
}
+ uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid));
if (!thread->isTrackAllowed_l(channelMask, format, sessionId, uid)) {
ALOGE("%s(%d): no more tracks available", __func__, mId);
releaseCblk(); // this makes the track invalid.
@@ -697,15 +711,20 @@
+ "_" + std::to_string(mId) + "_T");
#endif
- if (channelMask & AUDIO_CHANNEL_HAPTIC_ALL) {
+ if (thread->supportsHapticPlayback()) {
+ // If the track is attached to haptic playback thread, it is potentially to have
+ // HapticGenerator effect, which will generate haptic data, on the track. In that case,
+ // external vibration is always created for all tracks attached to haptic playback thread.
mAudioVibrationController = new AudioVibrationController(this);
+ std::string packageName = identity.packageName.has_value() ?
+ identity.packageName.value() : "";
mExternalVibration = new os::ExternalVibration(
- mUid, opPackageName, mAttr, mAudioVibrationController);
+ mUid, packageName, mAttr, mAudioVibrationController);
}
// Once this item is logged by the server, the client can add properties.
const char * const traits = sharedBuffer == 0 ? "" : "static";
- mTrackMetrics.logConstructor(creatorPid, uid, traits, streamType);
+ mTrackMetrics.logConstructor(creatorPid, uid, id(), traits, streamType);
}
AudioFlinger::PlaybackThread::Track::~Track()
@@ -1813,12 +1832,12 @@
audio_format_t format,
audio_channel_mask_t channelMask,
size_t frameCount,
- uid_t uid)
+ const Identity& identity)
: Track(playbackThread, NULL, AUDIO_STREAM_PATCH,
audio_attributes_t{} /* currently unused for output track */,
sampleRate, format, channelMask, frameCount,
nullptr /* buffer */, (size_t)0 /* bufferSize */, nullptr /* sharedBuffer */,
- AUDIO_SESSION_NONE, getpid(), uid, AUDIO_OUTPUT_FLAG_NONE,
+ AUDIO_SESSION_NONE, getpid(), identity, AUDIO_OUTPUT_FLAG_NONE,
TYPE_OUTPUT),
mActive(false), mSourceThread(sourceThread)
{
@@ -2048,8 +2067,8 @@
audio_attributes_t{} /* currently unused for patch track */,
sampleRate, format, channelMask, frameCount,
buffer, bufferSize, nullptr /* sharedBuffer */,
- AUDIO_SESSION_NONE, getpid(), AID_AUDIOSERVER, flags, TYPE_PATCH,
- AUDIO_PORT_HANDLE_NONE, frameCountToBeReady),
+ AUDIO_SESSION_NONE, getpid(), audioServerIdentity(getpid()), flags,
+ TYPE_PATCH, AUDIO_PORT_HANDLE_NONE, frameCountToBeReady),
PatchTrackBase(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, true, true),
*playbackThread, timeout)
{
@@ -2186,41 +2205,32 @@
// static
sp<AudioFlinger::RecordThread::OpRecordAudioMonitor>
AudioFlinger::RecordThread::OpRecordAudioMonitor::createIfNeeded(
- uid_t uid, const audio_attributes_t& attr, const String16& opPackageName)
+ const Identity& identity, const audio_attributes_t& attr)
{
- if (isServiceUid(uid)) {
- ALOGV("not silencing record for service uid:%d pack:%s",
- uid, String8(opPackageName).string());
+ if (isServiceUid(identity.uid)) {
+ ALOGV("not silencing record for service %s",
+ identity.toString().c_str());
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);
+ ALOGV("not muting FM TUNER capture for uid %d", identity.uid);
return nullptr;
}
- if (opPackageName.size() == 0) {
- Vector<String16> packages;
- // no package name, happens with SL ES clients
- // query package manager to find one
- PermissionController permissionController;
- permissionController.getPackagesForUid(uid, packages);
- if (packages.isEmpty()) {
- return nullptr;
- } else {
- ALOGV("using pack:%s for uid:%d", String8(packages[0]).string(), uid);
- return new OpRecordAudioMonitor(uid, packages[0]);
- }
+ Identity checkedIdentity = AudioFlinger::checkIdentityPackage(identity);
+ if (!checkedIdentity.packageName.has_value()
+ || checkedIdentity.packageName.value().size() == 0) {
+ return nullptr;
}
-
- return new OpRecordAudioMonitor(uid, opPackageName);
+ return new OpRecordAudioMonitor(checkedIdentity);
}
AudioFlinger::RecordThread::OpRecordAudioMonitor::OpRecordAudioMonitor(
- uid_t uid, const String16& opPackageName)
- : mHasOpRecordAudio(true), mUid(uid), mPackage(opPackageName)
+ const Identity& identity)
+ : mHasOpRecordAudio(true), mIdentity(identity)
{
}
@@ -2236,8 +2246,10 @@
{
checkRecordAudio();
mOpCallback = new RecordAudioOpCallback(this);
- ALOGV("start watching OP_RECORD_AUDIO for pack:%s", String8(mPackage).string());
- mAppOpsManager.startWatchingMode(AppOpsManager::OP_RECORD_AUDIO, mPackage, mOpCallback);
+ ALOGV("start watching OP_RECORD_AUDIO for %s", mIdentity.toString().c_str());
+ mAppOpsManager.startWatchingMode(AppOpsManager::OP_RECORD_AUDIO,
+ VALUE_OR_FATAL(aidl2legacy_string_view_String16(mIdentity.packageName.value_or(""))),
+ mOpCallback);
}
bool AudioFlinger::RecordThread::OpRecordAudioMonitor::hasOpRecordAudio() const {
@@ -2252,14 +2264,17 @@
// - not called from RecordAudioOpCallback because the callback is not installed in this case
void AudioFlinger::RecordThread::OpRecordAudioMonitor::checkRecordAudio()
{
+
const int32_t mode = mAppOpsManager.checkOp(AppOpsManager::OP_RECORD_AUDIO,
- mUid, mPackage);
+ mIdentity.uid, VALUE_OR_FATAL(aidl2legacy_string_view_String16(
+ mIdentity.packageName.value_or(""))));
const bool hasIt = (mode == AppOpsManager::MODE_ALLOWED);
// verbose logging only log when appOp changed
ALOGI_IF(hasIt != mHasOpRecordAudio.load(),
- "OP_RECORD_AUDIO missing, %ssilencing record uid%d pack:%s",
- hasIt ? "un" : "", mUid, String8(mPackage).string());
+ "OP_RECORD_AUDIO missing, %ssilencing record %s",
+ hasIt ? "un" : "", mIdentity.toString().c_str());
mHasOpRecordAudio.store(hasIt);
+
}
AudioFlinger::RecordThread::OpRecordAudioMonitor::RecordAudioOpCallback::RecordAudioOpCallback(
@@ -2298,7 +2313,7 @@
binder::Status AudioFlinger::RecordHandle::start(int /*AudioSystem::sync_event_t*/ event,
int /*audio_session_t*/ triggerSession) {
ALOGV("%s()", __func__);
- return binder::Status::fromStatusT(
+ return binderStatusFromStatusT(
mRecordTrack->start((AudioSystem::sync_event_t)event, (audio_session_t) triggerSession));
}
@@ -2313,22 +2328,33 @@
}
binder::Status AudioFlinger::RecordHandle::getActiveMicrophones(
- std::vector<media::MicrophoneInfo>* activeMicrophones) {
+ std::vector<media::MicrophoneInfoData>* activeMicrophones) {
ALOGV("%s()", __func__);
- return binder::Status::fromStatusT(
- mRecordTrack->getActiveMicrophones(activeMicrophones));
+ std::vector<media::MicrophoneInfo> mics;
+ status_t status = mRecordTrack->getActiveMicrophones(&mics);
+ activeMicrophones->resize(mics.size());
+ for (size_t i = 0; status == OK && i < mics.size(); ++i) {
+ status = mics[i].writeToParcelable(&activeMicrophones->at(i));
+ }
+ return binderStatusFromStatusT(status);
}
binder::Status AudioFlinger::RecordHandle::setPreferredMicrophoneDirection(
int /*audio_microphone_direction_t*/ direction) {
ALOGV("%s()", __func__);
- return binder::Status::fromStatusT(mRecordTrack->setPreferredMicrophoneDirection(
+ return binderStatusFromStatusT(mRecordTrack->setPreferredMicrophoneDirection(
static_cast<audio_microphone_direction_t>(direction)));
}
binder::Status AudioFlinger::RecordHandle::setPreferredMicrophoneFieldDimension(float zoom) {
ALOGV("%s()", __func__);
- return binder::Status::fromStatusT(mRecordTrack->setPreferredMicrophoneFieldDimension(zoom));
+ return binderStatusFromStatusT(mRecordTrack->setPreferredMicrophoneFieldDimension(zoom));
+}
+
+binder::Status AudioFlinger::RecordHandle::shareAudioHistory(
+ const std::string& sharedAudioPackageName, int64_t sharedAudioStartMs) {
+ return binderStatusFromStatusT(
+ mRecordTrack->shareAudioHistory(sharedAudioPackageName, sharedAudioStartMs));
}
// ----------------------------------------------------------------------------
@@ -2348,14 +2374,16 @@
size_t bufferSize,
audio_session_t sessionId,
pid_t creatorPid,
- uid_t uid,
+ const Identity& identity,
audio_input_flags_t flags,
track_type type,
- const String16& opPackageName,
- audio_port_handle_t portId)
+ audio_port_handle_t portId,
+ int32_t startFrames)
: TrackBase(thread, client, attr, sampleRate, format,
channelMask, frameCount, buffer, bufferSize, sessionId,
- creatorPid, uid, false /*isOut*/,
+ creatorPid,
+ VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid)),
+ false /*isOut*/,
(type == TYPE_DEFAULT) ?
((flags & AUDIO_INPUT_FLAG_FAST) ? ALLOC_PIPE : ALLOC_CBLK) :
((buffer == NULL) ? ALLOC_LOCAL : ALLOC_NONE),
@@ -2367,7 +2395,8 @@
mRecordBufferConverter(NULL),
mFlags(flags),
mSilenced(false),
- mOpRecordAudioMonitor(OpRecordAudioMonitor::createIfNeeded(uid, attr, opPackageName))
+ mOpRecordAudioMonitor(OpRecordAudioMonitor::createIfNeeded(identity, attr)),
+ mStartFrames(startFrames)
{
if (mCblk == NULL) {
return;
@@ -2408,7 +2437,7 @@
#endif
// Once this item is logged by the server, the client can add properties.
- mTrackMetrics.logConstructor(creatorPid, uid);
+ mTrackMetrics.logConstructor(creatorPid, uid(), id());
}
AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
@@ -2477,6 +2506,9 @@
Mutex::Autolock _l(thread->mLock);
RecordThread *recordThread = (RecordThread *) thread.get();
priorState = mState;
+ if (!mSharedAudioPackageName.empty()) {
+ recordThread->shareAudioHistory_l("");
+ }
recordThread->destroyTrack_l(this); // move mState to STOPPED, terminate
}
// APM portid/client management done outside of lock.
@@ -2663,6 +2695,37 @@
}
}
+status_t AudioFlinger::RecordThread::RecordTrack::shareAudioHistory(
+ const std::string& sharedAudioPackageName, int64_t sharedAudioStartMs) {
+
+ const uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ const pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ if (callingUid != mUid || callingPid != mCreatorPid) {
+ return PERMISSION_DENIED;
+ }
+
+ Identity identity{};
+ identity.uid = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(callingUid));
+ identity.pid = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(callingPid));
+ if (!captureHotwordAllowed(identity)) {
+ return PERMISSION_DENIED;
+ }
+
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ RecordThread *recordThread = (RecordThread *)thread.get();
+ status_t status = recordThread->shareAudioHistory(
+ sharedAudioPackageName, mSessionId, sharedAudioStartMs);
+ if (status == NO_ERROR) {
+ mSharedAudioPackageName = sharedAudioPackageName;
+ }
+ return status;
+ } else {
+ return BAD_VALUE;
+ }
+}
+
+
// ----------------------------------------------------------------------------
#undef LOG_TAG
#define LOG_TAG "AF::PatchRecord"
@@ -2679,8 +2742,8 @@
: RecordTrack(recordThread, NULL,
audio_attributes_t{} /* currently unused for patch track */,
sampleRate, format, channelMask, frameCount,
- buffer, bufferSize, AUDIO_SESSION_NONE, getpid(), AID_AUDIOSERVER,
- flags, TYPE_PATCH, String16()),
+ buffer, bufferSize, AUDIO_SESSION_NONE, getpid(),
+ audioServerIdentity(getpid()), flags, TYPE_PATCH),
PatchTrackBase(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, false, true),
*recordThread, timeout)
{
@@ -2957,21 +3020,23 @@
audio_channel_mask_t channelMask,
audio_session_t sessionId,
bool isOut,
- uid_t uid,
- pid_t pid,
+ const Identity& identity,
pid_t creatorPid,
audio_port_handle_t portId)
: TrackBase(thread, NULL, attr, sampleRate, format,
channelMask, (size_t)0 /* frameCount */,
nullptr /* buffer */, (size_t)0 /* bufferSize */,
- sessionId, creatorPid, uid, isOut,
+ sessionId, creatorPid,
+ VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.uid)),
+ isOut,
ALLOC_NONE,
TYPE_DEFAULT, portId,
std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_MMAP) + std::to_string(portId)),
- mPid(pid), mSilenced(false), mSilencedNotified(false)
+ mPid(VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(identity.pid))),
+ mSilenced(false), mSilencedNotified(false)
{
// Once this item is logged by the server, the client can add properties.
- mTrackMetrics.logConstructor(creatorPid, uid);
+ mTrackMetrics.logConstructor(creatorPid, uid(), id());
}
AudioFlinger::MmapThread::MmapTrack::~MmapTrack()
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 93819f5..5f052a5 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -21,6 +21,7 @@
#include <media/AudioSystem.h>
#include <media/AudioPolicy.h>
#include <media/DeviceDescriptorBase.h>
+#include <android/media/permission/Identity.h>
#include <utils/String8.h>
namespace android {
@@ -122,7 +123,7 @@
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
- uid_t uid,
+ const media::permission::Identity& identity,
const audio_config_t *config,
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
@@ -133,15 +134,15 @@
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.
virtual status_t stopOutput(audio_port_handle_t portId) = 0;
- // releases the output.
- virtual void releaseOutput(audio_port_handle_t portId) = 0;
+ // releases the output, return true if the output descriptor is reopened.
+ virtual bool releaseOutput(audio_port_handle_t portId) = 0;
// request an input appropriate for record from the supplied device with supplied parameters.
virtual status_t getInputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *input,
audio_unique_id_t riid,
audio_session_t session,
- uid_t uid,
+ const media::permission::Identity& identity,
const audio_config_base_t *config,
audio_input_flags_t flags,
audio_port_handle_t *selectedDeviceId,
@@ -191,7 +192,7 @@
int &index) = 0;
// return the strategy corresponding to a given stream type
- virtual uint32_t getStrategyForStream(audio_stream_type_t stream) = 0;
+ virtual product_strategy_t getStrategyForStream(audio_stream_type_t stream) = 0;
// return the enabled output devices for the given stream type
virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream) = 0;
@@ -204,7 +205,7 @@
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,
- uint32_t strategy,
+ product_strategy_t strategy,
int session,
int id) = 0;
virtual status_t unregisterEffect(int id) = 0;
@@ -220,16 +221,16 @@
virtual status_t dump(int fd) = 0;
virtual status_t setAllowedCapturePolicy(uid_t uid, audio_flags_mask_t flags) = 0;
- virtual bool isOffloadSupported(const audio_offload_info_t& offloadInfo) = 0;
+ virtual audio_offload_mode_t getOffloadSupport(const audio_offload_info_t& offloadInfo) = 0;
virtual bool isDirectOutputSupported(const audio_config_base_t& config,
const audio_attributes_t& attributes) = 0;
virtual status_t listAudioPorts(audio_port_role_t role,
audio_port_type_t type,
unsigned int *num_ports,
- struct audio_port *ports,
+ struct audio_port_v7 *ports,
unsigned int *generation) = 0;
- virtual status_t getAudioPort(struct audio_port *port) = 0;
+ virtual status_t getAudioPort(struct audio_port_v7 *port) = 0;
virtual status_t createAudioPatch(const struct audio_patch *patch,
audio_patch_handle_t *handle,
uid_t uid) = 0;
@@ -272,8 +273,11 @@
virtual status_t getSurroundFormats(unsigned int *numSurroundFormats,
audio_format_t *surroundFormats,
- bool *surroundFormatsEnabled,
- bool reported) = 0;
+ bool *surroundFormatsEnabled) = 0;
+
+ virtual status_t getReportedSurroundFormats(unsigned int *numSurroundFormats,
+ audio_format_t *surroundFormats) = 0;
+
virtual status_t setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled) = 0;
virtual bool isHapticPlaybackSupported() = 0;
@@ -285,13 +289,14 @@
virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) = 0;
- virtual status_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa,
- product_strategy_t &productStrategy) = 0;
+ virtual status_t getProductStrategyFromAudioAttributes(
+ const AudioAttributes &aa, product_strategy_t &productStrategy,
+ bool fallbackOnDefault) = 0;
virtual status_t listAudioVolumeGroups(AudioVolumeGroupVector &groups) = 0;
- virtual status_t getVolumeGroupFromAudioAttributes(const AudioAttributes &aa,
- volume_group_t &volumeGroup) = 0;
+ virtual status_t getVolumeGroupFromAudioAttributes(
+ const AudioAttributes &aa, volume_group_t &volumeGroup, bool fallbackOnDefault) = 0;
virtual bool isCallScreenModeSupported() = 0;
@@ -439,16 +444,22 @@
audio_patch_handle_t patchHandle,
audio_source_t source) = 0;
+ virtual void onRoutingUpdated() = 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;
+
+ virtual status_t getAudioPort(struct audio_port_v7 *port) = 0;
};
-extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface);
-extern "C" void destroyAudioPolicyManager(AudioPolicyInterface *interface);
-
+ // These are the signatures of createAudioPolicyManager/destroyAudioPolicyManager
+ // methods respectively, expected by AudioPolicyService, needs to be exposed by
+ // libaudiopolicymanagercustom.
+ using CreateAudioPolicyManagerInstance = AudioPolicyInterface* (*)(AudioPolicyClientInterface*);
+ using DestroyAudioPolicyManagerInstance = void (*)(AudioPolicyInterface*);
} // namespace android
diff --git a/services/audiopolicy/common/include/policy.h b/services/audiopolicy/common/include/policy.h
index 0537365..552919d 100644
--- a/services/audiopolicy/common/include/policy.h
+++ b/services/audiopolicy/common/include/policy.h
@@ -226,6 +226,8 @@
return AUDIO_DEVICE_OUT_SPEAKER_SAFE;
} else if (deviceTypes.count(AUDIO_DEVICE_OUT_HDMI_ARC) != 0) {
return AUDIO_DEVICE_OUT_HDMI_ARC;
+ } else if (deviceTypes.count(AUDIO_DEVICE_OUT_HDMI_EARC) != 0) {
+ return AUDIO_DEVICE_OUT_HDMI_EARC;
} else if (deviceTypes.count(AUDIO_DEVICE_OUT_AUX_LINE) != 0) {
return AUDIO_DEVICE_OUT_AUX_LINE;
} else if (deviceTypes.count(AUDIO_DEVICE_OUT_SPDIF) != 0) {
@@ -240,4 +242,4 @@
return a2dpDevices.empty() ? AUDIO_DEVICE_NONE : a2dpDevices[0];
}
}
-}
\ No newline at end of file
+}
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
index 6f47abc..a40f6aa 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
@@ -72,7 +72,7 @@
const struct audio_port_config *srcConfig = NULL) const;
virtual sp<AudioPort> getAudioPort() const { return mProfile; }
- void toAudioPort(struct audio_port *port) const;
+ void toAudioPort(struct audio_port_v7 *port) const;
void setPreemptedSessions(const SortedVector<audio_session_t>& sessions);
SortedVector<audio_session_t> getPreemptedSessions() const;
bool hasPreemptedSession(audio_session_t session) const;
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
index 1d9223e..1f9b535 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
@@ -182,6 +182,7 @@
* Active ref count of the client will be incremented/decremented through setActive API
*/
virtual void setClientActive(const sp<TrackClientDescriptor>& client, bool active);
+ bool isClientActive(const sp<TrackClientDescriptor>& client);
bool isActive(uint32_t inPastMs) const;
bool isActive(VolumeSource volumeSource = VOLUME_SOURCE_NONE,
@@ -260,7 +261,7 @@
const struct audio_port_config *srcConfig = NULL) const;
virtual sp<AudioPort> getAudioPort() const { return mPolicyAudioPort->asAudioPort(); }
- virtual void toAudioPort(struct audio_port *port) const;
+ virtual void toAudioPort(struct audio_port_v7 *port) const;
audio_module_handle_t getModuleHandle() const;
@@ -337,6 +338,8 @@
bool sharesHwModuleWith(const sp<SwAudioOutputDescriptor>& outputDesc);
virtual DeviceVector supportedDevices() const;
virtual bool devicesSupportEncodedFormats(const DeviceTypeSet& deviceTypes);
+ virtual bool containsSingleDeviceSupportingEncodedFormats(
+ const sp<DeviceDescriptor>& device) const;
virtual uint32_t latency();
virtual bool isDuplicated() const { return (mOutput1 != NULL && mOutput2 != NULL); }
virtual bool isFixedVolume(const DeviceTypeSet& deviceTypes);
@@ -357,7 +360,7 @@
virtual void toAudioPortConfig(struct audio_port_config *dstConfig,
const struct audio_port_config *srcConfig = NULL) const;
- virtual void toAudioPort(struct audio_port *port) const;
+ virtual void toAudioPort(struct audio_port_v7 *port) const;
status_t open(const audio_config_t *config,
const DeviceVector &devices,
@@ -395,6 +398,14 @@
bool supportsAllDevices(const DeviceVector &devices) const;
/**
+ * @brief supportsDevicesForPlayback
+ * @param devices to be checked against
+ * @return true if the devices is a supported combo for playback
+ * false otherwise
+ */
+ bool supportsDevicesForPlayback(const DeviceVector &devices) const;
+
+ /**
* @brief filterSupportedDevices takes a vector of devices and filters them according to the
* device supported by this output (the profile from which this output derives from)
* @param devices reference device vector to be filtered
@@ -411,6 +422,7 @@
sp<SwAudioOutputDescriptor> mOutput2; // used by duplicated outputs: second output
uint32_t mDirectOpenCount; // number of clients using this output (direct outputs only)
audio_session_t mDirectClientSession; // session id of the direct output client
+ bool mPendingReopenToQueryProfiles = false;
};
// Audio output driven by an input device directly.
@@ -431,7 +443,7 @@
virtual void toAudioPortConfig(struct audio_port_config *dstConfig,
const struct audio_port_config *srcConfig = NULL) const;
- virtual void toAudioPort(struct audio_port *port) const;
+ virtual void toAudioPort(struct audio_port_v7 *port) const;
const sp<SourceClientDescriptor> mSource;
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
index c6bdb04..c2a20c6 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
@@ -77,6 +77,7 @@
sp<DeviceDescriptor> getDeviceAndMixForInputSource(audio_source_t inputSource,
const DeviceVector &availableDeviceTypes,
+ uid_t uid,
sp<AudioPolicyMix> *policyMix) const;
/**
diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
index ce97748..59876c6 100644
--- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
@@ -113,6 +113,9 @@
const sp<AudioPolicyMix> getPrimaryMix() const {
return mPrimaryMix.promote();
};
+ bool hasLostPrimaryMix() const {
+ return mPrimaryMix.unsafe_get() && !mPrimaryMix.promote();
+ }
void setActive(bool active) override
{
diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
index 2038aa9..20b4044 100644
--- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
@@ -88,6 +88,7 @@
// AudioPort
virtual void toAudioPort(struct audio_port *port) const;
+ virtual void toAudioPort(struct audio_port_v7 *port) const;
void importAudioPortAndPickAudioProfile(const sp<PolicyAudioPort>& policyPort,
bool force = false);
@@ -97,6 +98,13 @@
void dump(String8 *dst, int spaces, int index, bool verbose = true) const;
private:
+ template <typename T, std::enable_if_t<std::is_same<T, struct audio_port>::value
+ || std::is_same<T, struct audio_port_v7>::value, int> = 0>
+ void toAudioPortInternal(T* port) const {
+ DeviceDescriptorBase::toAudioPort(port);
+ port->ext.device.hw_module = getModuleHandle();
+ }
+
std::string mTagName; // Unique human readable identifier for a device port found in conf file.
FormatVector mEncodedFormats;
audio_format_t mCurrentEncodedFormat;
@@ -264,15 +272,21 @@
return String8("");
}
+ const AudioProfileVector& getSupportedProfiles() { return mSupportedProfiles; }
+
// Return a string to describe the DeviceVector. The sensitive information will only be
// added to the string if `includeSensitiveInfo` is true.
std::string toString(bool includeSensitiveInfo = false) const;
void dump(String8 *dst, const String8 &tag, int spaces = 0, bool verbose = true) const;
+protected:
+ int do_compare(const void* lhs, const void* rhs) const;
private:
void refreshTypes();
+ void refreshAudioProfiles();
DeviceTypeSet mDeviceTypes;
+ AudioProfileVector mSupportedProfiles;
};
} // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
index c4eab30..59eee52 100644
--- a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
@@ -72,6 +72,9 @@
audio_io_handle_t dstOutput);
void moveEffects(const std::vector<int>& ids, audio_io_handle_t dstOutput);
+ audio_io_handle_t getIoForSession(audio_session_t sessionId,
+ const effect_uuid_t *effectType = nullptr);
+
void dump(String8 *dst, int spaces = 0, bool verbose = true) const;
private:
diff --git a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h
index 11d3a99..a74cefa 100644
--- a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h
+++ b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h
@@ -144,7 +144,7 @@
bool devicesSupportEncodedFormats(DeviceTypeSet deviceTypes) const
{
if (deviceTypes.empty()) {
- return true; // required for isOffloadSupported() check
+ return true; // required for getOffloadSupport() check
}
DeviceVector deviceList =
mSupportedDevices.getDevicesFromTypes(deviceTypes);
@@ -156,6 +156,8 @@
return false;
}
+ bool containsSingleDeviceSupportingEncodedFormats(const sp<DeviceDescriptor>& device) const;
+
void clearSupportedDevices() { mSupportedDevices.clear(); }
void addSupportedDevice(const sp<DeviceDescriptor> &device)
{
diff --git a/services/audiopolicy/common/managerdefinitions/include/PolicyAudioPort.h b/services/audiopolicy/common/managerdefinitions/include/PolicyAudioPort.h
index e6eef24..ab33b38 100644
--- a/services/audiopolicy/common/managerdefinitions/include/PolicyAudioPort.h
+++ b/services/audiopolicy/common/managerdefinitions/include/PolicyAudioPort.h
@@ -44,7 +44,7 @@
bool equals(const sp<PolicyAudioPort> &right) const
{
- return getTagName() == right->getTagName();
+ return right != 0 && getTagName() == right->getTagName();
}
virtual sp<AudioPort> asAudioPort() const = 0;
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
index 4922ebe..7016a08 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
@@ -92,7 +92,7 @@
dstConfig->ext.mix.usecase.source = source();
}
-void AudioInputDescriptor::toAudioPort(struct audio_port *port) const
+void AudioInputDescriptor::toAudioPort(struct audio_port_v7 *port) const
{
ALOG_ASSERT(mProfile != 0, "toAudioPort() called on input with null profile %d", mIoHandle);
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index 5c47d1b..6b08f7c 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -123,6 +123,12 @@
client->setActive(active);
}
+bool AudioOutputDescriptor::isClientActive(const sp<TrackClientDescriptor>& client)
+{
+ return client != nullptr &&
+ std::find(begin(mActiveClients), end(mActiveClients), client) != end(mActiveClients);
+}
+
bool AudioOutputDescriptor::isActive(VolumeSource vs, uint32_t inPastMs, nsecs_t sysTime) const
{
return (vs == VOLUME_SOURCE_NONE) ?
@@ -209,7 +215,7 @@
dstConfig->ext.mix.usecase.stream = AUDIO_STREAM_DEFAULT;
}
-void AudioOutputDescriptor::toAudioPort(struct audio_port *port) const
+void AudioOutputDescriptor::toAudioPort(struct audio_port_v7 *port) const
{
// Should not be called for duplicated ports, see SwAudioOutputDescriptor::toAudioPortConfig.
mPolicyAudioPort->asAudioPort()->toAudioPort(port);
@@ -338,6 +344,13 @@
return supportedDevices().containsAllDevices(devices);
}
+bool SwAudioOutputDescriptor::supportsDevicesForPlayback(const DeviceVector &devices) const
+{
+ // No considering duplicated output
+ // TODO: need to verify if the profile supports the devices combo for playback.
+ return !isDuplicated() && supportsAllDevices(devices);
+}
+
DeviceVector SwAudioOutputDescriptor::filterSupportedDevices(const DeviceVector &devices) const
{
DeviceVector filteredDevices = supportedDevices();
@@ -354,6 +367,16 @@
}
}
+bool SwAudioOutputDescriptor::containsSingleDeviceSupportingEncodedFormats(
+ const sp<DeviceDescriptor>& device) const
+{
+ if (isDuplicated()) {
+ return (mOutput1->containsSingleDeviceSupportingEncodedFormats(device) &&
+ mOutput2->containsSingleDeviceSupportingEncodedFormats(device));
+ }
+ return mProfile->containsSingleDeviceSupportingEncodedFormats(device);
+}
+
uint32_t SwAudioOutputDescriptor::latency()
{
if (isDuplicated()) {
@@ -400,8 +423,7 @@
dstConfig->ext.mix.handle = mIoHandle;
}
-void SwAudioOutputDescriptor::toAudioPort(
- struct audio_port *port) const
+void SwAudioOutputDescriptor::toAudioPort(struct audio_port_v7 *port) const
{
ALOG_ASSERT(!isDuplicated(), "toAudioPort() called on duplicated output %d", mIoHandle);
@@ -651,8 +673,7 @@
mSource->srcDevice()->toAudioPortConfig(dstConfig, srcConfig);
}
-void HwAudioOutputDescriptor::toAudioPort(
- struct audio_port *port) const
+void HwAudioOutputDescriptor::toAudioPort(struct audio_port_v7 *port) const
{
mSource->srcDevice()->toAudioPort(port);
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index fc1d0e2..b209a88 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -208,24 +208,25 @@
// Loopback render mixes are created from a public API and thus restricted
// to non sensible audio that have not opted out.
if (is_mix_loopback_render(mix->mRouteFlags)) {
- auto hasFlag = [](auto flags, auto flag) { return (flags & flag) == flag; };
- if (hasFlag(attributes.flags, AUDIO_FLAG_NO_SYSTEM_CAPTURE)) {
- return MixMatchStatus::NO_MATCH;
- }
- if (!mix->mAllowPrivilegedPlaybackCapture &&
- hasFlag(attributes.flags, AUDIO_FLAG_NO_MEDIA_PROJECTION)) {
- return MixMatchStatus::NO_MATCH;
- }
- if (attributes.usage == AUDIO_USAGE_VOICE_COMMUNICATION &&
- !mix->mVoiceCommunicationCaptureAllowed) {
- return MixMatchStatus::NO_MATCH;
- }
if (!(attributes.usage == AUDIO_USAGE_UNKNOWN ||
attributes.usage == AUDIO_USAGE_MEDIA ||
attributes.usage == AUDIO_USAGE_GAME ||
attributes.usage == AUDIO_USAGE_VOICE_COMMUNICATION)) {
return MixMatchStatus::NO_MATCH;
}
+ auto hasFlag = [](auto flags, auto flag) { return (flags & flag) == flag; };
+ if (hasFlag(attributes.flags, AUDIO_FLAG_NO_SYSTEM_CAPTURE)) {
+ return MixMatchStatus::NO_MATCH;
+ }
+
+ if (attributes.usage == AUDIO_USAGE_VOICE_COMMUNICATION) {
+ if (!mix->mVoiceCommunicationCaptureAllowed) {
+ return MixMatchStatus::NO_MATCH;
+ }
+ } else if (!mix->mAllowPrivilegedMediaPlaybackCapture &&
+ hasFlag(attributes.flags, AUDIO_FLAG_NO_MEDIA_PROJECTION)) {
+ return MixMatchStatus::NO_MATCH;
+ }
}
int userId = (int) multiuser_get_user_id(uid);
@@ -390,6 +391,7 @@
sp<DeviceDescriptor> AudioPolicyMixCollection::getDeviceAndMixForInputSource(
audio_source_t inputSource,
const DeviceVector &availDevices,
+ uid_t uid,
sp<AudioPolicyMix> *policyMix) const
{
for (size_t i = 0; i < size(); i++) {
@@ -401,7 +403,11 @@
if ((RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
mix->mCriteria[j].mValue.mSource == inputSource) ||
(RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
- mix->mCriteria[j].mValue.mSource != inputSource)) {
+ mix->mCriteria[j].mValue.mSource != inputSource) ||
+ (RULE_MATCH_UID == mix->mCriteria[j].mRule &&
+ mix->mCriteria[j].mValue.mUid == uid) ||
+ (RULE_EXCLUDE_UID == mix->mCriteria[j].mRule &&
+ mix->mCriteria[j].mValue.mUid != uid)) {
// assuming PolicyMix only for remote submix for input
// so mix->mDeviceType can only be AUDIO_DEVICE_OUT_REMOTE_SUBMIX
audio_devices_t device = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioRoute.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioRoute.cpp
index c8e4e76..866417e 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioRoute.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioRoute.cpp
@@ -39,7 +39,7 @@
bool AudioRoute::supportsPatch(const sp<PolicyAudioPort> &srcPort,
const sp<PolicyAudioPort> &dstPort) const
{
- if (mSink == 0 || dstPort == 0 || !dstPort->equals(mSink)) {
+ if (mSink == 0 || srcPort == 0 || dstPort == 0 || !dstPort->equals(mSink)) {
return false;
}
ALOGV("%s: sinks %s matching", __FUNCTION__, mSink->getTagName().c_str());
diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
index f6859c7..a92d31e 100644
--- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
@@ -162,8 +162,12 @@
void DeviceDescriptor::toAudioPort(struct audio_port *port) const
{
ALOGV("DeviceDescriptor::toAudioPort() handle %d type %08x", mId, mDeviceTypeAddr.mType);
- DeviceDescriptorBase::toAudioPort(port);
- port->ext.device.hw_module = getModuleHandle();
+ toAudioPortInternal(port);
+}
+
+void DeviceDescriptor::toAudioPort(struct audio_port_v7 *port) const {
+ ALOGV("DeviceDescriptor::toAudioPort() v7 handle %d type %08x", mId, mDeviceTypeAddr.mType);
+ toAudioPortInternal(port);
}
void DeviceDescriptor::importAudioPortAndPickAudioProfile(
@@ -220,6 +224,18 @@
ALOGV("DeviceVector::refreshTypes() mDeviceTypes %s", dumpDeviceTypes(mDeviceTypes).c_str());
}
+void DeviceVector::refreshAudioProfiles() {
+ if (empty()) {
+ mSupportedProfiles.clear();
+ return;
+ }
+ mSupportedProfiles = itemAt(0)->getAudioProfiles();
+ for (size_t i = 1; i < size(); ++i) {
+ mSupportedProfiles = intersectAudioProfiles(
+ mSupportedProfiles, itemAt(i)->getAudioProfiles());
+ }
+}
+
ssize_t DeviceVector::indexOf(const sp<DeviceDescriptor>& item) const
{
for (size_t i = 0; i < size(); i++) {
@@ -234,23 +250,27 @@
{
bool added = false;
for (const auto& device : devices) {
+ ALOG_ASSERT(device != nullptr, "Null pointer found when adding DeviceVector");
if (indexOf(device) < 0 && SortedVector::add(device) >= 0) {
added = true;
}
}
if (added) {
refreshTypes();
+ refreshAudioProfiles();
}
}
ssize_t DeviceVector::add(const sp<DeviceDescriptor>& item)
{
+ ALOG_ASSERT(item != nullptr, "Adding null pointer to DeviceVector");
ssize_t ret = indexOf(item);
if (ret < 0) {
ret = SortedVector::add(item);
if (ret >= 0) {
refreshTypes();
+ refreshAudioProfiles();
}
} else {
ALOGW("DeviceVector::add device %08x already in", item->type());
@@ -259,6 +279,23 @@
return ret;
}
+int DeviceVector::do_compare(const void* lhs, const void* rhs) const {
+ const auto ldevice = *reinterpret_cast<const sp<DeviceDescriptor>*>(lhs);
+ const auto rdevice = *reinterpret_cast<const sp<DeviceDescriptor>*>(rhs);
+ int ret = 0;
+
+ // sort by type.
+ ret = compare_type(ldevice->type(), rdevice->type());
+ if (ret != 0)
+ return ret;
+ // for same type higher priority for latest device.
+ ret = compare_type(rdevice->getId(), ldevice->getId());
+ if (ret != 0)
+ return ret;
+ // fallback to default sort using pointer address
+ return SortedVector::do_compare(lhs, rhs);
+}
+
ssize_t DeviceVector::remove(const sp<DeviceDescriptor>& item)
{
ssize_t ret = indexOf(item);
@@ -269,6 +306,7 @@
ret = SortedVector::removeAt(ret);
if (ret >= 0) {
refreshTypes();
+ refreshAudioProfiles();
}
}
return ret;
diff --git a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
index 415962a..843f5da 100644
--- a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
@@ -202,6 +202,19 @@
}
}
+audio_io_handle_t EffectDescriptorCollection::getIoForSession(audio_session_t sessionId,
+ const effect_uuid_t *effectType)
+{
+ for (size_t i = 0; i < size(); ++i) {
+ sp<EffectDescriptor> effect = valueAt(i);
+ if (effect->mSession == sessionId && (effectType == nullptr ||
+ memcmp(&effect->mDesc.type, effectType, sizeof(effect_uuid_t)) == 0)) {
+ return effect->mIo;
+ }
+ }
+ return AUDIO_IO_HANDLE_NONE;
+}
+
EffectDescriptorCollection EffectDescriptorCollection::getEffectsForIo(audio_io_handle_t io) const
{
EffectDescriptorCollection effects;
diff --git a/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp b/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp
index ae92b40..09b614d 100644
--- a/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp
@@ -105,6 +105,17 @@
return true;
}
+bool IOProfile::containsSingleDeviceSupportingEncodedFormats(
+ const sp<DeviceDescriptor>& device) const {
+ if (device == nullptr) {
+ return false;
+ }
+ DeviceVector deviceList = mSupportedDevices.getDevicesFromType(device->type());
+ return std::count_if(deviceList.begin(), deviceList.end(),
+ [&device](sp<DeviceDescriptor> deviceDesc) {
+ return device == deviceDesc && deviceDesc->hasCurrentEncodedFormat(); }) == 1;
+}
+
void IOProfile::dump(String8 *dst) const
{
std::string portStr;
diff --git a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
index 562c213..84ed656 100644
--- a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
@@ -253,6 +253,18 @@
// Children: ModulesTraits, VolumeTraits, SurroundSoundTraits (optional)
};
+// Deleter using free() for use with std::unique_ptr<>. See also UniqueCPtr<> below.
+struct FreeDelete {
+ // NOTE: Deleting a const object is valid but free() takes a non-const pointer.
+ void operator()(const void* ptr) const {
+ free(const_cast<void*>(ptr));
+ }
+};
+
+// Alias for std::unique_ptr<> that uses the C function free() to delete objects.
+template <typename T>
+using UniqueCPtr = std::unique_ptr<T, FreeDelete>;
+
template <class T>
constexpr void (*xmlDeleter)(T* t);
template <>
@@ -608,7 +620,7 @@
}
// Tokenize and Convert Sources name to port pointer
PolicyAudioPortVector sources;
- std::unique_ptr<char[]> sourcesLiteral{strndup(
+ UniqueCPtr<char> sourcesLiteral{strndup(
sourcesAttr.c_str(), strlen(sourcesAttr.c_str()))};
char *devTag = strtok(sourcesLiteral.get(), ",");
while (devTag != NULL) {
diff --git a/services/audiopolicy/engine/common/include/EngineBase.h b/services/audiopolicy/engine/common/include/EngineBase.h
index 4510f63..0f8b0a5 100644
--- a/services/audiopolicy/engine/common/include/EngineBase.h
+++ b/services/audiopolicy/engine/common/include/EngineBase.h
@@ -53,7 +53,7 @@
audio_policy_dev_state_t /*state*/) override;
product_strategy_t getProductStrategyForAttributes(
- const audio_attributes_t &attr) const override;
+ const audio_attributes_t &attr, bool fallbackOnDefault = true) const override;
audio_stream_type_t getStreamTypeForAttributes(const audio_attributes_t &attr) const override;
@@ -79,9 +79,11 @@
VolumeGroupVector getVolumeGroups() const override;
- volume_group_t getVolumeGroupForAttributes(const audio_attributes_t &attr) const override;
+ volume_group_t getVolumeGroupForAttributes(
+ const audio_attributes_t &attr, bool fallbackOnDefault = true) const override;
- volume_group_t getVolumeGroupForStreamType(audio_stream_type_t stream) const override;
+ volume_group_t getVolumeGroupForStreamType(
+ audio_stream_type_t stream, bool fallbackOnDefault = true) const override;
status_t listAudioVolumeGroups(AudioVolumeGroupVector &groups) const override;
@@ -151,12 +153,30 @@
status_t getDevicesForRoleAndCapturePreset(audio_source_t audioSource,
device_role_t role, AudioDeviceTypeAddrVector &devices) const override;
+ DeviceVector getActiveMediaDevices(const DeviceVector& availableDevices) const override;
+
private:
+ /**
+ * Get media devices as the given role
+ *
+ * @param role the audio devices role
+ * @param availableDevices all available devices
+ * @param devices the DeviceVector to store devices as the given role
+ * @return NO_ERROR if all devices associated to the given role are present in available devices
+ * NAME_NO_FOUND if there is no strategy for media or there are no devices associate to
+ * the given role
+ * NOT_ENOUGH_DATA if not all devices as given role are present in available devices
+ */
+ status_t getMediaDevicesForRole(device_role_t role, const DeviceVector& availableDevices,
+ DeviceVector& devices) const;
+
+ void dumpCapturePresetDevicesRoleMap(String8 *dst, int spaces) const;
+
AudioPolicyManagerObserver *mApmObserver = nullptr;
ProductStrategyMap mProductStrategies;
- ProductStrategyPreferredRoutingMap mProductStrategyPreferredDevices;
- CapturePresetDevicesRoleMap mCapturePresetDevicesRole;
+ ProductStrategyDevicesRoleMap mProductStrategyDeviceRoleMap;
+ CapturePresetDevicesRoleMap mCapturePresetDevicesRoleMap;
VolumeGroupMap mVolumeGroups;
LastRemovableMediaDevices mLastRemovableMediaDevices;
audio_mode_t mPhoneState = AUDIO_MODE_NORMAL; /**< current phone state. */
diff --git a/services/audiopolicy/engine/common/include/LastRemovableMediaDevices.h b/services/audiopolicy/engine/common/include/LastRemovableMediaDevices.h
index a3053a4..d7f8b1e 100644
--- a/services/audiopolicy/engine/common/include/LastRemovableMediaDevices.h
+++ b/services/audiopolicy/engine/common/include/LastRemovableMediaDevices.h
@@ -36,6 +36,8 @@
void setRemovableMediaDevices(sp<DeviceDescriptor> desc, audio_policy_dev_state_t state);
std::vector<audio_devices_t> getLastRemovableMediaDevices(
device_out_group_t group = GROUP_NONE) const;
+ sp<DeviceDescriptor> getLastRemovableMediaDevice(
+ const DeviceVector& excludedDevices, device_out_group_t group = GROUP_NONE) const;
private:
struct DeviceGroupDescriptor {
diff --git a/services/audiopolicy/engine/common/include/ProductStrategy.h b/services/audiopolicy/engine/common/include/ProductStrategy.h
index c505456..2aa2f9a 100644
--- a/services/audiopolicy/engine/common/include/ProductStrategy.h
+++ b/services/audiopolicy/engine/common/include/ProductStrategy.h
@@ -18,20 +18,20 @@
#include "VolumeGroup.h"
-#include <system/audio.h>
-#include <utils/RefBase.h>
-#include <HandleGenerator.h>
-#include <string>
-#include <vector>
#include <map>
-#include <utils/Errors.h>
-#include <utils/String8.h>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <HandleGenerator.h>
#include <media/AudioAttributes.h>
#include <media/AudioContainers.h>
#include <media/AudioDeviceTypeAddr.h>
#include <media/AudioPolicy.h>
-
-#include <vector>
+#include <system/audio.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
namespace android {
@@ -129,7 +129,8 @@
* @param attr
* @return applicable product strategy for the given attribute, default if none applicable.
*/
- product_strategy_t getProductStrategyForAttributes(const audio_attributes_t &attr) const;
+ product_strategy_t getProductStrategyForAttributes(
+ const audio_attributes_t &attr, bool fallbackOnDefault = true) const;
product_strategy_t getProductStrategyForStream(audio_stream_type_t stream) const;
@@ -153,9 +154,11 @@
std::string getDeviceAddressForProductStrategy(product_strategy_t strategy) const;
- volume_group_t getVolumeGroupForAttributes(const audio_attributes_t &attr) const;
+ volume_group_t getVolumeGroupForAttributes(
+ const audio_attributes_t &attr, bool fallbackOnDefault = true) const;
- volume_group_t getVolumeGroupForStreamType(audio_stream_type_t stream) const;
+ volume_group_t getVolumeGroupForStreamType(
+ audio_stream_type_t stream, bool fallbackOnDefault = true) const;
volume_group_t getDefaultVolumeGroup() const;
@@ -167,11 +170,12 @@
product_strategy_t mDefaultStrategy = PRODUCT_STRATEGY_NONE;
};
-class ProductStrategyPreferredRoutingMap : public std::map<product_strategy_t,
- AudioDeviceTypeAddrVector>
-{
-public:
- void dump(String8 *dst, int spaces = 0) const;
-};
+using ProductStrategyDevicesRoleMap =
+ std::map<std::pair<product_strategy_t, device_role_t>, AudioDeviceTypeAddrVector>;
+
+void dumpProductStrategyDevicesRoleMap(
+ const ProductStrategyDevicesRoleMap& productStrategyDeviceRoleMap,
+ String8 *dst,
+ int spaces);
} // namespace android
diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp
index 20c2c28..150a9a8 100644
--- a/services/audiopolicy/engine/common/src/EngineBase.cpp
+++ b/services/audiopolicy/engine/common/src/EngineBase.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "APM::AudioPolicyEngine/Base"
//#define LOG_NDEBUG 0
+#include <functional>
+#include <string>
#include <sys/stat.h>
#include "EngineBase.h"
@@ -76,9 +78,10 @@
return NO_ERROR;
}
-product_strategy_t EngineBase::getProductStrategyForAttributes(const audio_attributes_t &attr) const
+product_strategy_t EngineBase::getProductStrategyForAttributes(
+ const audio_attributes_t &attr, bool fallbackOnDefault) const
{
- return mProductStrategies.getProductStrategyForAttributes(attr);
+ return mProductStrategies.getProductStrategyForAttributes(attr, fallbackOnDefault);
}
audio_stream_type_t EngineBase::getStreamTypeForAttributes(const audio_attributes_t &attr) const
@@ -157,7 +160,7 @@
auto result = fileExists(engineConfig::DEFAULT_PATH) ?
engineConfig::parse(engineConfig::DEFAULT_PATH) : engineConfig::ParsingResult{};
if (result.parsedConfig == nullptr) {
- ALOGW("%s: No configuration found, using default matching phone experience.", __FUNCTION__);
+ ALOGD("%s: No configuration found, using default matching phone experience.", __FUNCTION__);
engineConfig::Config config = gDefaultEngineConfig;
android::status_t ret = engineConfig::parseLegacyVolumes(config.volumeGroups);
result = {std::make_unique<engineConfig::Config>(config),
@@ -327,14 +330,16 @@
return group;
}
-volume_group_t EngineBase::getVolumeGroupForAttributes(const audio_attributes_t &attr) const
+volume_group_t EngineBase::getVolumeGroupForAttributes(
+ const audio_attributes_t &attr, bool fallbackOnDefault) const
{
- return mProductStrategies.getVolumeGroupForAttributes(attr);
+ return mProductStrategies.getVolumeGroupForAttributes(attr, fallbackOnDefault);
}
-volume_group_t EngineBase::getVolumeGroupForStreamType(audio_stream_type_t stream) const
+volume_group_t EngineBase::getVolumeGroupForStreamType(
+ audio_stream_type_t stream, bool fallbackOnDefault) const
{
- return mProductStrategies.getVolumeGroupForStreamType(stream);
+ return mProductStrategies.getVolumeGroupForStreamType(stream, fallbackOnDefault);
}
status_t EngineBase::listAudioVolumeGroups(AudioVolumeGroupVector &groups) const
@@ -346,23 +351,33 @@
return NO_ERROR;
}
-status_t EngineBase::setDevicesRoleForStrategy(product_strategy_t strategy, device_role_t role,
- const AudioDeviceTypeAddrVector &devices)
-{
- // verify strategy exists
- if (mProductStrategies.find(strategy) == mProductStrategies.end()) {
- ALOGE("%s invalid strategy %u", __func__, strategy);
+namespace {
+template <typename T>
+status_t setDevicesRoleForT(
+ std::map<std::pair<T, device_role_t>, AudioDeviceTypeAddrVector>& tDevicesRoleMap,
+ T t, device_role_t role, const AudioDeviceTypeAddrVector &devices,
+ const std::string& logStr, std::function<bool(T)> p) {
+ if (!p(t)) {
+ ALOGE("%s invalid %s %u", __func__, logStr.c_str(), t);
return BAD_VALUE;
}
switch (role) {
case DEVICE_ROLE_PREFERRED:
- mProductStrategyPreferredDevices[strategy] = devices;
- break;
- case DEVICE_ROLE_DISABLED:
- // TODO: support set devices role as disabled for strategy.
- ALOGI("%s no implemented for role as %d", __func__, role);
- break;
+ case DEVICE_ROLE_DISABLED: {
+ tDevicesRoleMap[std::make_pair(t, role)] = devices;
+ // The preferred devices and disabled devices are mutually exclusive. Once a device is added
+ // the a list, it must be removed from the other one.
+ const device_role_t roleToRemove = role == DEVICE_ROLE_PREFERRED ? DEVICE_ROLE_DISABLED
+ : DEVICE_ROLE_PREFERRED;
+ auto it = tDevicesRoleMap.find(std::make_pair(t, roleToRemove));
+ if (it != tDevicesRoleMap.end()) {
+ it->second = excludeDeviceTypeAddrsFrom(it->second, devices);
+ if (it->second.empty()) {
+ tDevicesRoleMap.erase(it);
+ }
+ }
+ } break;
case DEVICE_ROLE_NONE:
// Intentionally fall-through as it is no need to set device role as none for a strategy.
default:
@@ -372,28 +387,26 @@
return NO_ERROR;
}
-status_t EngineBase::removeDevicesRoleForStrategy(product_strategy_t strategy, device_role_t role)
-{
- // verify strategy exists
- if (mProductStrategies.find(strategy) == mProductStrategies.end()) {
- ALOGE("%s invalid strategy %u", __func__, strategy);
+template <typename T>
+status_t removeAllDevicesRoleForT(
+ std::map<std::pair<T, device_role_t>, AudioDeviceTypeAddrVector>& tDevicesRoleMap,
+ T t, device_role_t role, const std::string& logStr, std::function<bool(T)> p) {
+ if (!p(t)) {
+ ALOGE("%s invalid %s %u", __func__, logStr.c_str(), t);
return BAD_VALUE;
}
switch (role) {
case DEVICE_ROLE_PREFERRED:
- if (mProductStrategyPreferredDevices.erase(strategy) == 0) {
- // no preferred device was set
+ case DEVICE_ROLE_DISABLED:
+ if (tDevicesRoleMap.erase(std::make_pair(t, role)) == 0) {
+ // no preferred/disabled device was set
return NAME_NOT_FOUND;
}
break;
- case DEVICE_ROLE_DISABLED:
- // TODO: support remove devices role as disabled for strategy.
- ALOGI("%s no implemented for role as %d", __func__, role);
- break;
case DEVICE_ROLE_NONE:
// Intentionally fall-through as it makes no sense to remove devices with
- // role as DEVICE_ROLE_NONE for a strategy
+ // role as DEVICE_ROLE_NONE
default:
ALOGE("%s invalid role %d", __func__, role);
return BAD_VALUE;
@@ -401,25 +414,26 @@
return NO_ERROR;
}
-status_t EngineBase::getDevicesForRoleAndStrategy(product_strategy_t strategy, device_role_t role,
- AudioDeviceTypeAddrVector &devices) const
-{
- // verify strategy exists
- if (mProductStrategies.find(strategy) == mProductStrategies.end()) {
- ALOGE("%s unknown strategy %u", __func__, strategy);
+template <typename T>
+status_t getDevicesRoleForT(
+ const std::map<std::pair<T, device_role_t>, AudioDeviceTypeAddrVector>& tDevicesRoleMap,
+ T t, device_role_t role, AudioDeviceTypeAddrVector &devices, const std::string& logStr,
+ std::function<bool(T)> p) {
+ if (!p(t)) {
+ ALOGE("%s invalid %s %u", __func__, logStr.c_str(), t);
return BAD_VALUE;
}
switch (role) {
- case DEVICE_ROLE_PREFERRED: {
- // preferred device for this strategy?
- auto devIt = mProductStrategyPreferredDevices.find(strategy);
- if (devIt == mProductStrategyPreferredDevices.end()) {
- ALOGV("%s no preferred device for strategy %u", __func__, strategy);
+ case DEVICE_ROLE_PREFERRED:
+ case DEVICE_ROLE_DISABLED: {
+ auto it = tDevicesRoleMap.find(std::make_pair(t, role));
+ if (it == tDevicesRoleMap.end()) {
+ ALOGV("%s no device as role %u for %s %u", __func__, role, logStr.c_str(), t);
return NAME_NOT_FOUND;
}
- devices = devIt->second;
+ devices = it->second;
} break;
case DEVICE_ROLE_NONE:
// Intentionally fall-through as the DEVICE_ROLE_NONE is never set
@@ -430,32 +444,45 @@
return NO_ERROR;
}
+} // namespace
+
+status_t EngineBase::setDevicesRoleForStrategy(product_strategy_t strategy, device_role_t role,
+ const AudioDeviceTypeAddrVector &devices)
+{
+ std::function<bool(product_strategy_t)> p = [this](product_strategy_t strategy) {
+ return mProductStrategies.find(strategy) != mProductStrategies.end();
+ };
+ return setDevicesRoleForT(
+ mProductStrategyDeviceRoleMap, strategy, role, devices, "strategy" /*logStr*/, p);
+}
+
+status_t EngineBase::removeDevicesRoleForStrategy(product_strategy_t strategy, device_role_t role)
+{
+ std::function<bool(product_strategy_t)> p = [this](product_strategy_t strategy) {
+ return mProductStrategies.find(strategy) != mProductStrategies.end();
+ };
+ return removeAllDevicesRoleForT(
+ mProductStrategyDeviceRoleMap, strategy, role, "strategy" /*logStr*/, p);
+}
+
+status_t EngineBase::getDevicesForRoleAndStrategy(product_strategy_t strategy, device_role_t role,
+ AudioDeviceTypeAddrVector &devices) const
+{
+ std::function<bool(product_strategy_t)> p = [this](product_strategy_t strategy) {
+ return mProductStrategies.find(strategy) != mProductStrategies.end();
+ };
+ return getDevicesRoleForT(
+ mProductStrategyDeviceRoleMap, strategy, role, devices, "strategy" /*logStr*/, p);
+}
+
status_t EngineBase::setDevicesRoleForCapturePreset(audio_source_t audioSource, device_role_t role,
const AudioDeviceTypeAddrVector &devices)
{
- // verify if the audio source is valid
- if (!audio_is_valid_audio_source(audioSource)) {
- ALOGE("%s unknown audio source %u", __func__, audioSource);
- }
-
- switch (role) {
- case DEVICE_ROLE_PREFERRED:
- mCapturePresetDevicesRole[audioSource][role] = devices;
- // When the devices are set as preferred devices, remove them from the disabled devices.
- doRemoveDevicesRoleForCapturePreset(
- audioSource, DEVICE_ROLE_DISABLED, devices, false /*forceMatched*/);
- break;
- case DEVICE_ROLE_DISABLED:
- // TODO: support setting devices role as disabled for capture preset.
- ALOGI("%s no implemented for role as %d", __func__, role);
- break;
- case DEVICE_ROLE_NONE:
- // Intentionally fall-through as it is no need to set device role as none
- default:
- ALOGE("%s invalid role %d", __func__, role);
- return BAD_VALUE;
- }
- return NO_ERROR;
+ std::function<bool(audio_source_t)> p = [](audio_source_t audioSource) {
+ return audio_is_valid_audio_source(audioSource);
+ };
+ return setDevicesRoleForT(
+ mCapturePresetDevicesRoleMap, audioSource, role, devices, "audio source" /*logStr*/, p);
}
status_t EngineBase::addDevicesRoleForCapturePreset(audio_source_t audioSource, device_role_t role,
@@ -468,19 +495,20 @@
switch (role) {
case DEVICE_ROLE_PREFERRED:
- mCapturePresetDevicesRole[audioSource][role] = excludeDeviceTypeAddrsFrom(
- mCapturePresetDevicesRole[audioSource][role], devices);
- for (const auto& device : devices) {
- mCapturePresetDevicesRole[audioSource][role].push_back(device);
+ case DEVICE_ROLE_DISABLED: {
+ const auto audioSourceRole = std::make_pair(audioSource, role);
+ mCapturePresetDevicesRoleMap[audioSourceRole] = excludeDeviceTypeAddrsFrom(
+ mCapturePresetDevicesRoleMap[audioSourceRole], devices);
+ for (const auto &device : devices) {
+ mCapturePresetDevicesRoleMap[audioSourceRole].push_back(device);
}
// When the devices are set as preferred devices, remove them from the disabled devices.
doRemoveDevicesRoleForCapturePreset(
- audioSource, DEVICE_ROLE_DISABLED, devices, false /*forceMatched*/);
- break;
- case DEVICE_ROLE_DISABLED:
- // TODO: support setting devices role as disabled for capture preset.
- ALOGI("%s no implemented for role as %d", __func__, role);
- break;
+ audioSource,
+ role == DEVICE_ROLE_PREFERRED ? DEVICE_ROLE_DISABLED : DEVICE_ROLE_PREFERRED,
+ devices,
+ false /*forceMatched*/);
+ } break;
case DEVICE_ROLE_NONE:
// Intentionally fall-through as it is no need to set device role as none
default:
@@ -506,21 +534,22 @@
switch (role) {
case DEVICE_ROLE_PREFERRED:
case DEVICE_ROLE_DISABLED: {
- if (mCapturePresetDevicesRole.count(audioSource) == 0 ||
- mCapturePresetDevicesRole[audioSource].count(role) == 0) {
+ const auto audioSourceRole = std::make_pair(audioSource, role);
+ if (mCapturePresetDevicesRoleMap.find(audioSourceRole) ==
+ mCapturePresetDevicesRoleMap.end()) {
return NAME_NOT_FOUND;
}
AudioDeviceTypeAddrVector remainingDevices = excludeDeviceTypeAddrsFrom(
- mCapturePresetDevicesRole[audioSource][role], devices);
+ mCapturePresetDevicesRoleMap[audioSourceRole], devices);
if (forceMatched && remainingDevices.size() !=
- mCapturePresetDevicesRole[audioSource][role].size() - devices.size()) {
+ mCapturePresetDevicesRoleMap[audioSourceRole].size() - devices.size()) {
// There are some devices from `devicesToRemove` that are not shown in the cached record
return BAD_VALUE;
}
- mCapturePresetDevicesRole[audioSource][role] = remainingDevices;
- if (mCapturePresetDevicesRole[audioSource][role].empty()) {
+ mCapturePresetDevicesRoleMap[audioSourceRole] = remainingDevices;
+ if (mCapturePresetDevicesRoleMap[audioSourceRole].empty()) {
// Remove the role when device list is empty
- mCapturePresetDevicesRole[audioSource].erase(role);
+ mCapturePresetDevicesRoleMap.erase(audioSourceRole);
}
} break;
case DEVICE_ROLE_NONE:
@@ -536,69 +565,78 @@
status_t EngineBase::clearDevicesRoleForCapturePreset(audio_source_t audioSource,
device_role_t role)
{
- // verify if the audio source is valid
- if (!audio_is_valid_audio_source(audioSource)) {
- ALOGE("%s unknown audio source %u", __func__, audioSource);
- }
-
- switch (role) {
- case DEVICE_ROLE_PREFERRED:
- if (mCapturePresetDevicesRole.count(audioSource) == 0 ||
- mCapturePresetDevicesRole[audioSource].erase(role) == 0) {
- // no preferred device for the given audio source
- return NAME_NOT_FOUND;
- }
- break;
- case DEVICE_ROLE_DISABLED:
- // TODO: support remove devices role as disabled for strategy.
- ALOGI("%s no implemented for role as %d", __func__, role);
- break;
- case DEVICE_ROLE_NONE:
- // Intentionally fall-through as it makes no sense to remove devices with
- // role as DEVICE_ROLE_NONE for a strategy
- default:
- ALOGE("%s invalid role %d", __func__, role);
- return BAD_VALUE;
- }
- return NO_ERROR;
+ std::function<bool(audio_source_t)> p = [](audio_source_t audioSource) {
+ return audio_is_valid_audio_source(audioSource);
+ };
+ return removeAllDevicesRoleForT(
+ mCapturePresetDevicesRoleMap, audioSource, role, "audio source" /*logStr*/, p);
}
status_t EngineBase::getDevicesForRoleAndCapturePreset(audio_source_t audioSource,
device_role_t role, AudioDeviceTypeAddrVector &devices) const
{
- // verify if the audio source is valid
- if (!audio_is_valid_audio_source(audioSource)) {
- ALOGE("%s unknown audio source %u", __func__, audioSource);
- return BAD_VALUE;
- }
+ std::function<bool(audio_source_t)> p = [](audio_source_t audioSource) {
+ return audio_is_valid_audio_source(audioSource);
+ };
+ return getDevicesRoleForT(
+ mCapturePresetDevicesRoleMap, audioSource, role, devices, "audio source" /*logStr*/, p);
+}
- switch (role) {
- case DEVICE_ROLE_PREFERRED:
- case DEVICE_ROLE_DISABLED: {
- if (mCapturePresetDevicesRole.count(audioSource) == 0) {
- return NAME_NOT_FOUND;
- }
- auto devIt = mCapturePresetDevicesRole.at(audioSource).find(role);
- if (devIt == mCapturePresetDevicesRole.at(audioSource).end()) {
- ALOGV("%s no devices role(%d) for capture preset %u", __func__, role, audioSource);
- return NAME_NOT_FOUND;
- }
-
- devices = devIt->second;
- } break;
- case DEVICE_ROLE_NONE:
- // Intentionally fall-through as the DEVICE_ROLE_NONE is never set
- default:
- ALOGE("%s invalid role %d", __func__, role);
- return BAD_VALUE;
+status_t EngineBase::getMediaDevicesForRole(device_role_t role,
+ const DeviceVector& availableDevices, DeviceVector& devices) const
+{
+ product_strategy_t strategy = getProductStrategyByName("STRATEGY_MEDIA" /*name*/);
+ if (strategy == PRODUCT_STRATEGY_NONE) {
+ strategy = getProductStrategyForStream(AUDIO_STREAM_MUSIC);
}
- return NO_ERROR;
+ if (strategy == PRODUCT_STRATEGY_NONE) {
+ return NAME_NOT_FOUND;
+ }
+ AudioDeviceTypeAddrVector deviceAddrVec;
+ status_t status = getDevicesForRoleAndStrategy(strategy, role, deviceAddrVec);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ devices = availableDevices.getDevicesFromDeviceTypeAddrVec(deviceAddrVec);
+ return deviceAddrVec.size() == devices.size() ? NO_ERROR : NOT_ENOUGH_DATA;
+}
+
+DeviceVector EngineBase::getActiveMediaDevices(const DeviceVector& availableDevices) const
+{
+ // The priority of active devices as follows:
+ // 1: the available preferred devices for media
+ // 2: the latest connected removable media device that is enabled
+ DeviceVector activeDevices;
+ if (getMediaDevicesForRole(
+ DEVICE_ROLE_PREFERRED, availableDevices, activeDevices) != NO_ERROR) {
+ activeDevices.clear();
+ DeviceVector disabledDevices;
+ getMediaDevicesForRole(DEVICE_ROLE_DISABLED, availableDevices, disabledDevices);
+ sp<DeviceDescriptor> device =
+ mLastRemovableMediaDevices.getLastRemovableMediaDevice(disabledDevices);
+ if (device != nullptr) {
+ activeDevices.add(device);
+ }
+ }
+ return activeDevices;
+}
+
+void EngineBase::dumpCapturePresetDevicesRoleMap(String8 *dst, int spaces) const
+{
+ dst->appendFormat("\n%*sDevice role per capture preset dump:", spaces, "");
+ for (const auto& [capturePresetRolePair, devices] : mCapturePresetDevicesRoleMap) {
+ dst->appendFormat("\n%*sCapture preset(%u) Device Role(%u) Devices(%s)", spaces + 2, "",
+ capturePresetRolePair.first, capturePresetRolePair.second,
+ dumpAudioDeviceTypeAddrVector(devices, true /*includeSensitiveInfo*/).c_str());
+ }
+ dst->appendFormat("\n");
}
void EngineBase::dump(String8 *dst) const
{
mProductStrategies.dump(dst, 2);
- mProductStrategyPreferredDevices.dump(dst, 2);
+ dumpProductStrategyDevicesRoleMap(mProductStrategyDeviceRoleMap, dst, 2);
+ dumpCapturePresetDevicesRoleMap(dst, 2);
mVolumeGroups.dump(dst, 2);
}
diff --git a/services/audiopolicy/engine/common/src/LastRemovableMediaDevices.cpp b/services/audiopolicy/engine/common/src/LastRemovableMediaDevices.cpp
index 96cc140..b3f8947 100644
--- a/services/audiopolicy/engine/common/src/LastRemovableMediaDevices.cpp
+++ b/services/audiopolicy/engine/common/src/LastRemovableMediaDevices.cpp
@@ -55,6 +55,17 @@
return ret;
}
+sp<DeviceDescriptor> LastRemovableMediaDevices::getLastRemovableMediaDevice(
+ const DeviceVector& excludedDevices, device_out_group_t group) const {
+ for (auto iter = mMediaDevices.begin(); iter != mMediaDevices.end(); ++iter) {
+ if ((group == GROUP_NONE || group == getDeviceOutGroup((iter->desc)->type())) &&
+ !excludedDevices.contains(iter->desc)) {
+ return iter->desc;
+ }
+ }
+ return nullptr;
+}
+
device_out_group_t LastRemovableMediaDevices::getDeviceOutGroup(audio_devices_t device) const
{
switch (device) {
diff --git a/services/audiopolicy/engine/common/src/ProductStrategy.cpp b/services/audiopolicy/engine/common/src/ProductStrategy.cpp
index 060568a..b3d144f 100644
--- a/services/audiopolicy/engine/common/src/ProductStrategy.cpp
+++ b/services/audiopolicy/engine/common/src/ProductStrategy.cpp
@@ -169,7 +169,7 @@
}
product_strategy_t ProductStrategyMap::getProductStrategyForAttributes(
- const audio_attributes_t &attr) const
+ const audio_attributes_t &attr, bool fallbackOnDefault) const
{
for (const auto &iter : *this) {
if (iter.second->matches(attr)) {
@@ -178,7 +178,7 @@
}
ALOGV("%s: No matching product strategy for attributes %s, return default", __FUNCTION__,
toString(attr).c_str());
- return getDefault();
+ return fallbackOnDefault? getDefault() : PRODUCT_STRATEGY_NONE;
}
audio_attributes_t ProductStrategyMap::getAttributesForStreamType(audio_stream_type_t stream) const
@@ -272,7 +272,8 @@
return at(psId)->getDeviceAddress();
}
-volume_group_t ProductStrategyMap::getVolumeGroupForAttributes(const audio_attributes_t &attr) const
+volume_group_t ProductStrategyMap::getVolumeGroupForAttributes(
+ const audio_attributes_t &attr, bool fallbackOnDefault) const
{
for (const auto &iter : *this) {
volume_group_t group = iter.second->getVolumeGroupForAttributes(attr);
@@ -280,10 +281,11 @@
return group;
}
}
- return getDefaultVolumeGroup();
+ return fallbackOnDefault ? getDefaultVolumeGroup() : VOLUME_GROUP_NONE;
}
-volume_group_t ProductStrategyMap::getVolumeGroupForStreamType(audio_stream_type_t stream) const
+volume_group_t ProductStrategyMap::getVolumeGroupForStreamType(
+ audio_stream_type_t stream, bool fallbackOnDefault) const
{
for (const auto &iter : *this) {
volume_group_t group = iter.second->getVolumeGroupForStreamType(stream);
@@ -292,7 +294,7 @@
}
}
ALOGW("%s: no volume group for %s, using default", __func__, toString(stream).c_str());
- return getDefaultVolumeGroup();
+ return fallbackOnDefault ? getDefaultVolumeGroup() : VOLUME_GROUP_NONE;
}
volume_group_t ProductStrategyMap::getDefaultVolumeGroup() const
@@ -318,14 +320,15 @@
}
}
-void ProductStrategyPreferredRoutingMap::dump(android::String8* dst, int spaces) const {
- dst->appendFormat("\n%*sPreferred devices per product strategy dump:", spaces, "");
- for (const auto& iter : *this) {
- dst->appendFormat("\n%*sStrategy %u %s",
- spaces + 2, "",
- (uint32_t) iter.first,
- dumpAudioDeviceTypeAddrVector(iter.second, true /*includeSensitiveInfo*/)
- .c_str());
+void dumpProductStrategyDevicesRoleMap(
+ const ProductStrategyDevicesRoleMap& productStrategyDeviceRoleMap,
+ String8 *dst,
+ int spaces) {
+ dst->appendFormat("\n%*sDevice role per product strategy dump:", spaces, "");
+ for (const auto& [strategyRolePair, devices] : productStrategyDeviceRoleMap) {
+ dst->appendFormat("\n%*sStrategy(%u) Device Role(%u) Devices(%s)", spaces + 2, "",
+ strategyRolePair.first, strategyRolePair.second,
+ dumpAudioDeviceTypeAddrVector(devices, true /*includeSensitiveInfo*/).c_str());
}
dst->appendFormat("\n");
}
diff --git a/services/audiopolicy/engine/common/src/VolumeCurve.cpp b/services/audiopolicy/engine/common/src/VolumeCurve.cpp
index c352578..8aa4b08 100644
--- a/services/audiopolicy/engine/common/src/VolumeCurve.cpp
+++ b/services/audiopolicy/engine/common/src/VolumeCurve.cpp
@@ -43,10 +43,24 @@
indexInUi = volIndexMax;
}
+ // Calculate the new volume index
size_t nbCurvePoints = mCurvePoints.size();
- // the volume index in the UI is relative to the min and max volume indices for this stream
- int nbSteps = 1 + mCurvePoints[nbCurvePoints - 1].mIndex - mCurvePoints[0].mIndex;
- int volIdx = (nbSteps * (indexInUi - volIndexMin)) / (volIndexMax - volIndexMin);
+
+ int volIdx;
+ if (volIndexMin == volIndexMax) {
+ if (indexInUi == volIndexMin) {
+ volIdx = volIndexMin;
+ } else {
+ // This would result in a divide-by-zero below
+ ALOG_ASSERT(volIndexmin != volIndexMax, "Invalid volume index range & value: 0");
+ return NAN;
+ }
+ } else {
+ // interpolaate
+ // the volume index in the UI is relative to the min and max volume indices for this stream
+ int nbSteps = 1 + mCurvePoints[nbCurvePoints - 1].mIndex - mCurvePoints[0].mIndex;
+ volIdx = (nbSteps * (indexInUi - volIndexMin)) / (volIndexMax - volIndexMin);
+ }
// Where would this volume index been inserted in the curve point
size_t indexInUiPosition = mCurvePoints.orderOf(CurvePoint(volIdx, 0));
diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp
index 7cfef5b..1c86051 100644
--- a/services/audiopolicy/engine/config/src/EngineConfig.cpp
+++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp
@@ -643,7 +643,11 @@
xmlDocPtr doc;
doc = xmlParseFile(path);
if (doc == NULL) {
- ALOGE("%s: Could not parse document %s", __FUNCTION__, path);
+ // It is OK not to find an engine config file at the default location
+ // as the caller will default to hardcoded default config
+ if (strncmp(path, DEFAULT_PATH, strlen(DEFAULT_PATH))) {
+ ALOGW("%s: Could not parse document %s", __FUNCTION__, path);
+ }
return {nullptr, 0};
}
xmlNodePtr cur = xmlDocGetRootElement(doc);
diff --git a/services/audiopolicy/engine/interface/EngineInterface.h b/services/audiopolicy/engine/interface/EngineInterface.h
index f64608d..518f86e 100644
--- a/services/audiopolicy/engine/interface/EngineInterface.h
+++ b/services/audiopolicy/engine/interface/EngineInterface.h
@@ -16,6 +16,8 @@
#pragma once
+#include <utility>
+
#include <AudioPolicyManagerObserver.h>
#include <media/AudioProductStrategy.h>
#include <media/AudioVolumeGroup.h>
@@ -35,7 +37,7 @@
using StrategyVector = std::vector<product_strategy_t>;
using VolumeGroupVector = std::vector<volume_group_t>;
using CapturePresetDevicesRoleMap =
- std::map<audio_source_t, std::map<device_role_t, AudioDeviceTypeAddrVector>>;
+ std::map<std::pair<audio_source_t, device_role_t>, AudioDeviceTypeAddrVector>;
/**
* This interface is dedicated to the policy manager that a Policy Engine shall implement.
@@ -110,11 +112,12 @@
* Get the strategy selected for a given audio attributes.
*
* @param[in] audio attributes to get the selected @product_strategy_t followed by.
- *
+ * @param fallbackOnDefault if true, will return the fallback strategy if the attributes
+ * are not explicitly assigned to a given strategy.
* @return @product_strategy_t to be followed.
*/
virtual product_strategy_t getProductStrategyForAttributes(
- const audio_attributes_t &attr) const = 0;
+ const audio_attributes_t &attr, bool fallbackOnDefault = true) const = 0;
/**
* @brief getOutputDevicesForAttributes retrieves the devices to be used for given
@@ -170,8 +173,10 @@
* @param[out] mix to be used if a mix has been installed for the given audio attributes.
* @return selected input device for the audio attributes, may be null if error.
*/
- virtual sp<DeviceDescriptor> getInputDeviceForAttributes(
- const audio_attributes_t &attr, sp<AudioPolicyMix> *mix = nullptr) const = 0;
+ virtual sp<DeviceDescriptor> getInputDeviceForAttributes(const audio_attributes_t &attr,
+ uid_t uid = 0,
+ sp<AudioPolicyMix> *mix = nullptr)
+ const = 0;
/**
* Get the legacy stream type for a given audio attributes.
@@ -271,19 +276,25 @@
* @brief getVolumeGroupForAttributes gets the appropriate volume group to be used for a given
* Audio Attributes.
* @param attr to be considered
+ * @param fallbackOnDefault if true, will return the fallback volume group if the attributes
+ * are not associated to any volume group.
* @return volume group associated to the given audio attributes, default group if none
* applicable, VOLUME_GROUP_NONE if no default group defined.
*/
- virtual volume_group_t getVolumeGroupForAttributes(const audio_attributes_t &attr) const = 0;
+ virtual volume_group_t getVolumeGroupForAttributes(
+ const audio_attributes_t &attr, bool fallbackOnDefault = true) const = 0;
/**
* @brief getVolumeGroupForStreamType gets the appropriate volume group to be used for a given
* legacy stream type
* @param stream type to be considered
+ * @param fallbackOnDefault if true, will return the fallback volume group if the stream type
+ * is not associated to any volume group.
* @return volume group associated to the given stream type, default group if none applicable,
* VOLUME_GROUP_NONE if no default group defined.
*/
- virtual volume_group_t getVolumeGroupForStreamType(audio_stream_type_t stream) const = 0;
+ virtual volume_group_t getVolumeGroupForStreamType(
+ audio_stream_type_t stream, bool fallbackOnDefault = true) const = 0;
/**
* @brief listAudioVolumeGroups introspection API to get the Audio Volume Groups, aka
@@ -403,6 +414,12 @@
virtual status_t getDevicesForRoleAndCapturePreset(audio_source_t audioSource,
device_role_t role, AudioDeviceTypeAddrVector &devices) const = 0;
+ /**
+ * @brief getActiveMediaDevices returns which devices will most likely to be used for media
+ * @param availableDevices all available devices
+ * @return collection of active devices
+ */
+ virtual DeviceVector getActiveMediaDevices(const DeviceVector& availableDevices) const = 0;
virtual void dump(String8 *dst) const = 0;
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.bp b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.bp
index 2d57d4f..bc72484 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.bp
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.bp
@@ -32,8 +32,6 @@
// all of the 'license_kinds' from "frameworks_av_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_av_license"],
}
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Android.bp b/services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Android.bp
index df31bb9..11da8c7 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Android.bp
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Android.bp
@@ -33,8 +33,6 @@
// all of the 'license_kinds' from "frameworks_av_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_av_license"],
}
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.bp b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.bp
index 65ab8b6..91ffeb5 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.bp
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.bp
@@ -32,8 +32,6 @@
// all of the 'license_kinds' from "frameworks_av_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_av_license"],
}
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoInput/Android.bp b/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoInput/Android.bp
index 90154ee..cac63fc 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoInput/Android.bp
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoInput/Android.bp
@@ -29,8 +29,6 @@
// all of the 'license_kinds' from "frameworks_av_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_av_license"],
}
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoOutput/Android.bp b/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoOutput/Android.bp
index 5b06852..337f358 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoOutput/Android.bp
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoOutput/Android.bp
@@ -29,8 +29,6 @@
// all of the 'license_kinds' from "frameworks_av_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_av_license"],
}
diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp
index 6d42fcf..b0c376a 100644
--- a/services/audiopolicy/engineconfigurable/src/Engine.cpp
+++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp
@@ -310,6 +310,7 @@
}
sp<DeviceDescriptor> Engine::getInputDeviceForAttributes(const audio_attributes_t &attr,
+ uid_t uid,
sp<AudioPolicyMix> *mix) const
{
const auto &policyMixes = getApmObserver()->getAudioPolicyMixCollection();
@@ -328,7 +329,10 @@
return device;
}
- device = policyMixes.getDeviceAndMixForInputSource(attr.source, availableInputDevices, mix);
+ device = policyMixes.getDeviceAndMixForInputSource(attr.source,
+ availableInputDevices,
+ uid,
+ mix);
if (device != nullptr) {
return device;
}
diff --git a/services/audiopolicy/engineconfigurable/src/Engine.h b/services/audiopolicy/engineconfigurable/src/Engine.h
index 3b371d8..d8e2742 100644
--- a/services/audiopolicy/engineconfigurable/src/Engine.h
+++ b/services/audiopolicy/engineconfigurable/src/Engine.h
@@ -61,8 +61,10 @@
DeviceVector getOutputDevicesForStream(audio_stream_type_t stream,
bool fromCache = false) const override;
- sp<DeviceDescriptor> getInputDeviceForAttributes(
- const audio_attributes_t &attr, sp<AudioPolicyMix> *mix = nullptr) const override;
+ sp<DeviceDescriptor> getInputDeviceForAttributes(const audio_attributes_t &attr,
+ uid_t uid = 0,
+ sp<AudioPolicyMix> *mix = nullptr)
+ const override;
void updateDeviceSelectionCache() override;
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index e0a0bbb..f67ffc1 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -142,47 +142,20 @@
return EngineBase::setForceUse(usage, config);
}
-DeviceVector Engine::getDevicesForStrategyInt(legacy_strategy strategy,
- DeviceVector availableOutputDevices,
- DeviceVector availableInputDevices,
- const SwAudioOutputCollection &outputs) const
+void Engine::filterOutputDevicesForStrategy(legacy_strategy strategy,
+ DeviceVector& availableOutputDevices,
+ const SwAudioOutputCollection &outputs) const
{
- DeviceVector devices;
+ DeviceVector availableInputDevices = getApmObserver()->getAvailableInputDevices();
switch (strategy) {
-
- case STRATEGY_TRANSMITTED_THROUGH_SPEAKER:
- devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER);
- break;
-
- case STRATEGY_SONIFICATION_RESPECTFUL:
- if (isInCall() || outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_VOICE_CALL))) {
- devices = getDevicesForStrategyInt(
- STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs);
- } else {
- bool media_active_locally =
- outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_MUSIC),
- SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)
- || outputs.isActiveLocally(
- toVolumeSource(AUDIO_STREAM_ACCESSIBILITY),
- SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY);
+ case STRATEGY_SONIFICATION_RESPECTFUL: {
+ if (!(isInCall() || outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_VOICE_CALL)))) {
// routing is same as media without the "remote" device
availableOutputDevices.remove(availableOutputDevices.getDevicesFromType(
AUDIO_DEVICE_OUT_REMOTE_SUBMIX));
- devices = getDevicesForStrategyInt(STRATEGY_MEDIA,
- availableOutputDevices,
- availableInputDevices, outputs);
- // if no media is playing on the device, check for mandatory use of "safe" speaker
- // when media would have played on speaker, and the safe speaker path is available
- if (!media_active_locally) {
- devices.replaceDevicesByType(
- AUDIO_DEVICE_OUT_SPEAKER,
- availableOutputDevices.getDevicesFromType(
- AUDIO_DEVICE_OUT_SPEAKER_SAFE));
- }
}
- break;
-
+ } break;
case STRATEGY_DTMF:
case STRATEGY_PHONE: {
// Force use of only devices on primary output if:
@@ -214,29 +187,87 @@
availableOutputDevices = availPrimaryOutputDevices;
}
}
+ } break;
+ case STRATEGY_ACCESSIBILITY: {
+ // do not route accessibility prompts to a digital output currently configured with a
+ // compressed format as they would likely not be mixed and dropped.
+ for (size_t i = 0; i < outputs.size(); i++) {
+ sp<AudioOutputDescriptor> desc = outputs.valueAt(i);
+ if (desc->isActive() && !audio_is_linear_pcm(desc->getFormat())) {
+ availableOutputDevices.remove(desc->devices().getDevicesFromTypes({
+ AUDIO_DEVICE_OUT_HDMI, AUDIO_DEVICE_OUT_SPDIF,
+ AUDIO_DEVICE_OUT_HDMI_ARC, AUDIO_DEVICE_OUT_HDMI_EARC}));
+ }
+ }
+ } break;
+ default:
+ break;
+ }
+}
+
+product_strategy_t Engine::remapStrategyFromContext(product_strategy_t strategy,
+ const SwAudioOutputCollection &outputs) const {
+ auto legacyStrategy = mLegacyStrategyMap.find(strategy) != end(mLegacyStrategyMap) ?
+ mLegacyStrategyMap.at(strategy) : STRATEGY_NONE;
+
+ if (isInCall()) {
+ switch (legacyStrategy) {
+ case STRATEGY_ACCESSIBILITY:
+ case STRATEGY_DTMF:
+ case STRATEGY_MEDIA:
+ case STRATEGY_SONIFICATION:
+ case STRATEGY_SONIFICATION_RESPECTFUL:
+ legacyStrategy = STRATEGY_PHONE;
+ break;
+
+ default:
+ return strategy;
+ }
+ } else {
+ switch (legacyStrategy) {
+ case STRATEGY_SONIFICATION_RESPECTFUL:
+ case STRATEGY_SONIFICATION:
+ if (outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_VOICE_CALL))) {
+ legacyStrategy = STRATEGY_PHONE;
+ }
+ break;
+
+ case STRATEGY_ACCESSIBILITY:
+ if (outputs.isActive(toVolumeSource(AUDIO_STREAM_RING)) ||
+ outputs.isActive(toVolumeSource(AUDIO_STREAM_ALARM))) {
+ legacyStrategy = STRATEGY_SONIFICATION;
+ }
+ break;
+
+ default:
+ return strategy;
+ }
+ }
+ return getProductStrategyFromLegacy(legacyStrategy);
+}
+
+DeviceVector Engine::getDevicesForStrategyInt(legacy_strategy strategy,
+ DeviceVector availableOutputDevices,
+ const SwAudioOutputCollection &outputs) const
+{
+ DeviceVector devices;
+
+ switch (strategy) {
+
+ case STRATEGY_TRANSMITTED_THROUGH_SPEAKER:
+ devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER);
+ break;
+
+ case STRATEGY_PHONE: {
devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_HEARING_AID);
if (!devices.isEmpty()) break;
- devices = availableOutputDevices.getFirstDevicesFromTypes({
- AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
- AUDIO_DEVICE_OUT_WIRED_HEADSET,
- AUDIO_DEVICE_OUT_LINE,
- AUDIO_DEVICE_OUT_USB_HEADSET,
- AUDIO_DEVICE_OUT_USB_DEVICE});
+ devices = availableOutputDevices.getFirstDevicesFromTypes(
+ getLastRemovableMediaDevices());
if (!devices.isEmpty()) break;
devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_EARPIECE);
} break;
case STRATEGY_SONIFICATION:
-
- // If incall, just select the STRATEGY_PHONE device
- if (isInCall() ||
- outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_VOICE_CALL))) {
- devices = getDevicesForStrategyInt(
- STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs);
- break;
- }
- FALLTHROUGH_INTENDED;
-
case STRATEGY_ENFORCED_AUDIBLE:
// strategy STRATEGY_ENFORCED_AUDIBLE uses same routing policy as STRATEGY_SONIFICATION
// except:
@@ -284,32 +315,9 @@
// The second device used for sonification is the same as the device used by media strategy
FALLTHROUGH_INTENDED;
+ case STRATEGY_DTMF:
case STRATEGY_ACCESSIBILITY:
- if (strategy == STRATEGY_ACCESSIBILITY) {
- // do not route accessibility prompts to a digital output currently configured with a
- // compressed format as they would likely not be mixed and dropped.
- for (size_t i = 0; i < outputs.size(); i++) {
- sp<AudioOutputDescriptor> desc = outputs.valueAt(i);
- if (desc->isActive() && !audio_is_linear_pcm(desc->getFormat())) {
- availableOutputDevices.remove(desc->devices().getDevicesFromTypes({
- AUDIO_DEVICE_OUT_HDMI, AUDIO_DEVICE_OUT_SPDIF,
- AUDIO_DEVICE_OUT_HDMI_ARC}));
- }
- }
- if (outputs.isActive(toVolumeSource(AUDIO_STREAM_RING)) ||
- outputs.isActive(toVolumeSource(AUDIO_STREAM_ALARM))) {
- return getDevicesForStrategyInt(
- STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs);
- }
- if (isInCall()) {
- return getDevicesForStrategyInt(
- STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs);
- }
- }
- // For other cases, STRATEGY_ACCESSIBILITY behaves like STRATEGY_MEDIA
- FALLTHROUGH_INTENDED;
-
- // FIXME: STRATEGY_REROUTING follow STRATEGY_MEDIA for now
+ case STRATEGY_SONIFICATION_RESPECTFUL:
case STRATEGY_REROUTING:
case STRATEGY_MEDIA: {
DeviceVector devices2;
@@ -322,11 +330,6 @@
devices2.add(remoteSubmix);
}
}
- if (isInCall() && (strategy == STRATEGY_MEDIA)) {
- devices = getDevicesForStrategyInt(
- STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs);
- break;
- }
if ((devices2.isEmpty()) &&
(getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) == AUDIO_POLICY_FORCE_SPEAKER)) {
@@ -359,7 +362,9 @@
if (strategy == STRATEGY_MEDIA) {
// ARC, SPDIF and AUX_LINE can co-exist with others.
devices3 = availableOutputDevices.getDevicesFromTypes({
- AUDIO_DEVICE_OUT_HDMI_ARC, AUDIO_DEVICE_OUT_SPDIF, AUDIO_DEVICE_OUT_AUX_LINE});
+ AUDIO_DEVICE_OUT_HDMI_ARC, AUDIO_DEVICE_OUT_HDMI_EARC,
+ AUDIO_DEVICE_OUT_SPDIF, AUDIO_DEVICE_OUT_AUX_LINE,
+ });
}
devices2.add(devices3);
@@ -374,9 +379,19 @@
devices.remove(devices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER));
}
- // for STRATEGY_SONIFICATION:
+ bool mediaActiveLocally =
+ outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_MUSIC),
+ SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)
+ || outputs.isActiveLocally(
+ toVolumeSource(AUDIO_STREAM_ACCESSIBILITY),
+ SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY);
+ // - for STRATEGY_SONIFICATION:
// if SPEAKER was selected, and SPEAKER_SAFE is available, use SPEAKER_SAFE instead
- if (strategy == STRATEGY_SONIFICATION) {
+ // - for STRATEGY_SONIFICATION_RESPECTFUL:
+ // if no media is playing on the device, check for mandatory use of "safe" speaker
+ // when media would have played on speaker, and the safe speaker path is available
+ if (strategy == STRATEGY_SONIFICATION
+ || (strategy == STRATEGY_SONIFICATION_RESPECTFUL && !mediaActiveLocally)) {
devices.replaceDevicesByType(
AUDIO_DEVICE_OUT_SPEAKER,
availableOutputDevices.getDevicesFromType(
@@ -388,22 +403,26 @@
devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_TELEPHONY_TX);
break;
+ case STRATEGY_NONE:
+ // Happens when internal strategies are processed ("rerouting", "patch"...)
+ break;
+
default:
- ALOGW("getDevicesForStrategy() unknown strategy: %d", strategy);
+ ALOGW("%s unknown strategy: %d", __func__, strategy);
break;
}
if (devices.isEmpty()) {
- ALOGV("getDevicesForStrategy() no device found for strategy %d", strategy);
+ ALOGV("%s no device found for strategy %d", __func__, strategy);
sp<DeviceDescriptor> defaultOutputDevice = getApmObserver()->getDefaultOutputDevice();
if (defaultOutputDevice != nullptr) {
devices.add(defaultOutputDevice);
}
ALOGE_IF(devices.isEmpty(),
- "getDevicesForStrategy() no default device defined");
+ "%s no default device defined", __func__);
}
- ALOGVV("getDevices ForStrategy() strategy %d, device %s",
+ ALOGVV("%s strategy %d, device %s", __func__,
strategy, dumpDeviceTypes(devices.types()).c_str());
return devices;
}
@@ -483,7 +502,8 @@
break;
case AUDIO_DEVICE_OUT_SPEAKER:
device = availableDevices.getFirstExistingDevice({
- AUDIO_DEVICE_IN_BACK_MIC, AUDIO_DEVICE_IN_BUILTIN_MIC});
+ AUDIO_DEVICE_IN_BACK_MIC, AUDIO_DEVICE_IN_BUILTIN_MIC,
+ AUDIO_DEVICE_IN_USB_DEVICE, AUDIO_DEVICE_IN_USB_HEADSET});
break;
default: // FORCE_NONE
device = availableDevices.getFirstExistingDevice({
@@ -624,16 +644,21 @@
return preferredAvailableDevVec;
}
+
DeviceVector Engine::getDevicesForProductStrategy(product_strategy_t strategy) const {
- DeviceVector availableOutputDevices = getApmObserver()->getAvailableOutputDevices();
+ const SwAudioOutputCollection& outputs = getApmObserver()->getOutputs();
+
+ // Take context into account to remap product strategy before
+ // checking preferred device for strategy and applying default routing rules
+ strategy = remapStrategyFromContext(strategy, outputs);
+
auto legacyStrategy = mLegacyStrategyMap.find(strategy) != end(mLegacyStrategyMap) ?
mLegacyStrategyMap.at(strategy) : STRATEGY_NONE;
- // When not in call, STRATEGY_PHONE and STRATEGY_DTMF follow STRATEGY_MEDIA
- if (!isInCall() && (legacyStrategy == STRATEGY_PHONE || legacyStrategy == STRATEGY_DTMF)) {
- legacyStrategy = STRATEGY_MEDIA;
- strategy = getProductStrategyFromLegacy(STRATEGY_MEDIA);
- }
+ DeviceVector availableOutputDevices = getApmObserver()->getAvailableOutputDevices();
+
+ filterOutputDevicesForStrategy(legacyStrategy, availableOutputDevices, outputs);
+
// check if this strategy has a preferred device that is available,
// if yes, give priority to it.
DeviceVector preferredAvailableDevVec =
@@ -642,12 +667,9 @@
return preferredAvailableDevVec;
}
- DeviceVector availableInputDevices = getApmObserver()->getAvailableInputDevices();
- const SwAudioOutputCollection& outputs = getApmObserver()->getOutputs();
-
return getDevicesForStrategyInt(legacyStrategy,
availableOutputDevices,
- availableInputDevices, outputs);
+ outputs);
}
DeviceVector Engine::getOutputDevicesForAttributes(const audio_attributes_t &attributes,
@@ -683,6 +705,7 @@
}
sp<DeviceDescriptor> Engine::getInputDeviceForAttributes(const audio_attributes_t &attr,
+ uid_t uid,
sp<AudioPolicyMix> *mix) const
{
const auto &policyMixes = getApmObserver()->getAudioPolicyMixCollection();
@@ -702,7 +725,10 @@
return device;
}
- device = policyMixes.getDeviceAndMixForInputSource(attr.source, availableInputDevices, mix);
+ device = policyMixes.getDeviceAndMixForInputSource(attr.source,
+ availableInputDevices,
+ uid,
+ mix);
if (device != nullptr) {
return device;
}
diff --git a/services/audiopolicy/enginedefault/src/Engine.h b/services/audiopolicy/enginedefault/src/Engine.h
index 6214fe7..595e289 100644
--- a/services/audiopolicy/enginedefault/src/Engine.h
+++ b/services/audiopolicy/enginedefault/src/Engine.h
@@ -62,8 +62,10 @@
DeviceVector getOutputDevicesForStream(audio_stream_type_t stream,
bool fromCache = false) const override;
- sp<DeviceDescriptor> getInputDeviceForAttributes(
- const audio_attributes_t &attr, sp<AudioPolicyMix> *mix = nullptr) const override;
+ sp<DeviceDescriptor> getInputDeviceForAttributes(const audio_attributes_t &attr,
+ uid_t uid = 0,
+ sp<AudioPolicyMix> *mix = nullptr)
+ const override;
void updateDeviceSelectionCache() override;
@@ -74,9 +76,15 @@
status_t setDefaultDevice(audio_devices_t device);
+ void filterOutputDevicesForStrategy(legacy_strategy strategy,
+ DeviceVector& availableOutputDevices,
+ const SwAudioOutputCollection &outputs) const;
+
+ product_strategy_t remapStrategyFromContext(product_strategy_t strategy,
+ const SwAudioOutputCollection &outputs) const;
+
DeviceVector getDevicesForStrategyInt(legacy_strategy strategy,
DeviceVector availableOutputDevices,
- DeviceVector availableInputDevices,
const SwAudioOutputCollection &outputs) const;
DeviceVector getDevicesForProductStrategy(product_strategy_t strategy) const;
diff --git a/services/audiopolicy/fuzzer/Android.bp b/services/audiopolicy/fuzzer/Android.bp
new file mode 100644
index 0000000..38bdedc
--- /dev/null
+++ b/services/audiopolicy/fuzzer/Android.bp
@@ -0,0 +1,65 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_fuzz {
+ name: "audiopolicy_fuzzer",
+ srcs: [
+ "audiopolicy_fuzzer.cpp",
+ ],
+ include_dirs: [
+ "frameworks/av/services/audiopolicy",
+ ],
+ shared_libs: [
+ "android.hardware.audio.common-util",
+ "capture_state_listener-aidl-cpp",
+ "libaudioclient",
+ "libaudiofoundation",
+ "libbase",
+ "libcutils",
+ "libhidlbase",
+ "libdl",
+ "liblog",
+ "libmedia_helper",
+ "libmediametrics",
+ "libutils",
+ "libxml2",
+ "libbinder",
+ "libaudiopolicy",
+ "libaudiopolicymanagerdefault",
+ "media_permission-aidl-cpp",
+ ],
+ static_libs: [
+ "android.hardware.audio.common@7.0-enums",
+ "libaudiopolicycomponents",
+ ],
+ header_libs: [
+ "libaudiopolicycommon",
+ "libaudiopolicyengine_interface_headers",
+ "libaudiopolicymanager_interface_headers",
+ ],
+ data: [":audiopolicyfuzzer_configuration_files"],
+}
diff --git a/services/audiopolicy/fuzzer/README.md b/services/audiopolicy/fuzzer/README.md
new file mode 100644
index 0000000..08d7213
--- /dev/null
+++ b/services/audiopolicy/fuzzer/README.md
@@ -0,0 +1,63 @@
+# Fuzzer for libaudiopolicy
+
+## Plugin Design Considerations
+The fuzzer plugin for libaudiopolicy is designed based on the
+understanding of the service and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+AudioPolicy APIs contain the following parameters:
+1. AudioFormats
+2. AudioChannelMasks
+3. AudioOutputFlags
+4. AudioDevices
+5. MixTypes
+6. MixRouteFlags
+7. SampleRates
+8. AudioUsages
+9. AudioContentTypes
+10. AudioSources
+11. AudioFlagMasks
+12. AudioPolicyDeviceStates
+
+| Parameter| Valid Input Values| Configured Value|
+|------------- |-------------| ----- |
+| `AudioFormat` | 77 values of type `audio_format_t` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `AudioChannelMask` | 83 values of type `audio_channel_mask_t` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `AudioOutputFlag` | 16 values of type `audio_output_flags_t` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `AudioDevice` | `AUDIO_DEVICE_OUT_AUX_DIGITAL`, `AUDIO_DEVICE_OUT_STUB`, `AUDIO_DEVICE_IN_VOICE_CALL`, `AUDIO_DEVICE_IN_AUX_DIGITAL`, `AUDIO_DEVICE_IN_STUB` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `MixType` | `MIX_TYPE_PLAYERS`, `MIX_TYPE_RECORDERS` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `MixRouteFlag` | `MIX_ROUTE_FLAG_RENDER`, `MIX_ROUTE_FLAG_LOOP_BACK`, `MIX_ROUTE_FLAG_LOOP_BACK_AND_RENDER`, `MIX_ROUTE_FLAG_ALL` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `SampleRate` | `0` to `UINT32_MAX` | Value obtained from FuzzedDataProvider |
+| `AudioUsage` | `AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST`, `AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT`, `AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED`, `AUDIO_USAGE_NOTIFICATION_EVENT` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `AudioContentType` | `AUDIO_CONTENT_TYPE_UNKNOWN`, `AUDIO_CONTENT_TYPE_SPEECH`, `AUDIO_CONTENT_TYPE_MUSIC`, `AUDIO_CONTENT_TYPE_MOVIE`, `AUDIO_CONTENT_TYPE_SONIFICATION` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `AudioSource` | 14 values of type `audio_source_t` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `AudioFlagMask` | 15 values of type `audio_flags_mask_t` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `AudioPolicyDeviceStates` | `AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE`, `AUDIO_POLICY_DEVICE_STATE_AVAILABLE`, `AUDIO_POLICY_DEVICE_STATE_CNT` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+
+This also ensures that the plugin is always deterministic for any given input.
+
+## Build
+
+This describes steps to build audiopolicy_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+ $ mm -j$(nproc) audiopolicy_fuzzer
+```
+
+#### Steps to run
+To run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/audiopolicy_fuzzer/audiopolicy_fuzzer
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp b/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp
new file mode 100644
index 0000000..1177b95
--- /dev/null
+++ b/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp
@@ -0,0 +1,984 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+#include <stdint.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <Serializer.h>
+#include <android-base/file.h>
+#include <android/media/permission/Identity.h>
+#include <libxml/parser.h>
+#include <libxml/xinclude.h>
+#include <media/AudioPolicy.h>
+#include <media/PatchBuilder.h>
+#include <media/RecordingActivityTracker.h>
+
+#include <AudioPolicyInterface.h>
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <tests/AudioPolicyManagerTestClient.h>
+#include <tests/AudioPolicyTestClient.h>
+#include <tests/AudioPolicyTestManager.h>
+#include <xsdc/XsdcSupport.h>
+
+using namespace android;
+
+namespace xsd {
+using namespace ::android::audio::policy::configuration::V7_0;
+}
+
+using media::permission::Identity;
+
+static const std::vector<audio_format_t> kAudioFormats = [] {
+ std::vector<audio_format_t> result;
+ for (const auto enumVal : xsdc_enum_range<xsd::AudioFormat>{}) {
+ audio_format_t audioFormatHal;
+ std::string audioFormat = toString(enumVal);
+ if (audio_format_from_string(audioFormat.c_str(), &audioFormatHal)) {
+ result.push_back(audioFormatHal);
+ }
+ }
+ return result;
+}();
+
+static const std::vector<audio_channel_mask_t> kAudioChannelOutMasks = [] {
+ std::vector<audio_channel_mask_t> result;
+ for (const auto enumVal : xsdc_enum_range<xsd::AudioChannelMask>{}) {
+ audio_channel_mask_t audioChannelMaskHal;
+ std::string audioChannelMask = toString(enumVal);
+ if (enumVal != xsd::AudioChannelMask::AUDIO_CHANNEL_NONE &&
+ audioChannelMask.find("_IN_") == std::string::npos &&
+ audio_channel_mask_from_string(audioChannelMask.c_str(), &audioChannelMaskHal)) {
+ result.push_back(audioChannelMaskHal);
+ }
+ }
+ return result;
+}();
+
+static const std::vector<audio_channel_mask_t> kAudioChannelInMasks = [] {
+ std::vector<audio_channel_mask_t> result;
+ for (const auto enumVal : xsdc_enum_range<xsd::AudioChannelMask>{}) {
+ audio_channel_mask_t audioChannelMaskHal;
+ std::string audioChannelMask = toString(enumVal);
+ if (enumVal != xsd::AudioChannelMask::AUDIO_CHANNEL_NONE &&
+ audioChannelMask.find("_OUT_") == std::string::npos &&
+ audio_channel_mask_from_string(audioChannelMask.c_str(), &audioChannelMaskHal)) {
+ result.push_back(audioChannelMaskHal);
+ }
+ }
+ return result;
+}();
+
+static const std::vector<audio_output_flags_t> kAudioOutputFlags = [] {
+ std::vector<audio_output_flags_t> result;
+ for (const auto enumVal : xsdc_enum_range<xsd::AudioInOutFlag>{}) {
+ audio_output_flags_t audioOutputFlagHal;
+ std::string audioOutputFlag = toString(enumVal);
+ if (audioOutputFlag.find("_OUTPUT_") != std::string::npos &&
+ audio_output_flag_from_string(audioOutputFlag.c_str(), &audioOutputFlagHal)) {
+ result.push_back(audioOutputFlagHal);
+ }
+ }
+ return result;
+}();
+
+static const std::vector<audio_devices_t> kAudioDevices = [] {
+ std::vector<audio_devices_t> result;
+ for (const auto enumVal : xsdc_enum_range<xsd::AudioDevice>{}) {
+ audio_devices_t audioDeviceHal;
+ std::string audioDevice = toString(enumVal);
+ if (audio_device_from_string(audioDevice.c_str(), &audioDeviceHal)) {
+ result.push_back(audioDeviceHal);
+ }
+ }
+ return result;
+}();
+
+static const std::vector<audio_usage_t> kAudioUsages = [] {
+ std::vector<audio_usage_t> result;
+ for (const auto enumVal : xsdc_enum_range<xsd::AudioUsage>{}) {
+ audio_usage_t audioUsageHal;
+ std::string audioUsage = toString(enumVal);
+ if (audio_usage_from_string(audioUsage.c_str(), &audioUsageHal)) {
+ result.push_back(audioUsageHal);
+ }
+ }
+ return result;
+}();
+
+static const std::vector<audio_source_t> kAudioSources = [] {
+ std::vector<audio_source_t> result;
+ for (const auto enumVal : xsdc_enum_range<xsd::AudioSource>{}) {
+ audio_source_t audioSourceHal;
+ std::string audioSource = toString(enumVal);
+ if (audio_source_from_string(audioSource.c_str(), &audioSourceHal)) {
+ result.push_back(audioSourceHal);
+ }
+ }
+ return result;
+}();
+
+static const std::vector<audio_content_type_t> kAudioContentTypes = [] {
+ std::vector<audio_content_type_t> result;
+ for (const auto enumVal : xsdc_enum_range<xsd::AudioContentType>{}) {
+ audio_content_type_t audioContentTypeHal;
+ std::string audioContentType = toString(enumVal);
+ if (audio_content_type_from_string(audioContentType.c_str(), &audioContentTypeHal)) {
+ result.push_back(audioContentTypeHal);
+ }
+ }
+ return result;
+}();
+
+std::vector<int> kMixTypes = {MIX_TYPE_PLAYERS, MIX_TYPE_RECORDERS};
+
+std::vector<int> kMixRouteFlags = {MIX_ROUTE_FLAG_RENDER, MIX_ROUTE_FLAG_LOOP_BACK,
+ MIX_ROUTE_FLAG_LOOP_BACK_AND_RENDER, MIX_ROUTE_FLAG_ALL};
+
+std::vector<audio_flags_mask_t> kAudioFlagMasks = {
+ AUDIO_FLAG_NONE, AUDIO_FLAG_AUDIBILITY_ENFORCED,
+ AUDIO_FLAG_SECURE, AUDIO_FLAG_SCO,
+ AUDIO_FLAG_BEACON, AUDIO_FLAG_HW_AV_SYNC,
+ AUDIO_FLAG_HW_HOTWORD, AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY,
+ AUDIO_FLAG_BYPASS_MUTE, AUDIO_FLAG_LOW_LATENCY,
+ AUDIO_FLAG_DEEP_BUFFER, AUDIO_FLAG_NO_MEDIA_PROJECTION,
+ AUDIO_FLAG_MUTE_HAPTIC, AUDIO_FLAG_NO_SYSTEM_CAPTURE,
+ AUDIO_FLAG_CAPTURE_PRIVATE};
+
+std::vector<audio_policy_dev_state_t> kAudioPolicyDeviceStates = {
+ AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+ AUDIO_POLICY_DEVICE_STATE_CNT,
+};
+
+std::vector<uint32_t> kSamplingRates = {8000, 16000, 44100, 48000, 88200, 96000};
+
+template <typename T>
+T getValueFromVector(FuzzedDataProvider *fdp, std::vector<T> arr) {
+ if (fdp->ConsumeBool()) {
+ return arr[fdp->ConsumeIntegralInRange<int32_t>(0, arr.size() - 1)];
+ } else {
+ return (T)fdp->ConsumeIntegral<uint32_t>();
+ }
+}
+
+class AudioPolicyManagerFuzzer {
+ public:
+ explicit AudioPolicyManagerFuzzer(FuzzedDataProvider *fdp);
+ virtual ~AudioPolicyManagerFuzzer() = default;
+ virtual bool initialize();
+ virtual void SetUpManagerConfig();
+ bool getOutputForAttr(audio_port_handle_t *selectedDeviceId, audio_format_t format,
+ audio_channel_mask_t 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 = {});
+ bool getInputForAttr(const audio_attributes_t &attr, audio_unique_id_t riid,
+ audio_port_handle_t *selectedDeviceId, audio_format_t format,
+ audio_channel_mask_t channelMask, int sampleRate,
+ audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE,
+ audio_port_handle_t *portId = nullptr);
+ bool findDevicePort(audio_port_role_t role, audio_devices_t deviceType,
+ const std::string &address, audio_port_v7 *foundPort);
+ static audio_port_handle_t getDeviceIdFromPatch(const struct audio_patch *patch);
+ audio_patch createFuzzedPatch();
+ void fuzzPatchCreation();
+ virtual void process();
+
+ protected:
+ std::unique_ptr<AudioPolicyManagerTestClient> mClient{new AudioPolicyManagerTestClient};
+ std::unique_ptr<AudioPolicyTestManager> mManager{new AudioPolicyTestManager(mClient.get())};
+ FuzzedDataProvider *mFdp;
+};
+
+AudioPolicyManagerFuzzer::AudioPolicyManagerFuzzer(FuzzedDataProvider *fdp)
+ : mFdp(fdp) {}
+
+bool AudioPolicyManagerFuzzer::initialize() {
+ if (mFdp->remaining_bytes() < 1) {
+ return false;
+ }
+ // init code
+ SetUpManagerConfig();
+
+ if (mManager->initialize() != NO_ERROR) {
+ return false;
+ }
+ if (mManager->initCheck() != NO_ERROR) {
+ return false;
+ }
+ return true;
+}
+
+void AudioPolicyManagerFuzzer::SetUpManagerConfig() { mManager->getConfig().setDefault(); }
+
+bool AudioPolicyManagerFuzzer::getOutputForAttr(
+ audio_port_handle_t *selectedDeviceId, audio_format_t format, audio_channel_mask_t 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 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;
+ config.channel_mask = channelMask;
+ config.format = format;
+ audio_port_handle_t localPortId;
+ if (!portId) portId = &localPortId;
+ *portId = AUDIO_PORT_HANDLE_NONE;
+ AudioPolicyInterface::output_type_t outputType;
+
+ // TODO b/182392769: use identity util
+ Identity i;
+ i.uid = 0;
+ if (mManager->getOutputForAttr(&attr, output, AUDIO_SESSION_NONE, &stream, i, &config,
+ &flags, selectedDeviceId, portId, {}, &outputType) != OK) {
+ return false;
+ }
+ if (*output == AUDIO_IO_HANDLE_NONE || *portId == AUDIO_PORT_HANDLE_NONE) {
+ return false;
+ }
+ return true;
+}
+
+bool AudioPolicyManagerFuzzer::getInputForAttr(
+ const audio_attributes_t &attr, audio_unique_id_t riid, audio_port_handle_t *selectedDeviceId,
+ audio_format_t format, audio_channel_mask_t channelMask, int sampleRate,
+ audio_input_flags_t flags, audio_port_handle_t *portId) {
+ audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
+ audio_config_base_t config = AUDIO_CONFIG_BASE_INITIALIZER;
+ config.sample_rate = sampleRate;
+ config.channel_mask = channelMask;
+ config.format = format;
+ audio_port_handle_t localPortId;
+ if (!portId) portId = &localPortId;
+ *portId = AUDIO_PORT_HANDLE_NONE;
+ AudioPolicyInterface::input_type_t inputType;
+
+ Identity i;
+ i.uid = 0;
+ if (mManager->getInputForAttr(&attr, &input, riid, AUDIO_SESSION_NONE, i, &config,
+ flags, selectedDeviceId, &inputType, portId) != OK) {
+ return false;
+ }
+ if (*portId == AUDIO_PORT_HANDLE_NONE || input == AUDIO_IO_HANDLE_NONE) {
+ return false;
+ }
+ return true;
+}
+
+bool AudioPolicyManagerFuzzer::findDevicePort(audio_port_role_t role, audio_devices_t deviceType,
+ const std::string &address,
+ audio_port_v7 *foundPort) {
+ uint32_t numPorts = 0;
+ uint32_t generation1;
+ status_t ret;
+
+ ret = mManager->listAudioPorts(role, AUDIO_PORT_TYPE_DEVICE, &numPorts, nullptr, &generation1);
+ if (ret != NO_ERROR) {
+ return false;
+ }
+
+ uint32_t generation2;
+ struct audio_port_v7 ports[numPorts];
+ ret = mManager->listAudioPorts(role, AUDIO_PORT_TYPE_DEVICE, &numPorts, ports, &generation2);
+ if (ret != NO_ERROR) {
+ return false;
+ }
+
+ for (const auto &port : ports) {
+ if (port.role == role && port.ext.device.type == deviceType &&
+ (strncmp(port.ext.device.address, address.c_str(), AUDIO_DEVICE_MAX_ADDRESS_LEN) ==
+ 0)) {
+ if (foundPort) *foundPort = port;
+ return true;
+ }
+ }
+ return false;
+}
+
+audio_port_handle_t AudioPolicyManagerFuzzer::getDeviceIdFromPatch(
+ const struct audio_patch *patch) {
+ if (patch->num_sources != 0 && patch->num_sinks != 0) {
+ if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) {
+ return patch->sinks[0].id;
+ } else {
+ return patch->sources[0].id;
+ }
+ }
+ return AUDIO_PORT_HANDLE_NONE;
+}
+
+audio_patch AudioPolicyManagerFuzzer::createFuzzedPatch() {
+ audio_patch patch{};
+ patch.id = mFdp->ConsumeIntegral<uint32_t>();
+ patch.num_sources = mFdp->ConsumeIntegralInRange(0, AUDIO_PATCH_PORTS_MAX);
+ for (int i = 0; i < patch.num_sources; ++i) {
+ audio_port_config config{};
+ std::vector<uint8_t> bytes = mFdp->ConsumeBytes<uint8_t>(sizeof(config));
+ memcpy(reinterpret_cast<uint8_t *>(&config), &bytes[0], bytes.size());
+ patch.sources[i] = config;
+ }
+ patch.num_sinks = mFdp->ConsumeIntegralInRange(0, AUDIO_PATCH_PORTS_MAX);
+ for (int i = 0; i < patch.num_sinks; ++i) {
+ audio_port_config config{};
+ std::vector<uint8_t> bytes = mFdp->ConsumeBytes<uint8_t>(sizeof(config));
+ memcpy(reinterpret_cast<uint8_t *>(&config), &bytes[0], bytes.size());
+ patch.sinks[i] = config;
+ }
+ return patch;
+}
+
+void AudioPolicyManagerFuzzer::fuzzPatchCreation() {
+ if (mFdp->remaining_bytes()) {
+ audio_patch_handle_t handle = AUDIO_PATCH_HANDLE_NONE;
+ uid_t uid = mFdp->ConsumeIntegral<uint32_t>();
+
+ // create a fuzzed patch
+ handle = AUDIO_PATCH_HANDLE_NONE;
+ audio_patch patch = createFuzzedPatch();
+ uid = mFdp->ConsumeIntegral<uint32_t>();
+ if (mManager->createAudioPatch(&patch, &handle, uid) == NO_ERROR) {
+ mManager->releaseAudioPatch(handle, uid);
+ }
+ }
+}
+
+void AudioPolicyManagerFuzzer::process() {
+ if (initialize()) {
+ fuzzPatchCreation();
+ }
+}
+
+class AudioPolicyManagerFuzzerWithConfigurationFile : public AudioPolicyManagerFuzzer {
+ public:
+ explicit AudioPolicyManagerFuzzerWithConfigurationFile(FuzzedDataProvider *fdp)
+ : AudioPolicyManagerFuzzer(fdp){};
+
+ protected:
+ void SetUpManagerConfig() override;
+ virtual std::string getConfigFile();
+ void traverseAndFuzzXML(xmlDocPtr pDoc, xmlNodePtr curr);
+ std::string fuzzXML(std::string xmlPath);
+
+ static inline const std::string sExecutableDir = base::GetExecutableDirectory() + "/";
+ static inline const std::string sDefaultConfig =
+ sExecutableDir + "data/test_audio_policy_configuration.xml";
+ static inline const std::string sFuzzedConfig = sExecutableDir + "fuzzed.xml";;
+};
+
+std::string AudioPolicyManagerFuzzerWithConfigurationFile::getConfigFile() {
+ return fuzzXML(sDefaultConfig);
+}
+
+void AudioPolicyManagerFuzzerWithConfigurationFile::SetUpManagerConfig() {
+ deserializeAudioPolicyFile(getConfigFile().c_str(), &mManager->getConfig());
+}
+
+void AudioPolicyManagerFuzzerWithConfigurationFile::traverseAndFuzzXML(xmlDocPtr pDoc,
+ xmlNodePtr curr) {
+ if (curr == nullptr) {
+ return;
+ }
+
+ xmlAttr *attribute = curr->properties;
+ while (attribute) {
+ if (!xmlStrcmp(attribute->name, reinterpret_cast<const xmlChar *>("format"))) {
+ const char *newFormat =
+ audio_format_to_string(getValueFromVector<audio_format_t>(mFdp, kAudioFormats));
+ xmlSetProp(curr, attribute->name, reinterpret_cast<const xmlChar *>(newFormat));
+ }
+ if (!xmlStrcmp(attribute->name, reinterpret_cast<const xmlChar *>("flags"))) {
+ std::string newFlag = "";
+ uint16_t numFlags = std::max((uint16_t)1, mFdp->ConsumeIntegral<uint16_t>());
+ for (uint16_t i = 0; i < numFlags; ++i) {
+ newFlag += std::string(audio_output_flag_to_string(
+ getValueFromVector<audio_output_flags_t>(mFdp, kAudioOutputFlags)));
+ if (i != (numFlags - 1)) {
+ newFlag += std::string("|");
+ }
+ }
+ xmlSetProp(curr, attribute->name, reinterpret_cast<const xmlChar *>(newFlag.c_str()));
+ }
+ if (!xmlStrcmp(attribute->name, reinterpret_cast<const xmlChar *>("samplingRates"))) {
+ std::string newRate = "";
+ uint16_t numRates = std::max((uint16_t)1, mFdp->ConsumeIntegral<uint16_t>());
+ for (uint16_t i = 0; i < numRates; ++i) {
+ newRate += std::to_string(getValueFromVector<uint32_t>(mFdp, kSamplingRates));
+ if (i != (numRates - 1)) {
+ newRate += std::string(",");
+ }
+ }
+ xmlSetProp(curr, attribute->name, reinterpret_cast<const xmlChar *>(newRate.c_str()));
+ }
+ if (!xmlStrcmp(attribute->name, reinterpret_cast<const xmlChar *>("channelMasks"))) {
+ int isOutMask = -1;
+ char *value =
+ reinterpret_cast<char *>(xmlNodeListGetString(pDoc, attribute->children, 1));
+ if (std::string(value).find(std::string("_OUT_")) != std::string::npos) {
+ // OUT mask
+ isOutMask = 1;
+ } else if (std::string(value).find(std::string("_IN_")) != std::string::npos) {
+ // IN mask
+ isOutMask = 0;
+ }
+ if (isOutMask != -1) {
+ std::string newMask = "";
+ uint16_t numMasks = std::max((uint16_t)1, mFdp->ConsumeIntegral<uint16_t>());
+ for (uint16_t i = 0; i < numMasks; ++i) {
+ if (isOutMask) {
+ newMask += std::string(audio_channel_out_mask_to_string(
+ getValueFromVector<audio_channel_mask_t>(mFdp, kAudioChannelOutMasks)));
+ } else {
+ newMask += std::string(audio_channel_in_mask_to_string(
+ getValueFromVector<audio_channel_mask_t>(mFdp, kAudioChannelInMasks)));
+ }
+ if (i != (numMasks - 1)) {
+ newMask += std::string(",");
+ }
+ }
+ xmlSetProp(curr, attribute->name,
+ reinterpret_cast<const xmlChar *>(newMask.c_str()));
+ }
+ xmlFree(value);
+ }
+ attribute = attribute->next;
+ }
+
+ curr = curr->xmlChildrenNode;
+ while (curr != nullptr) {
+ traverseAndFuzzXML(pDoc, curr);
+ curr = curr->next;
+ }
+}
+
+std::string AudioPolicyManagerFuzzerWithConfigurationFile::fuzzXML(std::string xmlPath) {
+ std::string outPath = sFuzzedConfig;
+
+ // Load in the xml file from disk
+ xmlDocPtr pDoc = xmlParseFile(xmlPath.c_str());
+ xmlNodePtr root = xmlDocGetRootElement(pDoc);
+
+ traverseAndFuzzXML(pDoc, root);
+
+ // Save the document back out to disk.
+ xmlSaveFileEnc(outPath.c_str(), pDoc, "UTF-8");
+ xmlFreeDoc(pDoc);
+
+ return outPath;
+}
+
+class AudioPolicyManagerFuzzerMsd : public AudioPolicyManagerFuzzerWithConfigurationFile {
+ public:
+ explicit AudioPolicyManagerFuzzerMsd(FuzzedDataProvider *fdp)
+ : AudioPolicyManagerFuzzerWithConfigurationFile(fdp) {}
+
+ protected:
+ std::string getConfigFile() override;
+
+ static inline const std::string sMsdConfig =
+ sExecutableDir + "data/test_audio_policy_msd_configuration.xml";
+};
+
+std::string AudioPolicyManagerFuzzerMsd::getConfigFile() { return fuzzXML(sMsdConfig); }
+
+using PolicyMixTuple = std::tuple<audio_usage_t, audio_source_t, uint32_t>;
+
+class AudioPolicyManagerFuzzerDynamicPolicy : public AudioPolicyManagerFuzzerWithConfigurationFile {
+ public:
+ explicit AudioPolicyManagerFuzzerDynamicPolicy(FuzzedDataProvider *fdp)
+ : AudioPolicyManagerFuzzerWithConfigurationFile(fdp){};
+ ~AudioPolicyManagerFuzzerDynamicPolicy() override;
+ void process() override;
+
+ protected:
+ status_t addPolicyMix(int mixType, int mixFlag, audio_devices_t deviceType,
+ std::string mixAddress, const audio_config_t &audioConfig,
+ const std::vector<PolicyMixTuple> &rules);
+ void clearPolicyMix();
+ void registerPolicyMixes();
+ void unregisterPolicyMixes();
+
+ Vector<AudioMix> mAudioMixes;
+ const std::string mMixAddress = "remote_submix_media";
+};
+
+AudioPolicyManagerFuzzerDynamicPolicy::~AudioPolicyManagerFuzzerDynamicPolicy() {
+ clearPolicyMix();
+}
+
+status_t AudioPolicyManagerFuzzerDynamicPolicy::addPolicyMix(
+ int mixType, int mixFlag, audio_devices_t deviceType, std::string mixAddress,
+ const audio_config_t &audioConfig, const std::vector<PolicyMixTuple> &rules) {
+ Vector<AudioMixMatchCriterion> myMixMatchCriteria;
+
+ for (const auto &rule : rules) {
+ myMixMatchCriteria.add(
+ AudioMixMatchCriterion(std::get<0>(rule), std::get<1>(rule), std::get<2>(rule)));
+ }
+
+ AudioMix myAudioMix(myMixMatchCriteria, mixType, audioConfig, mixFlag,
+ String8(mixAddress.c_str()), 0);
+ myAudioMix.mDeviceType = deviceType;
+ // Clear mAudioMix before add new one to make sure we don't add already existing mixes.
+ mAudioMixes.clear();
+ mAudioMixes.add(myAudioMix);
+
+ // As the policy mixes registration may fail at some case,
+ // caller need to check the returned status.
+ status_t ret = mManager->registerPolicyMixes(mAudioMixes);
+ return ret;
+}
+
+void AudioPolicyManagerFuzzerDynamicPolicy::clearPolicyMix() {
+ if (mManager != nullptr) {
+ mManager->unregisterPolicyMixes(mAudioMixes);
+ }
+ mAudioMixes.clear();
+}
+
+void AudioPolicyManagerFuzzerDynamicPolicy::registerPolicyMixes() {
+ const uint32_t numPolicies = mFdp->ConsumeIntegralInRange<uint32_t>(1, MAX_MIXES_PER_POLICY);
+
+ for (int i = 0; i < numPolicies; ++i) {
+ audio_config_t audioConfig = AUDIO_CONFIG_INITIALIZER;
+ audioConfig.channel_mask = getValueFromVector<audio_channel_mask_t>(
+ mFdp, mFdp->ConsumeBool() ? kAudioChannelInMasks : kAudioChannelOutMasks);
+ audioConfig.format = getValueFromVector<audio_format_t>(mFdp, kAudioFormats);
+ audioConfig.sample_rate = getValueFromVector<uint32_t>(mFdp, kSamplingRates);
+ addPolicyMix(getValueFromVector<int>(mFdp, kMixTypes),
+ getValueFromVector<int>(mFdp, kMixRouteFlags),
+ getValueFromVector<audio_devices_t>(mFdp, kAudioDevices), "", audioConfig,
+ std::vector<PolicyMixTuple>());
+ }
+}
+
+void AudioPolicyManagerFuzzerDynamicPolicy::unregisterPolicyMixes() {
+ mManager->unregisterPolicyMixes(mAudioMixes);
+}
+
+void AudioPolicyManagerFuzzerDynamicPolicy::process() {
+ if (initialize()) {
+ registerPolicyMixes();
+ fuzzPatchCreation();
+ unregisterPolicyMixes();
+ }
+}
+
+class AudioPolicyManagerFuzzerDPNoRemoteSubmixModule
+ : public AudioPolicyManagerFuzzerDynamicPolicy {
+ public:
+ explicit AudioPolicyManagerFuzzerDPNoRemoteSubmixModule(FuzzedDataProvider *fdp)
+ : AudioPolicyManagerFuzzerDynamicPolicy(fdp){};
+
+ protected:
+ std::string getConfigFile() override;
+
+ static inline const std::string sPrimaryOnlyConfig =
+ sExecutableDir + "data/test_audio_policy_primary_only_configuration.xml";
+};
+
+std::string AudioPolicyManagerFuzzerDPNoRemoteSubmixModule::getConfigFile() {
+ return fuzzXML(sPrimaryOnlyConfig);
+}
+
+class AudioPolicyManagerFuzzerDPPlaybackReRouting : public AudioPolicyManagerFuzzerDynamicPolicy {
+ public:
+ explicit AudioPolicyManagerFuzzerDPPlaybackReRouting(FuzzedDataProvider *fdp);
+ ~AudioPolicyManagerFuzzerDPPlaybackReRouting() override;
+ void process() override;
+
+ protected:
+ bool initialize() override;
+ void playBackReRouting();
+
+ std::unique_ptr<RecordingActivityTracker> mTracker;
+
+ std::vector<PolicyMixTuple> mUsageRules = {
+ {AUDIO_USAGE_MEDIA, AUDIO_SOURCE_DEFAULT, RULE_MATCH_ATTRIBUTE_USAGE},
+ {AUDIO_USAGE_ALARM, AUDIO_SOURCE_DEFAULT, RULE_MATCH_ATTRIBUTE_USAGE}};
+
+ struct audio_port_v7 mInjectionPort;
+ audio_port_handle_t mPortId = AUDIO_PORT_HANDLE_NONE;
+ audio_config_t mAudioConfig;
+};
+
+AudioPolicyManagerFuzzerDPPlaybackReRouting::AudioPolicyManagerFuzzerDPPlaybackReRouting(
+ FuzzedDataProvider *fdp)
+ : AudioPolicyManagerFuzzerDynamicPolicy(fdp) {
+ const uint32_t numRules = mFdp->ConsumeIntegralInRange<uint32_t>(1, 10);
+ for (int i = 0; i < numRules; ++i) {
+ PolicyMixTuple rule = {getValueFromVector<audio_usage_t>(mFdp, kAudioUsages),
+ getValueFromVector<audio_source_t>(mFdp, kAudioSources),
+ RULE_MATCH_ATTRIBUTE_USAGE};
+ mUsageRules.push_back(rule);
+ }
+}
+
+AudioPolicyManagerFuzzerDPPlaybackReRouting::~AudioPolicyManagerFuzzerDPPlaybackReRouting() {
+ mManager->stopInput(mPortId);
+}
+
+bool AudioPolicyManagerFuzzerDPPlaybackReRouting::initialize() {
+ if (!AudioPolicyManagerFuzzerDynamicPolicy::initialize()) {
+ return false;
+ }
+ mTracker.reset(new RecordingActivityTracker());
+
+ mAudioConfig = AUDIO_CONFIG_INITIALIZER;
+ mAudioConfig.channel_mask =
+ getValueFromVector<audio_channel_mask_t>(mFdp, kAudioChannelOutMasks);
+ mAudioConfig.format = getValueFromVector<audio_format_t>(mFdp, kAudioFormats);
+ mAudioConfig.sample_rate = getValueFromVector<uint32_t>(mFdp, kSamplingRates);
+ status_t ret = addPolicyMix(getValueFromVector<int>(mFdp, kMixTypes),
+ getValueFromVector<int>(mFdp, kMixRouteFlags),
+ getValueFromVector<audio_devices_t>(mFdp, kAudioDevices),
+ mMixAddress, mAudioConfig, mUsageRules);
+ if (ret != NO_ERROR) {
+ return false;
+ }
+
+ struct audio_port_v7 extractionPort;
+ findDevicePort(AUDIO_PORT_ROLE_SOURCE, getValueFromVector<audio_devices_t>(mFdp, kAudioDevices),
+ mMixAddress, &extractionPort);
+
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+ audio_source_t source = getValueFromVector<audio_source_t>(mFdp, kAudioSources);
+ audio_attributes_t attr = {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, source,
+ AUDIO_FLAG_NONE, ""};
+ std::string tags = "addr=" + mMixAddress;
+ strncpy(attr.tags, tags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
+ getInputForAttr(attr, mTracker->getRiid(), &selectedDeviceId, mAudioConfig.format,
+ mAudioConfig.channel_mask, mAudioConfig.sample_rate, AUDIO_INPUT_FLAG_NONE,
+ &mPortId);
+
+ ret = mManager->startInput(mPortId);
+ if (ret != NO_ERROR) {
+ return false;
+ }
+ if (!findDevicePort(AUDIO_PORT_ROLE_SINK,
+ getValueFromVector<audio_devices_t>(mFdp, kAudioDevices), mMixAddress,
+ &mInjectionPort)) {
+ return false;
+ }
+
+ return true;
+}
+
+void AudioPolicyManagerFuzzerDPPlaybackReRouting::playBackReRouting() {
+ const uint32_t numTestCases = mFdp->ConsumeIntegralInRange<uint32_t>(1, 10);
+ for (int i = 0; i < numTestCases; ++i) {
+ audio_attributes_t attr;
+ attr.content_type = getValueFromVector<audio_content_type_t>(mFdp, kAudioContentTypes);
+ attr.usage = getValueFromVector<audio_usage_t>(mFdp, kAudioUsages);
+ attr.source = getValueFromVector<audio_source_t>(mFdp, kAudioSources);
+ attr.flags = getValueFromVector<audio_flags_mask_t>(mFdp, kAudioFlagMasks);
+ std::string tags(mFdp->ConsumeBool() ? "" : "addr=remote_submix_media");
+ strncpy(attr.tags, tags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
+
+ audio_port_handle_t playbackRoutedPortId = AUDIO_PORT_HANDLE_NONE;
+ getOutputForAttr(&playbackRoutedPortId, mAudioConfig.format, mAudioConfig.channel_mask,
+ mAudioConfig.sample_rate, AUDIO_OUTPUT_FLAG_NONE, nullptr /*output*/,
+ nullptr /*portId*/, attr);
+ }
+}
+
+void AudioPolicyManagerFuzzerDPPlaybackReRouting::process() {
+ if (initialize()) {
+ playBackReRouting();
+ registerPolicyMixes();
+ fuzzPatchCreation();
+ unregisterPolicyMixes();
+ }
+}
+
+class AudioPolicyManagerFuzzerDPMixRecordInjection : public AudioPolicyManagerFuzzerDynamicPolicy {
+ public:
+ explicit AudioPolicyManagerFuzzerDPMixRecordInjection(FuzzedDataProvider *fdp);
+ ~AudioPolicyManagerFuzzerDPMixRecordInjection() override;
+ void process() override;
+
+ protected:
+ bool initialize() override;
+ void recordingInjection();
+
+ std::unique_ptr<RecordingActivityTracker> mTracker;
+
+ std::vector<PolicyMixTuple> mSourceRules = {
+ {AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_CAMCORDER, RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET},
+ {AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_MIC, RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET},
+ {AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_VOICE_COMMUNICATION,
+ RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET}};
+
+ struct audio_port_v7 mExtractionPort;
+ audio_port_handle_t mPortId = AUDIO_PORT_HANDLE_NONE;
+ audio_config_t mAudioConfig;
+};
+
+AudioPolicyManagerFuzzerDPMixRecordInjection::AudioPolicyManagerFuzzerDPMixRecordInjection(
+ FuzzedDataProvider *fdp)
+ : AudioPolicyManagerFuzzerDynamicPolicy(fdp) {
+ const uint32_t numRules = mFdp->ConsumeIntegralInRange<uint32_t>(1, 10);
+ for (int i = 0; i < numRules; ++i) {
+ PolicyMixTuple rule = {getValueFromVector<audio_usage_t>(mFdp, kAudioUsages),
+ getValueFromVector<audio_source_t>(mFdp, kAudioSources),
+ RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET};
+ mSourceRules.push_back(rule);
+ }
+}
+
+AudioPolicyManagerFuzzerDPMixRecordInjection::~AudioPolicyManagerFuzzerDPMixRecordInjection() {
+ mManager->stopOutput(mPortId);
+}
+
+bool AudioPolicyManagerFuzzerDPMixRecordInjection::initialize() {
+ if (!AudioPolicyManagerFuzzerDynamicPolicy::initialize()) {
+ return false;
+ }
+
+ mTracker.reset(new RecordingActivityTracker());
+
+ mAudioConfig = AUDIO_CONFIG_INITIALIZER;
+ mAudioConfig.channel_mask =
+ getValueFromVector<audio_channel_mask_t>(mFdp, kAudioChannelInMasks);
+ mAudioConfig.format = getValueFromVector<audio_format_t>(mFdp, kAudioFormats);
+ mAudioConfig.sample_rate = getValueFromVector<uint32_t>(mFdp, kSamplingRates);
+ status_t ret = addPolicyMix(getValueFromVector<int>(mFdp, kMixTypes),
+ getValueFromVector<int>(mFdp, kMixRouteFlags),
+ getValueFromVector<audio_devices_t>(mFdp, kAudioDevices),
+ mMixAddress, mAudioConfig, mSourceRules);
+ if (ret != NO_ERROR) {
+ return false;
+ }
+
+ struct audio_port_v7 injectionPort;
+ findDevicePort(AUDIO_PORT_ROLE_SINK, getValueFromVector<audio_devices_t>(mFdp, kAudioDevices),
+ mMixAddress, &injectionPort);
+
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+ audio_usage_t usage = getValueFromVector<audio_usage_t>(mFdp, kAudioUsages);
+ audio_attributes_t attr = {AUDIO_CONTENT_TYPE_UNKNOWN, usage, AUDIO_SOURCE_DEFAULT,
+ AUDIO_FLAG_NONE, ""};
+ std::string tags = std::string("addr=") + mMixAddress;
+ strncpy(attr.tags, tags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
+ getOutputForAttr(&selectedDeviceId, mAudioConfig.format, mAudioConfig.channel_mask,
+ mAudioConfig.sample_rate /*sampleRate*/, AUDIO_OUTPUT_FLAG_NONE,
+ nullptr /*output*/, &mPortId, attr);
+ ret = mManager->startOutput(mPortId);
+ if (ret != NO_ERROR) {
+ return false;
+ }
+ getDeviceIdFromPatch(mClient->getLastAddedPatch());
+ if (!findDevicePort(AUDIO_PORT_ROLE_SOURCE,
+ getValueFromVector<audio_devices_t>(mFdp, kAudioDevices), mMixAddress,
+ &mExtractionPort)) {
+ return false;
+ }
+
+ return true;
+}
+
+void AudioPolicyManagerFuzzerDPMixRecordInjection::recordingInjection() {
+ const uint32_t numTestCases = mFdp->ConsumeIntegralInRange<uint32_t>(1, 10);
+ for (int i = 0; i < numTestCases; ++i) {
+ audio_attributes_t attr;
+ attr.content_type = getValueFromVector<audio_content_type_t>(mFdp, kAudioContentTypes);
+ attr.usage = getValueFromVector<audio_usage_t>(mFdp, kAudioUsages);
+ attr.source = getValueFromVector<audio_source_t>(mFdp, kAudioSources);
+ attr.flags = getValueFromVector<audio_flags_mask_t>(mFdp, kAudioFlagMasks);
+ std::string tags(mFdp->ConsumeBool() ? "" : "addr=remote_submix_media");
+ strncpy(attr.tags, tags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
+
+ audio_port_handle_t captureRoutedPortId = AUDIO_PORT_HANDLE_NONE;
+ audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
+ getInputForAttr(attr, mTracker->getRiid(), &captureRoutedPortId, mAudioConfig.format,
+ mAudioConfig.channel_mask, mAudioConfig.sample_rate, AUDIO_INPUT_FLAG_NONE,
+ &portId);
+ }
+}
+
+void AudioPolicyManagerFuzzerDPMixRecordInjection::process() {
+ if (initialize()) {
+ recordingInjection();
+ registerPolicyMixes();
+ fuzzPatchCreation();
+ unregisterPolicyMixes();
+ }
+}
+
+using DeviceConnectionTestParams =
+ std::tuple<audio_devices_t /*type*/, std::string /*name*/, std::string /*address*/>;
+
+class AudioPolicyManagerFuzzerDeviceConnection
+ : public AudioPolicyManagerFuzzerWithConfigurationFile {
+ public:
+ explicit AudioPolicyManagerFuzzerDeviceConnection(FuzzedDataProvider *fdp)
+ : AudioPolicyManagerFuzzerWithConfigurationFile(fdp){};
+ void process() override;
+
+ protected:
+ void setDeviceConnectionState();
+ void explicitlyRoutingAfterConnection();
+};
+
+void AudioPolicyManagerFuzzerDeviceConnection::setDeviceConnectionState() {
+ const uint32_t numTestCases = mFdp->ConsumeIntegralInRange<uint32_t>(1, 10);
+ for (int i = 0; i < numTestCases; ++i) {
+ const audio_devices_t type = getValueFromVector<audio_devices_t>(mFdp, kAudioDevices);
+ const std::string name = mFdp->ConsumeRandomLengthString();
+ const std::string address = mFdp->ConsumeRandomLengthString();
+ mManager->setDeviceConnectionState(
+ type, getValueFromVector<audio_policy_dev_state_t>(mFdp, kAudioPolicyDeviceStates),
+ address.c_str(), name.c_str(), getValueFromVector<audio_format_t>(mFdp, kAudioFormats));
+ }
+}
+
+void AudioPolicyManagerFuzzerDeviceConnection::explicitlyRoutingAfterConnection() {
+ const uint32_t numTestCases = mFdp->ConsumeIntegralInRange<uint32_t>(1, 10);
+ for (int i = 0; i < numTestCases; ++i) {
+ const audio_devices_t type = getValueFromVector<audio_devices_t>(mFdp, kAudioDevices);
+ const std::string name = mFdp->ConsumeRandomLengthString();
+ const std::string address = mFdp->ConsumeRandomLengthString();
+ mManager->setDeviceConnectionState(
+ type, getValueFromVector<audio_policy_dev_state_t>(mFdp, kAudioPolicyDeviceStates),
+ address.c_str(), name.c_str(), getValueFromVector<audio_format_t>(mFdp, kAudioFormats));
+
+ audio_port_v7 devicePort;
+ const audio_port_role_t role =
+ audio_is_output_device(type) ? AUDIO_PORT_ROLE_SINK : AUDIO_PORT_ROLE_SOURCE;
+ findDevicePort(role, type, address, &devicePort);
+
+ audio_port_handle_t routedPortId = devicePort.id;
+ // Try start input or output according to the device type
+ if (audio_is_output_devices(type)) {
+ getOutputForAttr(&routedPortId, getValueFromVector<audio_format_t>(mFdp, kAudioFormats),
+ getValueFromVector<audio_channel_mask_t>(mFdp, kAudioChannelOutMasks),
+ getValueFromVector<uint32_t>(mFdp, kSamplingRates),
+ AUDIO_OUTPUT_FLAG_NONE);
+ } else if (audio_is_input_device(type)) {
+ RecordingActivityTracker tracker;
+ getInputForAttr({}, tracker.getRiid(), &routedPortId,
+ getValueFromVector<audio_format_t>(mFdp, kAudioFormats),
+ getValueFromVector<audio_channel_mask_t>(mFdp, kAudioChannelInMasks),
+ getValueFromVector<uint32_t>(mFdp, kSamplingRates),
+ AUDIO_INPUT_FLAG_NONE);
+ }
+ }
+}
+
+void AudioPolicyManagerFuzzerDeviceConnection::process() {
+ if (initialize()) {
+ setDeviceConnectionState();
+ explicitlyRoutingAfterConnection();
+ fuzzPatchCreation();
+ }
+}
+
+class AudioPolicyManagerTVFuzzer : public AudioPolicyManagerFuzzerWithConfigurationFile {
+ public:
+ explicit AudioPolicyManagerTVFuzzer(FuzzedDataProvider *fdp)
+ : AudioPolicyManagerFuzzerWithConfigurationFile(fdp){};
+ void process() override;
+
+ protected:
+ std::string getConfigFile();
+ void testHDMIPortSelection(audio_output_flags_t flags);
+
+ static inline const std::string sTvConfig =
+ AudioPolicyManagerTVFuzzer::sExecutableDir + "data/test_tv_apm_configuration.xml";
+};
+
+std::string AudioPolicyManagerTVFuzzer::getConfigFile() { return fuzzXML(sTvConfig); }
+
+void AudioPolicyManagerTVFuzzer::testHDMIPortSelection(audio_output_flags_t flags) {
+ audio_devices_t audioDevice = getValueFromVector<audio_devices_t>(mFdp, kAudioDevices);
+ audio_format_t audioFormat = getValueFromVector<audio_format_t>(mFdp, kAudioFormats);
+ status_t ret = mManager->setDeviceConnectionState(
+ audioDevice, AUDIO_POLICY_DEVICE_STATE_AVAILABLE, "" /*address*/, "" /*name*/, audioFormat);
+ if (ret != NO_ERROR) {
+ return;
+ }
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+ audio_io_handle_t output;
+ audio_port_handle_t portId;
+ getOutputForAttr(&selectedDeviceId, getValueFromVector<audio_format_t>(mFdp, kAudioFormats),
+ getValueFromVector<audio_channel_mask_t>(mFdp, kAudioChannelOutMasks),
+ getValueFromVector<uint32_t>(mFdp, kSamplingRates), flags, &output, &portId);
+ sp<SwAudioOutputDescriptor> outDesc = mManager->getOutputs().valueFor(output);
+ if (outDesc.get() == nullptr) {
+ return;
+ }
+ audio_port_v7 port = {};
+ outDesc->toAudioPort(&port);
+ mManager->releaseOutput(portId);
+ mManager->setDeviceConnectionState(audioDevice, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ "" /*address*/, "" /*name*/, audioFormat);
+}
+
+void AudioPolicyManagerTVFuzzer::process() {
+ if (initialize()) {
+ testHDMIPortSelection(getValueFromVector<audio_output_flags_t>(mFdp, kAudioOutputFlags));
+ fuzzPatchCreation();
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 1) {
+ return 0;
+ }
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ while (fdp.remaining_bytes() > 0) {
+ AudioPolicyManagerFuzzer audioPolicyManagerFuzzer(&fdp);
+ audioPolicyManagerFuzzer.process();
+
+ AudioPolicyManagerFuzzerMsd audioPolicyManagerFuzzerMsd(&fdp);
+ audioPolicyManagerFuzzerMsd.process();
+
+ AudioPolicyManagerFuzzerWithConfigurationFile audioPolicyManagerFuzzerWithConfigurationFile(
+ &fdp);
+ audioPolicyManagerFuzzerWithConfigurationFile.process();
+
+ AudioPolicyManagerFuzzerDynamicPolicy audioPolicyManagerFuzzerDynamicPolicy(&fdp);
+ audioPolicyManagerFuzzerDynamicPolicy.process();
+
+ AudioPolicyManagerFuzzerDPNoRemoteSubmixModule
+ audioPolicyManagerFuzzerDPNoRemoteSubmixModule(&fdp);
+ audioPolicyManagerFuzzerDPNoRemoteSubmixModule.process();
+
+ AudioPolicyManagerFuzzerDPPlaybackReRouting audioPolicyManagerFuzzerDPPlaybackReRouting(
+ &fdp);
+ audioPolicyManagerFuzzerDPPlaybackReRouting.process();
+
+ AudioPolicyManagerFuzzerDPMixRecordInjection audioPolicyManagerFuzzerDPMixRecordInjection(
+ &fdp);
+ audioPolicyManagerFuzzerDPMixRecordInjection.process();
+
+ AudioPolicyManagerFuzzerDeviceConnection audioPolicyManagerFuzzerDeviceConnection(&fdp);
+ audioPolicyManagerFuzzerDeviceConnection.process();
+
+ AudioPolicyManagerTVFuzzer audioPolicyManagerTVFuzzer(&fdp);
+ audioPolicyManagerTVFuzzer.process();
+ }
+ return 0;
+}
diff --git a/services/audiopolicy/fuzzer/resources/Android.bp b/services/audiopolicy/fuzzer/resources/Android.bp
new file mode 100644
index 0000000..22ee256
--- /dev/null
+++ b/services/audiopolicy/fuzzer/resources/Android.bp
@@ -0,0 +1,36 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+filegroup {
+ name: "audiopolicyfuzzer_configuration_files",
+ srcs: [
+ "test_audio_policy_configuration.xml",
+ "test_audio_policy_msd_configuration.xml",
+ "test_audio_policy_primary_only_configuration.xml",
+ "test_tv_apm_configuration.xml",
+ ],
+}
diff --git a/services/audiopolicy/fuzzer/resources/test_audio_policy_configuration.xml b/services/audiopolicy/fuzzer/resources/test_audio_policy_configuration.xml
new file mode 100644
index 0000000..7e26c33
--- /dev/null
+++ b/services/audiopolicy/fuzzer/resources/test_audio_policy_configuration.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <globalConfiguration speaker_drc_enabled="true"/>
+
+ <modules>
+ <!-- Primary module -->
+ <module name="primary" halVersion="2.0">
+ <attachedDevices>
+ <item>Speaker</item>
+ <item>Built-In Mic</item>
+ </attachedDevices>
+ <defaultOutputDevice>Speaker</defaultOutputDevice>
+ <mixPorts>
+ <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>
+ <mixPort name="primary input" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_bt_hfp_output" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_bt_hfp_input" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="8000,11025,16000,44100,48000"
+ channelMasks="AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_MONO"/>
+ </mixPort>
+ </mixPorts>
+ <devicePorts>
+ <devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" role="sink">
+ </devicePort>
+ <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
+ </devicePort>
+ <devicePort tagName="Hdmi" type="AUDIO_DEVICE_OUT_HDMI" role="sink">
+ </devicePort>
+ <devicePort tagName="Hdmi-In Mic" type="AUDIO_DEVICE_IN_HDMI" role="source">
+ </devicePort>
+ <devicePort tagName="BT SCO" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO"
+ role="sink" address="hfp_client_out">
+ </devicePort>
+ <devicePort tagName="BT SCO Headset Mic" type="AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET"
+ role="source" address="hfp_client_in">
+ </devicePort>
+ </devicePorts>
+ <routes>
+ <route type="mix" sink="Speaker"
+ sources="primary output"/>
+ <route type="mix" sink="primary input"
+ sources="Built-In Mic,Hdmi-In Mic"/>
+ <route type="mix" sink="Hdmi"
+ sources="primary output"/>
+ <route type="mix" sink="BT SCO"
+ sources="mixport_bt_hfp_output"/>
+ <route type="mix" sink="mixport_bt_hfp_input"
+ sources="BT SCO Headset Mic"/>
+ </routes>
+ </module>
+
+ <!-- Remote Submix module -->
+ <module name="r_submix" halVersion="2.0">
+ <attachedDevices>
+ <item>Remote Submix In</item>
+ </attachedDevices>
+ <mixPorts>
+ <mixPort name="r_submix output" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="r_submix input" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+ </mixPort>
+ </mixPorts>
+ <devicePorts>
+ <devicePort tagName="Remote Submix Out" type="AUDIO_DEVICE_OUT_REMOTE_SUBMIX" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </devicePort>
+ <devicePort tagName="Remote Submix In" type="AUDIO_DEVICE_IN_REMOTE_SUBMIX" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+ </devicePort>
+ </devicePorts>
+ <routes>
+ <route type="mix" sink="Remote Submix Out"
+ sources="r_submix output"/>
+ <route type="mix" sink="r_submix input"
+ sources="Remote Submix In"/>
+ </routes>
+ </module>
+ </modules>
+</audioPolicyConfiguration>
diff --git a/services/audiopolicy/fuzzer/resources/test_audio_policy_msd_configuration.xml b/services/audiopolicy/fuzzer/resources/test_audio_policy_msd_configuration.xml
new file mode 100644
index 0000000..5248d79
--- /dev/null
+++ b/services/audiopolicy/fuzzer/resources/test_audio_policy_msd_configuration.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <globalConfiguration speaker_drc_enabled="true"/>
+
+ <modules>
+ <module name="msd" halVersion="2.0">
+ <attachedDevices>
+ <item>MS12 Input</item>
+ <item>MS12 Output</item>
+ </attachedDevices>
+ <mixPorts>
+ <mixPort name="ms12 input" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="ms12 compressed input" role="source"
+ flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD|AUDIO_OUTPUT_FLAG_NON_BLOCKING">
+ <profile name="" format="AUDIO_FORMAT_AC3"
+ samplingRates="32000,44100,48000"
+ channelMasks="AUDIO_CHANNEL_OUT_MONO|AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_5POINT1"/>
+ <profile name="" format="AUDIO_FORMAT_E_AC3"
+ samplingRates="32000,44100,48000"
+ channelMasks="AUDIO_CHANNEL_OUT_MONO|AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_5POINT1|AUDIO_CHANNEL_OUT_7POINT1"/>
+ <profile name="" format="AUDIO_FORMAT_E_AC3_JOC"
+ samplingRates="32000,44100,48000"
+ channelMasks="AUDIO_CHANNEL_OUT_MONO|AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_5POINT1|AUDIO_CHANNEL_OUT_7POINT1"/>
+ <profile name="" format="AUDIO_FORMAT_AC4"
+ samplingRates="32000,44100,48000"
+ 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|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"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_5POINT1"/>
+ <profile name="" format="AUDIO_FORMAT_E_AC3"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_5POINT1"/>
+ </mixPort>
+ </mixPorts>
+ <devicePorts>
+ <devicePort tagName="MS12 Input" type="AUDIO_DEVICE_OUT_BUS" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <profile name="" format="AUDIO_FORMAT_AC3"
+ samplingRates="32000,44100,48000"
+ channelMasks="AUDIO_CHANNEL_OUT_MONO|AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_5POINT1"/>
+ <profile name="" format="AUDIO_FORMAT_E_AC3"
+ samplingRates="32000,44100,48000"
+ channelMasks="AUDIO_CHANNEL_OUT_MONO|AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_5POINT1|AUDIO_CHANNEL_OUT_7POINT1"/>
+ <profile name="" format="AUDIO_FORMAT_E_AC3_JOC"
+ samplingRates="32000,44100,48000"
+ channelMasks="AUDIO_CHANNEL_OUT_MONO|AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_5POINT1|AUDIO_CHANNEL_OUT_7POINT1"/>
+ <profile name="" format="AUDIO_FORMAT_AC4"
+ samplingRates="32000,44100,48000"
+ channelMasks="AUDIO_CHANNEL_OUT_MONO|AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_5POINT1|AUDIO_CHANNEL_OUT_7POINT1"/>
+ </devicePort>
+ <devicePort tagName="MS12 Output" type="AUDIO_DEVICE_IN_BUS" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+ </devicePort>
+ </devicePorts>
+ <routes>
+ <route type="mix" sink="MS12 Input" sources="ms12 input,ms12 compressed input"/>
+ <route type="mix" sink="ms12 output" sources="MS12 Output"/>
+ </routes>
+ </module>
+ </modules>
+</audioPolicyConfiguration>
diff --git a/services/audiopolicy/fuzzer/resources/test_audio_policy_primary_only_configuration.xml b/services/audiopolicy/fuzzer/resources/test_audio_policy_primary_only_configuration.xml
new file mode 100644
index 0000000..15e3773
--- /dev/null
+++ b/services/audiopolicy/fuzzer/resources/test_audio_policy_primary_only_configuration.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <globalConfiguration speaker_drc_enabled="true"/>
+
+ <modules>
+ <!-- Primary module -->
+ <module name="primary" halVersion="2.0">
+ <attachedDevices>
+ <item>Speaker</item>
+ <item>Built-In Mic</item>
+ </attachedDevices>
+ <defaultOutputDevice>Speaker</defaultOutputDevice>
+ <mixPorts>
+ <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>
+ <mixPort name="primary input" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+ </mixPort>
+ </mixPorts>
+ <devicePorts>
+ <devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" role="sink">
+ </devicePort>
+ <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
+ </devicePort>
+ </devicePorts>
+ <routes>
+ <route type="mix" sink="Speaker"
+ sources="primary output"/>
+ <route type="mix" sink="primary input"
+ sources="Built-In Mic"/>
+ </routes>
+ </module>
+ </modules>
+</audioPolicyConfiguration>
diff --git a/services/audiopolicy/fuzzer/resources/test_tv_apm_configuration.xml b/services/audiopolicy/fuzzer/resources/test_tv_apm_configuration.xml
new file mode 100644
index 0000000..658d3ce
--- /dev/null
+++ b/services/audiopolicy/fuzzer/resources/test_tv_apm_configuration.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<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/audiopolicy/manager/Android.mk b/services/audiopolicy/manager/Android.mk
deleted file mode 100644
index 6fc8388..0000000
--- a/services/audiopolicy/manager/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-ifneq ($(USE_CUSTOM_AUDIO_POLICY), 1)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- AudioPolicyFactory.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- libaudiopolicymanagerdefault
-
-LOCAL_STATIC_LIBRARIES := \
- libaudiopolicycomponents
-
-LOCAL_C_INCLUDES += \
- $(call include-path-for, audio-utils)
-
-LOCAL_HEADER_LIBRARIES := \
- libaudiopolicycommon \
- libaudiopolicyengine_interface_headers \
- libaudiopolicymanager_interface_headers
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_MODULE:= libaudiopolicymanager
-LOCAL_LICENSE_KINDS:= SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS:= notice
-LOCAL_NOTICE_FILE:= $(LOCAL_PATH)/../../../NOTICE
-
-include $(BUILD_SHARED_LIBRARY)
-
-endif #ifneq ($(USE_CUSTOM_AUDIO_POLICY), 1)
diff --git a/services/audiopolicy/manager/AudioPolicyFactory.cpp b/services/audiopolicy/manager/AudioPolicyFactory.cpp
deleted file mode 100644
index 476a1ec..0000000
--- a/services/audiopolicy/manager/AudioPolicyFactory.cpp
+++ /dev/null
@@ -1,38 +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.
- */
-
-#include <AudioPolicyManager.h>
-
-namespace android {
-
-extern "C" AudioPolicyInterface* createAudioPolicyManager(
- AudioPolicyClientInterface *clientInterface)
-{
- AudioPolicyManager *apm = new AudioPolicyManager(clientInterface);
- status_t status = apm->initialize();
- if (status != NO_ERROR) {
- delete apm;
- apm = nullptr;
- }
- return apm;
-}
-
-extern "C" void destroyAudioPolicyManager(AudioPolicyInterface *interface)
-{
- delete interface;
-}
-
-} // namespace android
diff --git a/services/audiopolicy/managerdefault/Android.bp b/services/audiopolicy/managerdefault/Android.bp
index 5572beb..b111db4 100644
--- a/services/audiopolicy/managerdefault/Android.bp
+++ b/services/audiopolicy/managerdefault/Android.bp
@@ -34,6 +34,8 @@
// a dependency on it in the device makefile. There will be no build time
// conflict with libaudiopolicyenginedefault.
"libaudiopolicyenginedefault",
+ "media_permission-aidl-cpp",
+ "libaudioclient_aidl_conversion",
],
header_libs: [
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 5b53c0b..485188a 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -44,6 +44,7 @@
#include <private/android_filesystem_config.h>
#include <system/audio.h>
#include <system/audio_config.h>
+#include <system/audio_effects/effect_hapticgenerator.h>
#include <utils/Log.h>
#include "AudioPolicyManager.h"
@@ -51,6 +52,8 @@
namespace android {
+using media::permission::Identity;
+
//FIXME: workaround for truncated touch sounds
// to be removed when the problem is handled by system UI
#define TOUCH_SOUND_FIXED_DELAY_MS 100
@@ -205,6 +208,9 @@
// Reset active device codec
device->setEncodedFormat(AUDIO_FORMAT_DEFAULT);
+ // remove device from mReportedFormatsMap cache
+ mReportedFormatsMap.erase(device);
+
} break;
default:
@@ -258,10 +264,14 @@
checkCloseOutputs();
}
(void)updateCallRouting(false /*fromCache*/);
+ std::vector<audio_io_handle_t> outputsToReopen;
const DeviceVector msdOutDevices = getMsdAudioOutDevices();
+ const DeviceVector activeMediaDevices =
+ mEngine->getActiveMediaDevices(mAvailableOutputDevices);
for (size_t i = 0; i < mOutputs.size(); i++) {
sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
- if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (desc != mPrimaryOutput)) {
+ if (desc->isActive() && ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) ||
+ (desc != mPrimaryOutput))) {
DeviceVector newDevices = getNewOutputDevices(desc, true /*fromCache*/);
// do not force device change on duplicated output because if device is 0, it will
// also force a device 0 for the two outputs it is duplicated to which may override
@@ -273,6 +283,28 @@
|| (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE));
setOutputDevices(desc, newDevices, force, 0);
}
+ if (!desc->isDuplicated() && desc->mProfile->hasDynamicAudioProfile() &&
+ !activeMediaDevices.empty() && desc->devices() != activeMediaDevices &&
+ desc->supportsDevicesForPlayback(activeMediaDevices)) {
+ // Reopen the output to query the dynamic profiles when there is not active
+ // clients or all active clients will be rerouted. Otherwise, set the flag
+ // `mPendingReopenToQueryProfiles` in the SwOutputDescriptor so that the output
+ // can be reopened to query dynamic profiles when all clients are inactive.
+ if (areAllActiveTracksRerouted(desc)) {
+ outputsToReopen.push_back(mOutputs.keyAt(i));
+ } else {
+ desc->mPendingReopenToQueryProfiles = true;
+ }
+ }
+ if (!desc->supportsDevicesForPlayback(activeMediaDevices)) {
+ // Clear the flag that previously set for re-querying profiles.
+ desc->mPendingReopenToQueryProfiles = false;
+ }
+ }
+ for (const auto& output : outputsToReopen) {
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(output);
+ closeOutput(output);
+ openOutputWithProfileAndDevice(desc->mProfile, activeMediaDevices);
}
if (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) {
@@ -330,6 +362,9 @@
mAvailableInputDevices.remove(device);
checkInputsForDevice(device, state);
+
+ // remove device from mReportedFormatsMap cache
+ mReportedFormatsMap.erase(device);
} break;
default:
@@ -1097,7 +1132,7 @@
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
- uid_t uid,
+ const Identity& identity,
const audio_config_t *config,
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
@@ -1109,6 +1144,8 @@
if (*portId != AUDIO_PORT_HANDLE_NONE) {
return INVALID_OPERATION;
}
+ const uid_t uid = VALUE_OR_RETURN_STATUS(
+ aidl2legacy_int32_t_uid_t(identity.uid));
const audio_port_handle_t requestedPortId = *selectedDeviceId;
audio_attributes_t resultAttr;
bool isRequestedDeviceForExclusiveUse = false;
@@ -1208,7 +1245,7 @@
(config->channel_mask == desc->getChannelMask()) &&
(session == desc->mDirectClientSession)) {
desc->mDirectOpenCount++;
- ALOGI("%s reusing direct output %d for session %d", __func__,
+ ALOGV("%s reusing direct output %d for session %d", __func__,
mOutputs.keyAt(i), session);
*output = mOutputs.keyAt(i);
return NO_ERROR;
@@ -1328,7 +1365,8 @@
// at this stage we should ignore the DIRECT flag as no direct output could be found earlier
*flags = (audio_output_flags_t)(*flags & ~AUDIO_OUTPUT_FLAG_DIRECT);
- output = selectOutput(outputs, *flags, config->format, channelMask, config->sample_rate);
+ output = selectOutput(
+ outputs, *flags, config->format, channelMask, config->sample_rate, session);
}
ALOGW_IF((output == 0), "getOutputForDevices() could not find output for stream %d, "
"sampling rate %d, format %#x, channels %#x, flags %#x",
@@ -1566,14 +1604,26 @@
}
audio_io_handle_t AudioPolicyManager::selectOutput(const SortedVector<audio_io_handle_t>& outputs,
- audio_output_flags_t flags,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- uint32_t samplingRate)
+ audio_output_flags_t flags,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ uint32_t samplingRate,
+ audio_session_t sessionId)
{
LOG_ALWAYS_FATAL_IF(!(format == AUDIO_FORMAT_INVALID || audio_is_linear_pcm(format)),
"%s called with format %#x", __func__, format);
+ // Return the output that haptic-generating attached to when 1) session id is specified,
+ // 2) haptic-generating effect exists for given session id and 3) the output that
+ // haptic-generating effect attached to is in given outputs.
+ if (sessionId != AUDIO_SESSION_NONE) {
+ audio_io_handle_t hapticGeneratingOutput = mEffects.getIoForSession(
+ sessionId, FX_IID_HAPTICGENERATOR);
+ if (outputs.indexOf(hapticGeneratingOutput) >= 0) {
+ return hapticGeneratingOutput;
+ }
+ }
+
// Flags disqualifying an output: the match must happen before calling selectOutput()
static const audio_output_flags_t kExcludedFlags = (audio_output_flags_t)
(AUDIO_OUTPUT_FLAG_HW_AV_SYNC | AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT);
@@ -2009,7 +2059,7 @@
}
}
-void AudioPolicyManager::releaseOutput(audio_port_handle_t portId)
+bool AudioPolicyManager::releaseOutput(audio_port_handle_t portId)
{
ALOGV("%s portId %d", __FUNCTION__, portId);
@@ -2022,33 +2072,49 @@
//
// Here we just log a warning, instead of a fatal error.
ALOGW("releaseOutput() no output for client %d", portId);
- return;
+ return false;
}
ALOGV("releaseOutput() %d", outputDesc->mIoHandle);
+ sp<TrackClientDescriptor> client = outputDesc->getClient(portId);
+ if (outputDesc->isClientActive(client)) {
+ ALOGW("releaseOutput() inactivates portId %d in good faith", portId);
+ stopOutput(portId);
+ }
+
if (outputDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) {
if (outputDesc->mDirectOpenCount <= 0) {
ALOGW("releaseOutput() invalid open count %d for output %d",
outputDesc->mDirectOpenCount, outputDesc->mIoHandle);
- return;
+ return false;
}
if (--outputDesc->mDirectOpenCount == 0) {
closeOutput(outputDesc->mIoHandle);
mpClientInterface->onAudioPortListUpdate();
}
}
- // stopOutput() needs to be successfully called before releaseOutput()
- // otherwise there may be inaccurate stream reference counts.
- // This is checked in outputDesc->removeClient below.
+
outputDesc->removeClient(portId);
+ if (outputDesc->mPendingReopenToQueryProfiles && outputDesc->getClientCount() == 0) {
+ // The output is pending reopened to query dynamic profiles and
+ // there is no active clients
+ closeOutput(outputDesc->mIoHandle);
+ sp<SwAudioOutputDescriptor> newOutputDesc = openOutputWithProfileAndDevice(
+ outputDesc->mProfile, mEngine->getActiveMediaDevices(mAvailableOutputDevices));
+ if (newOutputDesc == nullptr) {
+ ALOGE("%s failed to open output", __func__);
+ }
+ return true;
+ }
+ return false;
}
status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *input,
audio_unique_id_t riid,
audio_session_t session,
- uid_t uid,
+ const Identity& identity,
const audio_config_base_t *config,
audio_input_flags_t flags,
audio_port_handle_t *selectedDeviceId,
@@ -2067,6 +2133,7 @@
sp<AudioInputDescriptor> inputDesc;
sp<RecordClientDescriptor> clientDesc;
audio_port_handle_t requestedDeviceId = *selectedDeviceId;
+ uid_t uid = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_uid_t(identity.uid));
bool isSoundTrigger;
// The supplied portId must be AUDIO_PORT_HANDLE_NONE
@@ -2123,7 +2190,7 @@
*inputType = API_INPUT_LEGACY;
device = inputDesc->getDevice();
- ALOGI("%s reusing MMAP input %d for session %d", __FUNCTION__, *input, session);
+ ALOGV("%s reusing MMAP input %d for session %d", __FUNCTION__, *input, session);
goto exit;
}
@@ -2161,14 +2228,18 @@
} else {
// Prevent from storing invalid requested device id in clients
requestedDeviceId = AUDIO_PORT_HANDLE_NONE;
- device = mEngine->getInputDeviceForAttributes(attributes, &policyMix);
+ device = mEngine->getInputDeviceForAttributes(attributes, uid, &policyMix);
+ ALOGV_IF(device != nullptr, "%s found device type is 0x%X",
+ __FUNCTION__, device->type());
}
if (device == nullptr) {
ALOGW("getInputForAttr() could not find device for source %d", attributes.source);
status = BAD_VALUE;
goto error;
}
- if (policyMix) {
+ if (device->type() == AUDIO_DEVICE_IN_ECHO_REFERENCE) {
+ *inputType = API_INPUT_MIX_CAPTURE;
+ } else if (policyMix) {
ALOG_ASSERT(policyMix->mMixType == MIX_TYPE_RECORDERS, "Invalid Mix Type");
// there is an external policy, but this input is attached to a mix of recorders,
// meaning it receives audio injected into the framework, so the recorder doesn't
@@ -2277,6 +2348,21 @@
return input;
}
+ // Reuse an already opened input if a client with the same session ID already exists
+ // on that input
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ sp <AudioInputDescriptor> desc = mInputs.valueAt(i);
+ if (desc->mProfile != profile) {
+ continue;
+ }
+ RecordClientVector clients = desc->clientsList();
+ for (const auto &client : clients) {
+ if (session == client->session()) {
+ return desc->mIoHandle;
+ }
+ }
+ }
+
if (!profile->canOpenNewIo()) {
for (size_t i = 0; i < mInputs.size(); ) {
sp<AudioInputDescriptor> desc = mInputs.valueAt(i);
@@ -2545,7 +2631,7 @@
bool close = false;
for (const auto& client : input->clientsList()) {
sp<DeviceDescriptor> device =
- mEngine->getInputDeviceForAttributes(client->attributes());
+ mEngine->getInputDeviceForAttributes(client->attributes(), client->uid());
if (!input->supportedDevices().contains(device)) {
close = true;
break;
@@ -2873,7 +2959,7 @@
status_t AudioPolicyManager::registerEffect(const effect_descriptor_t *desc,
audio_io_handle_t io,
- uint32_t strategy,
+ product_strategy_t strategy,
int session,
int id)
{
@@ -3286,6 +3372,11 @@
void AudioPolicyManager::updateInputRouting() {
for (const auto& activeDesc : mInputs.getActiveInputs()) {
+ // Skip for hotword recording as the input device switch
+ // is handled within sound trigger HAL
+ if (activeDesc->isSoundTrigger() && activeDesc->source() == AUDIO_SOURCE_HOTWORD) {
+ continue;
+ }
auto newDevice = getNewInputDevice(activeDesc);
// Force new input selection if the new device can not be reached via current input
if (activeDesc->mProfile->getSupportedDevices().contains(newDevice)) {
@@ -3299,7 +3390,7 @@
status_t AudioPolicyManager::removeDevicesRoleForStrategy(product_strategy_t strategy,
device_role_t role)
{
- ALOGI("%s() strategy=%d role=%d", __func__, strategy, role);
+ ALOGV("%s() strategy=%d role=%d", __func__, strategy, role);
status_t status = mEngine->removeDevicesRoleForStrategy(strategy, role);
if (status != NO_ERROR) {
@@ -3401,7 +3492,7 @@
status_t AudioPolicyManager::setUserIdDeviceAffinities(int userId,
const AudioDeviceTypeAddrVector& devices) {
- ALOGI("%s() userId=%d num devices %zu", __func__, userId, devices.size());
+ ALOGV("%s() userId=%d num devices %zu", __func__, userId, devices.size());
if (!areAllDevicesSupported(devices, audio_is_output_device, __func__)) {
return BAD_VALUE;
}
@@ -3420,7 +3511,7 @@
}
status_t AudioPolicyManager::removeUserIdDeviceAffinities(int userId) {
- ALOGI("%s() userId=%d", __FUNCTION__, userId);
+ ALOGV("%s() userId=%d", __FUNCTION__, userId);
status_t status = mPolicyMixes.removeUserIdDeviceAffinities(userId);
if (status != NO_ERROR) {
ALOGE("%s() Could not remove all device affinities fo userId = %d",
@@ -3499,38 +3590,38 @@
// This function checks for the parameters which can be offloaded.
// This can be enhanced depending on the capability of the DSP and policy
// of the system.
-bool AudioPolicyManager::isOffloadSupported(const audio_offload_info_t& offloadInfo)
+audio_offload_mode_t AudioPolicyManager::getOffloadSupport(const audio_offload_info_t& offloadInfo)
{
- ALOGV("isOffloadSupported: SR=%u, CM=0x%x, Format=0x%x, StreamType=%d,"
+ ALOGV("%s: SR=%u, CM=0x%x, Format=0x%x, StreamType=%d,"
" BitRate=%u, duration=%" PRId64 " us, has_video=%d",
- offloadInfo.sample_rate, offloadInfo.channel_mask,
+ __func__, offloadInfo.sample_rate, offloadInfo.channel_mask,
offloadInfo.format,
offloadInfo.stream_type, offloadInfo.bit_rate, offloadInfo.duration_us,
offloadInfo.has_video);
if (mMasterMono) {
- return false; // no offloading if mono is set.
+ return AUDIO_OFFLOAD_NOT_SUPPORTED; // no offloading if mono is set.
}
// Check if offload has been disabled
if (property_get_bool("audio.offload.disable", false /* default_value */)) {
- ALOGV("offload disabled by audio.offload.disable");
- return false;
+ ALOGV("%s: offload disabled by audio.offload.disable", __func__);
+ return AUDIO_OFFLOAD_NOT_SUPPORTED;
}
// Check if stream type is music, then only allow offload as of now.
if (offloadInfo.stream_type != AUDIO_STREAM_MUSIC)
{
- ALOGV("isOffloadSupported: stream_type != MUSIC, returning false");
- return false;
+ ALOGV("%s: stream_type != MUSIC, returning false", __func__);
+ return AUDIO_OFFLOAD_NOT_SUPPORTED;
}
//TODO: enable audio offloading with video when ready
const bool allowOffloadWithVideo =
property_get_bool("audio.offload.video", false /* default_value */);
if (offloadInfo.has_video && !allowOffloadWithVideo) {
- ALOGV("isOffloadSupported: has_video == true, returning false");
- return false;
+ ALOGV("%s: has_video == true, returning false", __func__);
+ return AUDIO_OFFLOAD_NOT_SUPPORTED;
}
//If duration is less than minimum value defined in property, return false
@@ -3538,13 +3629,14 @@
"audio.offload.min.duration.secs", -1 /* default_value */);
if (min_duration_secs >= 0) {
if (offloadInfo.duration_us < min_duration_secs * 1000000LL) {
- ALOGV("Offload denied by duration < audio.offload.min.duration.secs(=%d)",
- min_duration_secs);
- return false;
+ ALOGV("%s: Offload denied by duration < audio.offload.min.duration.secs(=%d)",
+ __func__, min_duration_secs);
+ return AUDIO_OFFLOAD_NOT_SUPPORTED;
}
} else if (offloadInfo.duration_us < OFFLOAD_DEFAULT_MIN_DURATION_SECS * 1000000) {
- ALOGV("Offload denied by duration < default min(=%u)", OFFLOAD_DEFAULT_MIN_DURATION_SECS);
- return false;
+ ALOGV("%s: Offload denied by duration < default min(=%u)",
+ __func__, OFFLOAD_DEFAULT_MIN_DURATION_SECS);
+ return AUDIO_OFFLOAD_NOT_SUPPORTED;
}
// Do not allow offloading if one non offloadable effect is enabled. This prevents from
@@ -3554,7 +3646,7 @@
// This may prevent offloading in rare situations where effects are left active by apps
// in the background.
if (mEffects.isNonOffloadableEffectEnabled()) {
- return false;
+ return AUDIO_OFFLOAD_NOT_SUPPORTED;
}
// See if there is a profile to support this.
@@ -3565,8 +3657,16 @@
offloadInfo.channel_mask,
AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD,
true /* directOnly */);
- ALOGV("isOffloadSupported() profile %sfound", profile != 0 ? "" : "NOT ");
- return (profile != 0);
+ ALOGV("%s: profile %sfound%s", __func__, profile != nullptr ? "" : "NOT ",
+ (profile != nullptr && (profile->getFlags() & AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD) != 0)
+ ? ", supports gapless" : "");
+ if (profile == nullptr) {
+ return AUDIO_OFFLOAD_NOT_SUPPORTED;
+ }
+ if ((profile->getFlags() & AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD) != 0) {
+ return AUDIO_OFFLOAD_GAPLESS_SUPPORTED;
+ }
+ return AUDIO_OFFLOAD_SUPPORTED;
}
bool AudioPolicyManager::isDirectOutputSupported(const audio_config_base_t& config,
@@ -3590,15 +3690,15 @@
status_t AudioPolicyManager::listAudioPorts(audio_port_role_t role,
audio_port_type_t type,
unsigned int *num_ports,
- struct audio_port *ports,
+ struct audio_port_v7 *ports,
unsigned int *generation)
{
- if (num_ports == NULL || (*num_ports != 0 && ports == NULL) ||
- generation == NULL) {
+ if (num_ports == nullptr || (*num_ports != 0 && ports == nullptr) ||
+ generation == nullptr) {
return BAD_VALUE;
}
ALOGV("listAudioPorts() role %d type %d num_ports %d ports %p", role, type, *num_ports, ports);
- if (ports == NULL) {
+ if (ports == nullptr) {
*num_ports = 0;
}
@@ -3656,7 +3756,7 @@
return NO_ERROR;
}
-status_t AudioPolicyManager::getAudioPort(struct audio_port *port)
+status_t AudioPolicyManager::getAudioPort(struct audio_port_v7 *port)
{
if (port == nullptr || port->id == AUDIO_PORT_HANDLE_NONE) {
return BAD_VALUE;
@@ -4446,54 +4546,28 @@
status_t AudioPolicyManager::getSurroundFormats(unsigned int *numSurroundFormats,
audio_format_t *surroundFormats,
- bool *surroundFormatsEnabled,
- bool reported)
+ bool *surroundFormatsEnabled)
{
- if (numSurroundFormats == NULL || (*numSurroundFormats != 0 &&
- (surroundFormats == NULL || surroundFormatsEnabled == NULL))) {
+ if (numSurroundFormats == nullptr || (*numSurroundFormats != 0 &&
+ (surroundFormats == nullptr || surroundFormatsEnabled == nullptr))) {
return BAD_VALUE;
}
- ALOGV("%s() numSurroundFormats %d surroundFormats %p surroundFormatsEnabled %p reported %d",
- __func__, *numSurroundFormats, surroundFormats, surroundFormatsEnabled, reported);
+ ALOGV("%s() numSurroundFormats %d surroundFormats %p surroundFormatsEnabled %p",
+ __func__, *numSurroundFormats, surroundFormats, surroundFormatsEnabled);
size_t formatsWritten = 0;
size_t formatsMax = *numSurroundFormats;
- std::unordered_set<audio_format_t> formats; // Uses primary surround formats only
- if (reported) {
- // Return formats from all device profiles that have already been resolved by
- // checkOutputsForDevice().
- for (size_t i = 0; i < mAvailableOutputDevices.size(); i++) {
- sp<DeviceDescriptor> device = mAvailableOutputDevices[i];
- FormatVector supportedFormats =
- device->getAudioPort()->getAudioProfiles().getSupportedFormats();
- for (size_t j = 0; j < supportedFormats.size(); j++) {
- if (mConfig.getSurroundFormats().count(supportedFormats[j]) != 0) {
- formats.insert(supportedFormats[j]);
- } else {
- for (const auto& pair : mConfig.getSurroundFormats()) {
- if (pair.second.count(supportedFormats[j]) != 0) {
- formats.insert(pair.first);
- break;
- }
- }
- }
- }
- }
- } else {
- for (const auto& pair : mConfig.getSurroundFormats()) {
- formats.insert(pair.first);
- }
- }
- *numSurroundFormats = formats.size();
+
+ *numSurroundFormats = mConfig.getSurroundFormats().size();
audio_policy_forced_cfg_t forceUse = mEngine->getForceUse(
AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND);
- for (const auto& format: formats) {
+ for (const auto& format: mConfig.getSurroundFormats()) {
if (formatsWritten < formatsMax) {
- surroundFormats[formatsWritten] = format;
+ surroundFormats[formatsWritten] = format.first;
bool formatEnabled = true;
switch (forceUse) {
case AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL:
- formatEnabled = mManualSurroundFormats.count(format) != 0;
+ formatEnabled = mManualSurroundFormats.count(format.first) != 0;
break;
case AUDIO_POLICY_FORCE_ENCODED_SURROUND_NEVER:
formatEnabled = false;
@@ -4507,6 +4581,60 @@
return NO_ERROR;
}
+status_t AudioPolicyManager::getReportedSurroundFormats(unsigned int *numSurroundFormats,
+ audio_format_t *surroundFormats) {
+ if (numSurroundFormats == nullptr || (*numSurroundFormats != 0 && surroundFormats == nullptr)) {
+ return BAD_VALUE;
+ }
+ ALOGV("%s() numSurroundFormats %d surroundFormats %p",
+ __func__, *numSurroundFormats, surroundFormats);
+
+ size_t formatsWritten = 0;
+ size_t formatsMax = *numSurroundFormats;
+ std::unordered_set<audio_format_t> formats; // Uses primary surround formats only
+
+ // Return formats from all device profiles that have already been resolved by
+ // checkOutputsForDevice().
+ for (size_t i = 0; i < mAvailableOutputDevices.size(); i++) {
+ sp<DeviceDescriptor> device = mAvailableOutputDevices[i];
+ audio_devices_t deviceType = device->type();
+ // Enabling/disabling formats are applied to only HDMI devices. So, this function
+ // returns formats reported by HDMI devices.
+ if (deviceType != AUDIO_DEVICE_OUT_HDMI) {
+ continue;
+ }
+ // Formats reported by sink devices
+ std::unordered_set<audio_format_t> formatset;
+ if (auto it = mReportedFormatsMap.find(device); it != mReportedFormatsMap.end()) {
+ formatset.insert(it->second.begin(), it->second.end());
+ }
+
+ // Formats hard-coded in the in policy configuration file (if any).
+ FormatVector encodedFormats = device->encodedFormats();
+ formatset.insert(encodedFormats.begin(), encodedFormats.end());
+ // Filter the formats which are supported by the vendor hardware.
+ for (auto it = formatset.begin(); it != formatset.end(); ++it) {
+ if (mConfig.getSurroundFormats().count(*it) != 0) {
+ formats.insert(*it);
+ } else {
+ for (const auto& pair : mConfig.getSurroundFormats()) {
+ if (pair.second.count(*it) != 0) {
+ formats.insert(pair.first);
+ break;
+ }
+ }
+ }
+ }
+ }
+ *numSurroundFormats = formats.size();
+ for (const auto& format: formats) {
+ if (formatsWritten < formatsMax) {
+ surroundFormats[formatsWritten++] = format;
+ }
+ }
+ return NO_ERROR;
+}
+
status_t AudioPolicyManager::setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled)
{
ALOGV("%s() format 0x%X enabled %d", __func__, audioFormat, enabled);
@@ -4637,7 +4765,11 @@
if (status == NO_ERROR) {
swOutput->stop();
}
- releaseOutput(sourceDesc->portId());
+ if (releaseOutput(sourceDesc->portId())) {
+ // The output descriptor is reopened to query dynamic profiles. In that case, there is
+ // no need to release audio patch here but just return NO_ERROR.
+ return NO_ERROR;
+ }
} else {
sp<HwAudioOutputDescriptor> hwOutputDesc = sourceDesc->hwOutput().promote();
if (hwOutputDesc != 0) {
@@ -4739,6 +4871,9 @@
return status;
}
+ mCommunnicationStrategy = mEngine->getProductStrategyForAttributes(
+ mEngine->getAttributesForStreamType(AUDIO_STREAM_VOICE_CALL));
+
// after parsing the config, mOutputDevicesAll and mInputDevicesAll contain all known devices;
// open all output streams needed to access attached devices
onNewAudioModulesAvailableInt(nullptr /*newDevices*/);
@@ -4765,9 +4900,6 @@
// Silence ALOGV statements
property_set("log.tag." LOG_TAG, "D");
- mCommunnicationStrategy = mEngine->getProductStrategyForAttributes(
- mEngine->getAttributesForStreamType(AUDIO_STREAM_VOICE_CALL));
-
updateDevicesAndOutputs();
return status;
}
@@ -4905,7 +5037,8 @@
const DeviceVector &supportedDevices = inProfile->getSupportedDevices();
DeviceVector availProfileDevices = supportedDevices.filter(mInputDevicesAll);
if (availProfileDevices.isEmpty()) {
- ALOGE("%s: Input device list is empty!", __FUNCTION__);
+ ALOGV("%s: Input device list is empty! for profile %s",
+ __func__, inProfile->getTagName().c_str());
continue;
}
sp<AudioInputDescriptor> inputDesc =
@@ -4979,7 +5112,15 @@
}
if (state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) {
- // first list already open outputs that can be routed to this device
+ // first call getAudioPort to get the supported attributes from the HAL
+ struct audio_port_v7 port = {};
+ device->toAudioPort(&port);
+ status_t status = mpClientInterface->getAudioPort(&port);
+ if (status == NO_ERROR) {
+ device->importAudioPort(port);
+ }
+
+ // then list already open outputs that can be routed to this device
for (size_t i = 0; i < mOutputs.size(); i++) {
desc = mOutputs.valueAt(i);
if (!desc->isDuplicated() && desc->supportsDevice(device)
@@ -5039,88 +5180,8 @@
ALOGV("opening output for device %08x with params %s profile %p name %s",
deviceType, address.string(), profile.get(), profile->getName().c_str());
- desc = new SwAudioOutputDescriptor(profile, mpClientInterface);
- audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
- status_t status = desc->open(nullptr, DeviceVector(device),
- AUDIO_STREAM_DEFAULT, AUDIO_OUTPUT_FLAG_NONE, &output);
-
- if (status == NO_ERROR) {
- // Here is where the out_set_parameters() for card & device gets called
- if (!address.isEmpty()) {
- char *param = audio_device_address_to_parameter(deviceType, address);
- mpClientInterface->setParameters(output, String8(param));
- free(param);
- }
- updateAudioProfiles(device, output, profile->getAudioProfiles());
- if (!profile->hasValidAudioProfile()) {
- ALOGW("checkOutputsForDevice() missing param");
- desc->close();
- output = AUDIO_IO_HANDLE_NONE;
- } else if (profile->hasDynamicAudioProfile()) {
- desc->close();
- output = AUDIO_IO_HANDLE_NONE;
- audio_config_t config = AUDIO_CONFIG_INITIALIZER;
- profile->pickAudioProfile(
- config.sample_rate, config.channel_mask, config.format);
- config.offload_info.sample_rate = config.sample_rate;
- config.offload_info.channel_mask = config.channel_mask;
- config.offload_info.format = config.format;
-
- status_t status = desc->open(&config, DeviceVector(device),
- AUDIO_STREAM_DEFAULT,
- AUDIO_OUTPUT_FLAG_NONE, &output);
- if (status != NO_ERROR) {
- output = AUDIO_IO_HANDLE_NONE;
- }
- }
-
- if (output != AUDIO_IO_HANDLE_NONE) {
- addOutput(output, desc);
- if (audio_is_remote_submix_device(deviceType) && address != "0") {
- sp<AudioPolicyMix> policyMix;
- if (mPolicyMixes.getAudioPolicyMix(deviceType, address, policyMix)
- == NO_ERROR) {
- policyMix->setOutput(desc);
- desc->mPolicyMix = policyMix;
- } else {
- ALOGW("checkOutputsForDevice() cannot find policy for address %s",
- address.string());
- }
-
- } else if (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) &&
- hasPrimaryOutput()) {
- // no duplicated output for direct outputs and
- // outputs used by dynamic policy mixes
- audio_io_handle_t duplicatedOutput = AUDIO_IO_HANDLE_NONE;
-
- //TODO: configure audio effect output stage here
-
- // open a duplicating output thread for the new output and the primary output
- sp<SwAudioOutputDescriptor> dupOutputDesc =
- new SwAudioOutputDescriptor(NULL, mpClientInterface);
- status_t status = dupOutputDesc->openDuplicating(mPrimaryOutput, desc,
- &duplicatedOutput);
- if (status == NO_ERROR) {
- // add duplicated output descriptor
- addOutput(duplicatedOutput, dupOutputDesc);
- } else {
- ALOGW("checkOutputsForDevice() could not open dup output for %d and %d",
- mPrimaryOutput->mIoHandle, output);
- desc->close();
- removeOutput(output);
- nextAudioPortGeneration();
- output = AUDIO_IO_HANDLE_NONE;
- }
- }
- if (mPrimaryOutput == nullptr
- && (profile->getFlags() & AUDIO_OUTPUT_FLAG_PRIMARY) != 0) {
- ALOGV("%s(): re-assigning mPrimaryOutput", __func__);
- mPrimaryOutput = desc;
- }
- }
- } else {
- output = AUDIO_IO_HANDLE_NONE;
- }
+ desc = openOutputWithProfileAndDevice(profile, DeviceVector(device));
+ audio_io_handle_t output = desc == nullptr ? AUDIO_IO_HANDLE_NONE : desc->mIoHandle;
if (output == AUDIO_IO_HANDLE_NONE) {
ALOGW("checkOutputsForDevice() could not open output for device %x", deviceType);
profiles.removeAt(profile_index);
@@ -5129,6 +5190,8 @@
outputs.add(output);
// Load digital format info only for digital devices
if (audio_device_is_digital(deviceType)) {
+ // TODO: when getAudioPort is ready, it may not be needed to import the audio
+ // port but just pick audio profile
device->importAudioPortAndPickAudioProfile(profile);
}
@@ -5153,7 +5216,7 @@
if (!desc->isDuplicated()) {
// exact match on device
if (device_distinguishes_on_address(deviceType) && desc->supportsDevice(device)
- && desc->devicesSupportEncodedFormats({deviceType})) {
+ && desc->containsSingleDeviceSupportingEncodedFormats(device)) {
outputs.add(mOutputs.keyAt(i));
} else if (!mAvailableOutputDevices.containsAtLeastOne(desc->supportedDevices())) {
ALOGV("checkOutputsForDevice(): disconnecting adding output %d",
@@ -5166,11 +5229,30 @@
for (const auto& hwModule : mHwModules) {
for (size_t j = 0; j < hwModule->getOutputProfiles().size(); j++) {
sp<IOProfile> profile = hwModule->getOutputProfiles()[j];
- if (profile->supportsDevice(device)) {
- ALOGV("checkOutputsForDevice(): "
- "clearing direct output profile %zu on module %s",
- j, hwModule->getName());
- profile->clearAudioProfiles();
+ if (!profile->supportsDevice(device)) {
+ continue;
+ }
+ ALOGV("checkOutputsForDevice(): "
+ "clearing direct output profile %zu on module %s",
+ j, hwModule->getName());
+ profile->clearAudioProfiles();
+ if (!profile->hasDynamicAudioProfile()) {
+ continue;
+ }
+ // When a device is disconnected, if there is an IOProfile that contains dynamic
+ // profiles and supports the disconnected device, call getAudioPort to repopulate
+ // the capabilities of the devices that is supported by the IOProfile.
+ for (const auto& supportedDevice : profile->getSupportedDevices()) {
+ if (supportedDevice == device ||
+ !mAvailableOutputDevices.contains(supportedDevice)) {
+ continue;
+ }
+ struct audio_port_v7 port;
+ supportedDevice->toAudioPort(&port);
+ status_t status = mpClientInterface->getAudioPort(&port);
+ if (status == NO_ERROR) {
+ supportedDevice->importAudioPort(port);
+ }
}
}
}
@@ -5453,6 +5535,8 @@
// This may be complicated by the fact that devices may become unavailable.
setMsdOutputPatches();
}
+ // an event that changed routing likely occurred, inform upper layers
+ mpClientInterface->onRoutingUpdated();
}
bool AudioPolicyManager::followsSameRouting(const audio_attributes_t &lAttr,
@@ -5514,7 +5598,7 @@
if (status != OK) {
continue;
}
- if (client->getPrimaryMix() != primaryMix) {
+ if (client->getPrimaryMix() != primaryMix || client->hasLostPrimaryMix()) {
invalidate = true;
if (desc->isStrategyActive(psId)) {
maxLatency = desc->latency();
@@ -5545,7 +5629,8 @@
client->flags(),
client->config().format,
client->config().channel_mask,
- client->config().sample_rate);
+ client->config().sample_rate,
+ client->session());
if (newOutput != srcOut) {
invalidate = true;
break;
@@ -5734,13 +5819,27 @@
for (const auto &productStrategy : mEngine->getOrderedProductStrategies()) {
StreamTypeVector streams = mEngine->getStreamTypesForProductStrategy(productStrategy);
auto attr = mEngine->getAllAttributesForProductStrategy(productStrategy).front();
+ auto hasStreamActive = [&](auto stream) {
+ return hasStream(streams, stream) && isStreamActive(stream, 0);
+ };
- if ((hasVoiceStream(streams) &&
- (isInCall() || mOutputs.isStrategyActiveOnSameModule(productStrategy, outputDesc)) &&
- !isStreamActive(AUDIO_STREAM_ENFORCED_AUDIBLE, 0)) ||
- ((hasStream(streams, AUDIO_STREAM_ALARM) || hasStream(streams, AUDIO_STREAM_ENFORCED_AUDIBLE)) &&
- mOutputs.isStrategyActiveOnSameModule(productStrategy, outputDesc)) ||
- outputDesc->isStrategyActive(productStrategy)) {
+ auto doGetOutputDevicesForVoice = [&]() {
+ return hasVoiceStream(streams) && (outputDesc == mPrimaryOutput ||
+ outputDesc->isActive(toVolumeSource(AUDIO_STREAM_VOICE_CALL))) &&
+ (isInCall() ||
+ mOutputs.isStrategyActiveOnSameModule(productStrategy, outputDesc));
+ };
+
+ // With low-latency playing on speaker, music on WFD, when the first low-latency
+ // output is stopped, getNewOutputDevices checks for a product strategy
+ // from the list, as STRATEGY_SONIFICATION comes prior to STRATEGY_MEDIA.
+ // If an ALARM or ENFORCED_AUDIBLE stream is supported by the product strategy,
+ // devices are returned for STRATEGY_SONIFICATION without checking whether the
+ // stream is associated to the output descriptor.
+ if (doGetOutputDevicesForVoice() || outputDesc->isStrategyActive(productStrategy) ||
+ ((hasStreamActive(AUDIO_STREAM_ALARM) ||
+ hasStreamActive(AUDIO_STREAM_ENFORCED_AUDIBLE)) &&
+ mOutputs.isStrategyActiveOnSameModule(productStrategy, outputDesc))) {
// Retrieval of devices for voice DL is done on primary output profile, cannot
// check the route (would force modifying configuration file for this profile)
devices = mEngine->getOutputDevicesForAttributes(attr, nullptr, fromCache);
@@ -5776,12 +5875,22 @@
// If we are not in call and no client is active on this input, this methods returns
// a null sp<>, causing the patch on the input stream to be released.
- audio_attributes_t attributes = inputDesc->getHighestPriorityAttributes();
+ audio_attributes_t attributes;
+ uid_t uid;
+ sp<RecordClientDescriptor> topClient = inputDesc->getHighestPriorityClient();
+ if (topClient != nullptr) {
+ attributes = topClient->attributes();
+ uid = topClient->uid();
+ } else {
+ attributes = { .source = AUDIO_SOURCE_DEFAULT };
+ uid = 0;
+ }
+
if (attributes.source == AUDIO_SOURCE_DEFAULT && isInCall()) {
attributes.source = AUDIO_SOURCE_VOICE_COMMUNICATION;
}
if (attributes.source != AUDIO_SOURCE_DEFAULT) {
- device = mEngine->getInputDeviceForAttributes(attributes);
+ device = mEngine->getInputDeviceForAttributes(attributes, uid);
}
return device;
@@ -6678,7 +6787,7 @@
for (auto it = channelMasks.begin(); it != channelMasks.end();) {
audio_channel_mask_t channelMask = *it;
if (channelMask & ~AUDIO_CHANNEL_OUT_STEREO) {
- ALOGI("%s: force NEVER, so remove channelMask 0x%08x", __FUNCTION__, channelMask);
+ ALOGV("%s: force NEVER, so remove channelMask 0x%08x", __FUNCTION__, channelMask);
it = channelMasks.erase(it);
} else {
++it;
@@ -6698,7 +6807,7 @@
// If not then add 5.1 support.
if (!supports5dot1) {
channelMasks.insert(AUDIO_CHANNEL_OUT_5POINT1);
- ALOGI("%s: force MANUAL or ALWAYS, so adding channelMask for 5.1 surround", __func__);
+ ALOGV("%s: force MANUAL or ALWAYS, so adding channelMask for 5.1 surround", __func__);
}
}
}
@@ -6722,6 +6831,7 @@
return;
}
FormatVector formats = formatsFromString(reply.string());
+ mReportedFormatsMap[devDesc] = formats;
if (device == AUDIO_DEVICE_OUT_HDMI
|| isDeviceOfModule(devDesc, AUDIO_HARDWARE_MODULE_ID_MSD)) {
modifySurroundFormats(devDesc, &formats);
@@ -6821,4 +6931,129 @@
return status;
}
+bool AudioPolicyManager::areAllActiveTracksRerouted(const sp<SwAudioOutputDescriptor>& output)
+{
+ const TrackClientVector activeClients = output->getActiveClients();
+ if (activeClients.empty()) {
+ return true;
+ }
+ ssize_t index = mAudioPatches.indexOfKey(output->getPatchHandle());
+ if (index < 0) {
+ ALOGE("%s, no audio patch found while there are active clients on output %d",
+ __func__, output->getId());
+ return false;
+ }
+ sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
+ DeviceVector routedDevices;
+ for (int i = 0; i < patchDesc->mPatch.num_sinks; ++i) {
+ sp<DeviceDescriptor> device = mAvailableOutputDevices.getDeviceFromId(
+ patchDesc->mPatch.sinks[i].id);
+ if (device == nullptr) {
+ ALOGE("%s, no audio device found with id(%d)",
+ __func__, patchDesc->mPatch.sinks[i].id);
+ return false;
+ }
+ routedDevices.add(device);
+ }
+ for (const auto& client : activeClients) {
+ // TODO: b/175343099 only travel the valid client
+ sp<DeviceDescriptor> preferredDevice =
+ mAvailableOutputDevices.getDeviceFromId(client->preferredDeviceId());
+ if (mEngine->getOutputDevicesForAttributes(
+ client->attributes(), preferredDevice, false) == routedDevices) {
+ return false;
+ }
+ }
+ return true;
+}
+
+sp<SwAudioOutputDescriptor> AudioPolicyManager::openOutputWithProfileAndDevice(
+ const sp<IOProfile>& profile, const DeviceVector& devices)
+{
+ for (const auto& device : devices) {
+ // TODO: This should be checking if the profile supports the device combo.
+ if (!profile->supportsDevice(device)) {
+ return nullptr;
+ }
+ }
+ sp<SwAudioOutputDescriptor> desc = new SwAudioOutputDescriptor(profile, mpClientInterface);
+ audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
+ status_t status = desc->open(nullptr, devices,
+ AUDIO_STREAM_DEFAULT, AUDIO_OUTPUT_FLAG_NONE, &output);
+ if (status != NO_ERROR) {
+ return nullptr;
+ }
+
+ // Here is where the out_set_parameters() for card & device gets called
+ sp<DeviceDescriptor> device = devices.getDeviceForOpening();
+ const audio_devices_t deviceType = device->type();
+ const String8 &address = String8(device->address().c_str());
+ if (!address.isEmpty()) {
+ char *param = audio_device_address_to_parameter(deviceType, address.c_str());
+ mpClientInterface->setParameters(output, String8(param));
+ free(param);
+ }
+ updateAudioProfiles(device, output, profile->getAudioProfiles());
+ if (!profile->hasValidAudioProfile()) {
+ ALOGW("%s() missing param", __func__);
+ desc->close();
+ return nullptr;
+ } else if (profile->hasDynamicAudioProfile()) {
+ desc->close();
+ output = AUDIO_IO_HANDLE_NONE;
+ audio_config_t config = AUDIO_CONFIG_INITIALIZER;
+ profile->pickAudioProfile(
+ config.sample_rate, config.channel_mask, config.format);
+ config.offload_info.sample_rate = config.sample_rate;
+ config.offload_info.channel_mask = config.channel_mask;
+ config.offload_info.format = config.format;
+
+ status = desc->open(&config, devices,
+ AUDIO_STREAM_DEFAULT, AUDIO_OUTPUT_FLAG_NONE, &output);
+ if (status != NO_ERROR) {
+ return nullptr;
+ }
+ }
+
+ addOutput(output, desc);
+ if (audio_is_remote_submix_device(deviceType) && address != "0") {
+ sp<AudioPolicyMix> policyMix;
+ if (mPolicyMixes.getAudioPolicyMix(deviceType, address, policyMix) == NO_ERROR) {
+ policyMix->setOutput(desc);
+ desc->mPolicyMix = policyMix;
+ } else {
+ ALOGW("checkOutputsForDevice() cannot find policy for address %s",
+ address.string());
+ }
+
+ } else if (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) && hasPrimaryOutput()) {
+ // no duplicated output for direct outputs and
+ // outputs used by dynamic policy mixes
+ audio_io_handle_t duplicatedOutput = AUDIO_IO_HANDLE_NONE;
+
+ //TODO: configure audio effect output stage here
+
+ // open a duplicating output thread for the new output and the primary output
+ sp<SwAudioOutputDescriptor> dupOutputDesc =
+ new SwAudioOutputDescriptor(nullptr, mpClientInterface);
+ status = dupOutputDesc->openDuplicating(mPrimaryOutput, desc, &duplicatedOutput);
+ if (status == NO_ERROR) {
+ // add duplicated output descriptor
+ addOutput(duplicatedOutput, dupOutputDesc);
+ } else {
+ ALOGW("checkOutputsForDevice() could not open dup output for %d and %d",
+ mPrimaryOutput->mIoHandle, output);
+ desc->close();
+ removeOutput(output);
+ nextAudioPortGeneration();
+ return nullptr;
+ }
+ }
+ if (mPrimaryOutput == nullptr && profile->getFlags() & AUDIO_OUTPUT_FLAG_PRIMARY) {
+ ALOGV("%s(): re-assigning mPrimaryOutput", __func__);
+ mPrimaryOutput = desc;
+ }
+ return desc;
+}
+
} // namespace android
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 5533829..f5dd20c 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -116,7 +116,7 @@
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
- uid_t uid,
+ const media::permission::Identity& identity,
const audio_config_t *config,
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
@@ -125,12 +125,12 @@
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);
+ virtual bool releaseOutput(audio_port_handle_t portId);
virtual status_t getInputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *input,
audio_unique_id_t riid,
audio_session_t session,
- uid_t uid,
+ const media::permission::Identity& identity,
const audio_config_base_t *config,
audio_input_flags_t flags,
audio_port_handle_t *selectedDeviceId,
@@ -180,7 +180,7 @@
const DeviceTypeSet& deviceTypes) const;
// return the strategy corresponding to a given stream type
- virtual uint32_t getStrategyForStream(audio_stream_type_t stream)
+ virtual product_strategy_t getStrategyForStream(audio_stream_type_t stream)
{
return streamToStrategy(stream);
}
@@ -200,7 +200,7 @@
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,
- uint32_t strategy,
+ product_strategy_t strategy,
int session,
int id);
virtual status_t unregisterEffect(int id);
@@ -225,7 +225,7 @@
status_t dump(int fd) override;
status_t setAllowedCapturePolicy(uid_t uid, audio_flags_mask_t capturePolicy) override;
- virtual bool isOffloadSupported(const audio_offload_info_t& offloadInfo);
+ virtual audio_offload_mode_t getOffloadSupport(const audio_offload_info_t& offloadInfo);
virtual bool isDirectOutputSupported(const audio_config_base_t& config,
const audio_attributes_t& attributes);
@@ -233,9 +233,9 @@
virtual status_t listAudioPorts(audio_port_role_t role,
audio_port_type_t type,
unsigned int *num_ports,
- struct audio_port *ports,
+ struct audio_port_v7 *ports,
unsigned int *generation);
- virtual status_t getAudioPort(struct audio_port *port);
+ virtual status_t getAudioPort(struct audio_port_v7 *port);
virtual status_t createAudioPatch(const struct audio_patch *patch,
audio_patch_handle_t *handle,
uid_t uid) {
@@ -313,8 +313,9 @@
virtual status_t getSurroundFormats(unsigned int *numSurroundFormats,
audio_format_t *surroundFormats,
- bool *surroundFormatsEnabled,
- bool reported);
+ bool *surroundFormatsEnabled);
+ virtual status_t getReportedSurroundFormats(unsigned int *numSurroundFormats,
+ audio_format_t *surroundFormats);
virtual status_t setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled);
virtual status_t getHwOffloadEncodingFormatsSupportedForA2DP(
@@ -329,11 +330,14 @@
return mEngine->listAudioProductStrategies(strategies);
}
- virtual status_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa,
- product_strategy_t &productStrategy)
+ virtual status_t getProductStrategyFromAudioAttributes(
+ const AudioAttributes &aa, product_strategy_t &productStrategy,
+ bool fallbackOnDefault)
{
- productStrategy = mEngine->getProductStrategyForAttributes(aa.getAttributes());
- return productStrategy != PRODUCT_STRATEGY_NONE ? NO_ERROR : BAD_VALUE;
+ productStrategy = mEngine->getProductStrategyForAttributes(
+ aa.getAttributes(), fallbackOnDefault);
+ return (fallbackOnDefault && productStrategy == PRODUCT_STRATEGY_NONE) ?
+ BAD_VALUE : NO_ERROR;
}
virtual status_t listAudioVolumeGroups(AudioVolumeGroupVector &groups)
@@ -341,11 +345,13 @@
return mEngine->listAudioVolumeGroups(groups);
}
- virtual status_t getVolumeGroupFromAudioAttributes(const AudioAttributes &aa,
- volume_group_t &volumeGroup)
+ virtual status_t getVolumeGroupFromAudioAttributes(
+ const AudioAttributes &aa, volume_group_t &volumeGroup, bool fallbackOnDefault)
{
- volumeGroup = mEngine->getVolumeGroupForAttributes(aa.getAttributes());
- return volumeGroup != VOLUME_GROUP_NONE ? NO_ERROR : BAD_VALUE;
+ volumeGroup = mEngine->getVolumeGroupForAttributes(
+ aa.getAttributes(), fallbackOnDefault);
+ return (fallbackOnDefault && volumeGroup == VOLUME_GROUP_NONE) ?
+ BAD_VALUE : NO_ERROR;
}
bool isCallScreenModeSupported() override;
@@ -657,7 +663,8 @@
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
audio_format_t format = AUDIO_FORMAT_INVALID,
audio_channel_mask_t channelMask = AUDIO_CHANNEL_NONE,
- uint32_t samplingRate = 0);
+ uint32_t samplingRate = 0,
+ audio_session_t sessionId = AUDIO_SESSION_NONE);
// samplingRate, format, channelMask are in/out and so may be modified
sp<IOProfile> getInputProfile(const sp<DeviceDescriptor> & device,
uint32_t& samplingRate,
@@ -850,6 +857,9 @@
std::unordered_map<uid_t, audio_flags_mask_t> mAllowedCapturePolicies;
+ // The map of device descriptor and formats reported by the device.
+ std::map<wp<DeviceDescriptor>, FormatVector> mReportedFormatsMap;
+
// Cached product strategy ID corresponding to legacy strategy STRATEGY_PHONE
product_strategy_t mCommunnicationStrategy;
@@ -1021,6 +1031,12 @@
const char* context);
bool isScoRequestedForComm() const;
+
+ bool areAllActiveTracksRerouted(const sp<SwAudioOutputDescriptor>& output);
+
+ sp<SwAudioOutputDescriptor> openOutputWithProfileAndDevice(const sp<IOProfile>& profile,
+ const DeviceVector& devices);
+
};
};
diff --git a/services/audiopolicy/service/Android.bp b/services/audiopolicy/service/Android.bp
new file mode 100644
index 0000000..14be671
--- /dev/null
+++ b/services/audiopolicy/service/Android.bp
@@ -0,0 +1,75 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_library_shared {
+ name: "libaudiopolicyservice",
+
+ srcs: [
+ "AudioPolicyClientImpl.cpp",
+ "AudioPolicyEffects.cpp",
+ "AudioPolicyInterfaceImpl.cpp",
+ "AudioPolicyService.cpp",
+ "CaptureStateNotifier.cpp",
+ ],
+
+ include_dirs: [
+ "frameworks/av/services/audioflinger"
+ ],
+
+ shared_libs: [
+ "libactivitymanager_aidl",
+ "libaudioclient",
+ "libaudioclient_aidl_conversion",
+ "libaudiofoundation",
+ "libaudiopolicy",
+ "libaudiopolicymanagerdefault",
+ "libaudioutils",
+ "libbinder",
+ "libcutils",
+ "libeffectsconfig",
+ "libhardware_legacy",
+ "liblog",
+ "libmedia_helper",
+ "libmediametrics",
+ "libmediautils",
+ "libsensorprivacy",
+ "libutils",
+ "audioclient-types-aidl-cpp",
+ "audioflinger-aidl-cpp",
+ "audiopolicy-aidl-cpp",
+ "audiopolicy-types-aidl-cpp",
+ "capture_state_listener-aidl-cpp",
+ "media_permission-aidl-cpp",
+ ],
+
+ static_libs: [
+ "libaudiopolicycomponents",
+ "media_permission-aidl-cpp",
+ ],
+
+ header_libs: [
+ "libaudiopolicycommon",
+ "libaudiopolicyengine_interface_headers",
+ "libaudiopolicymanager_interface_headers",
+ "libaudioutils_headers",
+ ],
+
+ cflags: [
+ "-fvisibility=hidden",
+ "-Werror",
+ "-Wall",
+ "-Wthread-safety",
+ ],
+
+ export_shared_lib_headers: [
+ "libactivitymanager_aidl",
+ "libsensorprivacy",
+ "media_permission-aidl-cpp",
+ ],
+}
diff --git a/services/audiopolicy/service/Android.mk b/services/audiopolicy/service/Android.mk
deleted file mode 100644
index 7be10c4..0000000
--- a/services/audiopolicy/service/Android.mk
+++ /dev/null
@@ -1,54 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- AudioPolicyService.cpp \
- AudioPolicyEffects.cpp \
- AudioPolicyInterfaceImpl.cpp \
- AudioPolicyClientImpl.cpp \
- CaptureStateNotifier.cpp
-
-LOCAL_C_INCLUDES := \
- frameworks/av/services/audioflinger \
- $(call include-path-for, audio-utils)
-
-LOCAL_HEADER_LIBRARIES := \
- libaudiopolicycommon \
- libaudiopolicyengine_interface_headers \
- libaudiopolicymanager_interface_headers
-
-LOCAL_SHARED_LIBRARIES := \
- libactivitymanager_aidl \
- libcutils \
- libutils \
- liblog \
- libbinder \
- libaudioclient \
- libaudioutils \
- libaudiofoundation \
- libhardware_legacy \
- libaudiopolicymanager \
- libmedia_helper \
- libmediametrics \
- libmediautils \
- libeffectsconfig \
- libsensorprivacy \
- capture_state_listener-aidl-cpp
-
-LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := \
- libactivitymanager_aidl \
- libsensorprivacy
-
-LOCAL_STATIC_LIBRARIES := \
- libaudiopolicycomponents
-
-LOCAL_MODULE:= libaudiopolicyservice
-LOCAL_LICENSE_KINDS:= SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS:= notice
-LOCAL_NOTICE_FILE:= $(LOCAL_PATH)/../../../NOTICE
-
-LOCAL_CFLAGS += -fvisibility=hidden
-LOCAL_CFLAGS += -Wall -Werror -Wthread-safety
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/services/audiopolicy/service/AudioPolicyClientImpl.cpp b/services/audiopolicy/service/AudioPolicyClientImpl.cpp
index 9fa7a53..77b5200 100644
--- a/services/audiopolicy/service/AudioPolicyClientImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyClientImpl.cpp
@@ -50,7 +50,22 @@
ALOGW("%s: could not get AudioFlinger", __func__);
return PERMISSION_DENIED;
}
- return af->openOutput(module, output, config, device, latencyMs, flags);
+
+ media::OpenOutputRequest request;
+ media::OpenOutputResponse response;
+
+ request.module = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_module_handle_t_int32_t(module));
+ request.config = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_config_t_AudioConfig(*config));
+ request.device = VALUE_OR_RETURN_STATUS(legacy2aidl_DeviceDescriptorBase(device));
+ request.flags = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_output_flags_t_int32_t_mask(flags));
+
+ status_t status = af->openOutput(request, &response);
+ if (status == OK) {
+ *output = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_io_handle_t(response.output));
+ *config = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioConfig_audio_config_t(response.config));
+ *latencyMs = VALUE_OR_RETURN_STATUS(convertIntegral<uint32_t>(response.latencyMs));
+ }
+ return status;
}
audio_io_handle_t AudioPolicyService::AudioPolicyClient::openDuplicateOutput(
@@ -111,7 +126,22 @@
return PERMISSION_DENIED;
}
- return af->openInput(module, input, config, device, address, source, flags);
+ AudioDeviceTypeAddr deviceTypeAddr(*device, address.c_str());
+
+ media::OpenInputRequest request;
+ request.module = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_module_handle_t_int32_t(module));
+ request.input = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(*input));
+ request.config = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_config_t_AudioConfig(*config));
+ request.device = VALUE_OR_RETURN_STATUS(legacy2aidl_AudioDeviceTypeAddress(deviceTypeAddr));
+ request.source = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_source_t_AudioSourceType(source));
+ request.flags = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_input_flags_t_int32_t_mask(flags));
+
+ media::OpenInputResponse response;
+ status_t status = af->openInput(request, &response);
+ if (status == OK) {
+ *input = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_module_handle_t(response.input));
+ }
+ return status;
}
status_t AudioPolicyService::AudioPolicyClient::closeInput(audio_io_handle_t input)
@@ -236,6 +266,11 @@
mAudioPolicyService->onAudioVolumeGroupChanged(group, flags);
}
+void AudioPolicyService::AudioPolicyClient::onRoutingUpdated()
+{
+ mAudioPolicyService->onRoutingUpdated();
+}
+
audio_unique_id_t AudioPolicyService::AudioPolicyClient::newAudioUniqueId(audio_unique_id_use_t use)
{
return AudioSystem::newAudioUniqueId(use);
@@ -246,4 +281,14 @@
mAudioPolicyService->mCaptureStateNotifier.setCaptureState(active);
}
+status_t AudioPolicyService::AudioPolicyClient::getAudioPort(struct audio_port_v7 *port)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == 0) {
+ ALOGW("%s: could not get AudioFlinger", __func__);
+ return PERMISSION_DENIED;
+ }
+ return af->getAudioPort(port);
+}
+
} // namespace android
diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp
index 5dac55b..8426a77 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.cpp
+++ b/services/audiopolicy/service/AudioPolicyEffects.cpp
@@ -35,6 +35,8 @@
namespace android {
+using media::permission::Identity;
+
// ----------------------------------------------------------------------------
// AudioPolicyEffects Implementation
// ----------------------------------------------------------------------------
@@ -121,7 +123,9 @@
Vector <EffectDesc *> effects = mInputSources.valueAt(index)->mEffects;
for (size_t i = 0; i < effects.size(); i++) {
EffectDesc *effect = effects[i];
- sp<AudioEffect> fx = new AudioEffect(String16("android"));
+ Identity identity;
+ identity.packageName = "android";
+ sp<AudioEffect> fx = new AudioEffect(identity);
fx->set(NULL, &effect->mUuid, -1, 0, 0, audioSession, input);
status_t status = fx->initCheck();
if (status != NO_ERROR && status != ALREADY_EXISTS) {
@@ -270,7 +274,9 @@
Vector <EffectDesc *> effects = mOutputStreams.valueAt(index)->mEffects;
for (size_t i = 0; i < effects.size(); i++) {
EffectDesc *effect = effects[i];
- sp<AudioEffect> fx = new AudioEffect(String16("android"));
+ Identity identity;
+ identity.packageName = "android";
+ sp<AudioEffect> fx = new AudioEffect(identity);
fx->set(NULL, &effect->mUuid, 0, 0, 0, audioSession, output);
status_t status = fx->initCheck();
if (status != NO_ERROR && status != ALREADY_EXISTS) {
@@ -970,7 +976,9 @@
for (const auto& deviceEffectsIter : mDeviceEffects) {
const auto& deviceEffects = deviceEffectsIter.second;
for (const auto& effectDesc : deviceEffects->mEffectDescriptors->mEffects) {
- sp<AudioEffect> fx = new AudioEffect(String16("android"));
+ Identity identity;
+ identity.packageName = "android";
+ sp<AudioEffect> fx = new AudioEffect(identity);
fx->set(EFFECT_UUID_NULL, &effectDesc->mUuid, 0, nullptr,
nullptr, AUDIO_SESSION_DEVICE, AUDIO_IO_HANDLE_NONE,
AudioDeviceTypeAddr{deviceEffects->getDeviceType(),
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index dcd30f6..551013f 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -19,11 +19,31 @@
#include "AudioPolicyService.h"
#include "TypeConverter.h"
-#include <media/MediaMetricsItem.h>
+#include <media/AidlConversion.h>
#include <media/AudioPolicy.h>
+#include <media/AudioValidator.h>
+#include <media/MediaMetricsItem.h>
+#include <media/PolicyAidlConversion.h>
#include <utils/Log.h>
+#include <android/media/permission/Identity.h>
+
+#define VALUE_OR_RETURN_BINDER_STATUS(x) \
+ ({ auto _tmp = (x); \
+ if (!_tmp.ok()) return aidl_utils::binderStatusFromStatusT(_tmp.error()); \
+ std::move(_tmp.value()); })
+
+#define RETURN_IF_BINDER_ERROR(x) \
+ { \
+ binder::Status _tmp = (x); \
+ if (!_tmp.isOk()) return _tmp; \
+ }
+
+#define MAX_ITEMS_PER_LIST 1024
namespace android {
+using binder::Status;
+using aidl_utils::binderStatusFromStatusT;
+using media::permission::Identity;
const std::vector<audio_usage_t>& SYSTEM_USAGES = {
AUDIO_USAGE_CALL_ASSISTANT,
@@ -44,15 +64,15 @@
}
status_t AudioPolicyService::validateUsage(audio_usage_t usage) {
- return validateUsage(usage, IPCThreadState::self()->getCallingPid(),
- IPCThreadState::self()->getCallingUid());
+ return validateUsage(usage, getCallingIdentity());
}
-status_t AudioPolicyService::validateUsage(audio_usage_t usage, pid_t pid, uid_t uid) {
+status_t AudioPolicyService::validateUsage(audio_usage_t usage, const Identity& identity) {
if (isSystemUsage(usage)) {
if (isSupportedSystemUsage(usage)) {
- if (!modifyAudioRoutingAllowed(pid, uid)) {
- ALOGE("permission denied: modify audio routing not allowed for uid %d", uid);
+ if (!modifyAudioRoutingAllowed(identity)) {
+ ALOGE(("permission denied: modify audio routing not allowed "
+ "for identity %s"), identity.toString().c_str());
return PERMISSION_DENIED;
}
} else {
@@ -74,71 +94,94 @@
mAudioPolicyManager->onNewAudioModulesAvailable();
}
-status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device,
- audio_policy_dev_state_t state,
- const char *device_address,
- const char *device_name,
- audio_format_t encodedFormat)
-{
+Status AudioPolicyService::setDeviceConnectionState(
+ const media::AudioDevice& deviceAidl,
+ media::AudioPolicyDeviceState stateAidl,
+ const std::string& deviceNameAidl,
+ media::audio::common::AudioFormat encodedFormatAidl) {
+ audio_devices_t device = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_devices_t(deviceAidl.type));
+ audio_policy_dev_state_t state = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioPolicyDeviceState_audio_policy_dev_state_t(stateAidl));
+ audio_format_t encodedFormat = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioFormat_audio_format_t(encodedFormatAidl));
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
if (!settingsAllowed()) {
- return PERMISSION_DENIED;
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (state != AUDIO_POLICY_DEVICE_STATE_AVAILABLE &&
state != AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) {
- return BAD_VALUE;
+ return binderStatusFromStatusT(BAD_VALUE);
}
ALOGV("setDeviceConnectionState()");
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->setDeviceConnectionState(device, state,
- device_address, device_name, encodedFormat);
+ return binderStatusFromStatusT(
+ mAudioPolicyManager->setDeviceConnectionState(device, state,
+ deviceAidl.address.c_str(),
+ deviceNameAidl.c_str(),
+ encodedFormat));
}
-audio_policy_dev_state_t AudioPolicyService::getDeviceConnectionState(
- audio_devices_t device,
- const char *device_address)
-{
+Status AudioPolicyService::getDeviceConnectionState(const media::AudioDevice& deviceAidl,
+ media::AudioPolicyDeviceState* _aidl_return) {
+ audio_devices_t device = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_devices_t(deviceAidl.type));
if (mAudioPolicyManager == NULL) {
- return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE;
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_policy_dev_state_t_AudioPolicyDeviceState(
+ AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE));
+ return Status::ok();
}
AutoCallerClear acc;
- return mAudioPolicyManager->getDeviceConnectionState(device,
- device_address);
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_policy_dev_state_t_AudioPolicyDeviceState(
+ mAudioPolicyManager->getDeviceConnectionState(device,
+ deviceAidl.address.c_str())));
+ return Status::ok();
}
-status_t AudioPolicyService::handleDeviceConfigChange(audio_devices_t device,
- const char *device_address,
- const char *device_name,
- audio_format_t encodedFormat)
-{
+Status AudioPolicyService::handleDeviceConfigChange(
+ const media::AudioDevice& deviceAidl,
+ const std::string& deviceNameAidl,
+ media::audio::common::AudioFormat encodedFormatAidl) {
+ audio_devices_t device = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_devices_t(deviceAidl.type));
+ audio_format_t encodedFormat = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioFormat_audio_format_t(encodedFormatAidl));
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
if (!settingsAllowed()) {
- return PERMISSION_DENIED;
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
ALOGV("handleDeviceConfigChange()");
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->handleDeviceConfigChange(device, device_address,
- device_name, encodedFormat);
+ return binderStatusFromStatusT(
+ mAudioPolicyManager->handleDeviceConfigChange(device, deviceAidl.address.c_str(),
+ deviceNameAidl.c_str(), encodedFormat));
}
-status_t AudioPolicyService::setPhoneState(audio_mode_t state, uid_t uid)
+Status AudioPolicyService::setPhoneState(media::AudioMode stateAidl, int32_t uidAidl)
{
+ audio_mode_t state = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioMode_audio_mode_t(stateAidl));
+ uid_t uid = VALUE_OR_RETURN_BINDER_STATUS(aidl2legacy_int32_t_uid_t(uidAidl));
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
if (!settingsAllowed()) {
- return PERMISSION_DENIED;
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (uint32_t(state) >= AUDIO_MODE_CNT) {
- return BAD_VALUE;
+ return binderStatusFromStatusT(BAD_VALUE);
}
ALOGV("setPhoneState()");
@@ -154,110 +197,150 @@
mAudioPolicyManager->setPhoneState(state);
mPhoneState = state;
mPhoneStateOwnerUid = uid;
- return NO_ERROR;
+ updateUidStates_l();
+ return Status::ok();
}
-audio_mode_t AudioPolicyService::getPhoneState()
-{
+Status AudioPolicyService::getPhoneState(media::AudioMode* _aidl_return) {
Mutex::Autolock _l(mLock);
- return mPhoneState;
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(legacy2aidl_audio_mode_t_AudioMode(mPhoneState));
+ return Status::ok();
}
-status_t AudioPolicyService::setForceUse(audio_policy_force_use_t usage,
- audio_policy_forced_cfg_t config)
+Status AudioPolicyService::setForceUse(media::AudioPolicyForceUse usageAidl,
+ media::AudioPolicyForcedConfig configAidl)
{
+ audio_policy_force_use_t usage = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioPolicyForceUse_audio_policy_force_use_t(usageAidl));
+ audio_policy_forced_cfg_t config = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioPolicyForcedConfig_audio_policy_forced_cfg_t(configAidl));
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
if (!modifyAudioRoutingAllowed()) {
- return PERMISSION_DENIED;
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) {
- return BAD_VALUE;
+ return binderStatusFromStatusT(BAD_VALUE);
}
if (config < 0 || config >= AUDIO_POLICY_FORCE_CFG_CNT) {
- return BAD_VALUE;
+ return binderStatusFromStatusT(BAD_VALUE);
}
ALOGV("setForceUse()");
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
mAudioPolicyManager->setForceUse(usage, config);
- return NO_ERROR;
+ return Status::ok();
}
-audio_policy_forced_cfg_t AudioPolicyService::getForceUse(audio_policy_force_use_t usage)
-{
+Status AudioPolicyService::getForceUse(media::AudioPolicyForceUse usageAidl,
+ media::AudioPolicyForcedConfig* _aidl_return) {
+ audio_policy_force_use_t usage = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioPolicyForceUse_audio_policy_force_use_t(usageAidl));
+
if (mAudioPolicyManager == NULL) {
- return AUDIO_POLICY_FORCE_NONE;
+ return binderStatusFromStatusT(NO_INIT);
}
if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) {
- return AUDIO_POLICY_FORCE_NONE;
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_policy_forced_cfg_t_AudioPolicyForcedConfig(AUDIO_POLICY_FORCE_NONE));
+ return Status::ok();
}
AutoCallerClear acc;
- return mAudioPolicyManager->getForceUse(usage);
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_policy_forced_cfg_t_AudioPolicyForcedConfig(
+ mAudioPolicyManager->getForceUse(usage)));
+ return Status::ok();
}
-audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream)
+Status AudioPolicyService::getOutput(media::AudioStreamType streamAidl, int32_t* _aidl_return)
{
+ audio_stream_type_t stream = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioStreamType_audio_stream_type_t(streamAidl));
+
if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
- return AUDIO_IO_HANDLE_NONE;
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_io_handle_t_int32_t(AUDIO_IO_HANDLE_NONE));
+ return Status::ok();
}
if (mAudioPolicyManager == NULL) {
- return AUDIO_IO_HANDLE_NONE;
+ return binderStatusFromStatusT(NO_INIT);
}
ALOGV("getOutput()");
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->getOutput(stream);
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_io_handle_t_int32_t(mAudioPolicyManager->getOutput(stream)));
+ return Status::ok();
}
-status_t AudioPolicyService::getOutputForAttr(audio_attributes_t *attr,
- audio_io_handle_t *output,
- audio_session_t session,
- audio_stream_type_t *stream,
- pid_t pid,
- uid_t uid,
- const audio_config_t *config,
- audio_output_flags_t flags,
- audio_port_handle_t *selectedDeviceId,
- audio_port_handle_t *portId,
- std::vector<audio_io_handle_t> *secondaryOutputs)
+Status AudioPolicyService::getOutputForAttr(const media::AudioAttributesInternal& attrAidl,
+ int32_t sessionAidl,
+ const Identity& identity,
+ const media::AudioConfig& configAidl,
+ int32_t flagsAidl,
+ int32_t selectedDeviceIdAidl,
+ media::GetOutputForAttrResponse* _aidl_return)
{
+ audio_attributes_t attr = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioAttributesInternal_audio_attributes_t(attrAidl));
+ audio_session_t session = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_session_t(sessionAidl));
+ audio_stream_type_t stream = AUDIO_STREAM_DEFAULT;
+ audio_config_t config = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioConfig_audio_config_t(configAidl));
+ audio_output_flags_t flags = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_output_flags_t_mask(flagsAidl));
+ audio_port_handle_t selectedDeviceId = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_port_handle_t(selectedDeviceIdAidl));
+
+ audio_io_handle_t output;
+ audio_port_handle_t portId;
+ std::vector<audio_io_handle_t> secondaryOutputs;
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
- status_t result = validateUsage(attr->usage, pid, uid);
- if (result != NO_ERROR) {
- return result;
- }
+ RETURN_IF_BINDER_ERROR(
+ binderStatusFromStatusT(AudioValidator::validateAudioAttributes(attr, "68953950")));
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(validateUsage(attr.usage, identity)));
ALOGV("%s()", __func__);
Mutex::Autolock _l(mLock);
+ // TODO b/182392553: refactor or remove
+ Identity adjIdentity = identity;
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", __func__, callingUid, uid);
- uid = callingUid;
+ if (!isAudioServerOrMediaServerUid(callingUid) || identity.uid == -1) {
+ int32_t callingUidAidl = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_uid_t_int32_t(callingUid));
+ ALOGW_IF(identity.uid != -1 && identity.uid != callingUidAidl,
+ "%s uid %d tried to pass itself off as %d", __func__,
+ callingUidAidl, identity.uid);
+ adjIdentity.uid = callingUidAidl;
}
- if (!mPackageManager.allowPlaybackCapture(uid)) {
- attr->flags = static_cast<audio_flags_mask_t>(attr->flags | AUDIO_FLAG_NO_MEDIA_PROJECTION);
+ if (!mPackageManager.allowPlaybackCapture(VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_uid_t(adjIdentity.uid)))) {
+ attr.flags = static_cast<audio_flags_mask_t>(attr.flags | AUDIO_FLAG_NO_MEDIA_PROJECTION);
}
- if (((attr->flags & (AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY|AUDIO_FLAG_BYPASS_MUTE)) != 0)
- && !bypassInterruptionPolicyAllowed(pid, uid)) {
- attr->flags = static_cast<audio_flags_mask_t>(
- attr->flags & ~(AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY|AUDIO_FLAG_BYPASS_MUTE));
+ if (((attr.flags & (AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY|AUDIO_FLAG_BYPASS_MUTE)) != 0)
+ && !bypassInterruptionPolicyAllowed(identity)) {
+ attr.flags = static_cast<audio_flags_mask_t>(
+ attr.flags & ~(AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY|AUDIO_FLAG_BYPASS_MUTE));
}
AutoCallerClear acc;
AudioPolicyInterface::output_type_t outputType;
- result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid,
- config,
- &flags, selectedDeviceId, portId,
- secondaryOutputs,
- &outputType);
+ status_t result = mAudioPolicyManager->getOutputForAttr(&attr, &output, session,
+ &stream,
+ adjIdentity,
+ &config,
+ &flags, &selectedDeviceId, &portId,
+ &secondaryOutputs,
+ &outputType);
// FIXME: Introduce a way to check for the the telephony device before opening the output
if (result == NO_ERROR) {
@@ -266,16 +349,16 @@
case AudioPolicyInterface::API_OUTPUT_LEGACY:
break;
case AudioPolicyInterface::API_OUTPUT_TELEPHONY_TX:
- if (!modifyPhoneStateAllowed(pid, uid)) {
+ if (!modifyPhoneStateAllowed(adjIdentity)) {
ALOGE("%s() permission denied: modify phone state not allowed for uid %d",
- __func__, uid);
+ __func__, adjIdentity.uid);
result = PERMISSION_DENIED;
}
break;
case AudioPolicyInterface::API_OUT_MIX_PLAYBACK:
- if (!modifyAudioRoutingAllowed(pid, uid)) {
+ if (!modifyAudioRoutingAllowed(adjIdentity)) {
ALOGE("%s() permission denied: modify audio routing not allowed for uid %d",
- __func__, uid);
+ __func__, adjIdentity.uid);
result = PERMISSION_DENIED;
}
break;
@@ -287,11 +370,24 @@
}
if (result == NO_ERROR) {
- sp <AudioPlaybackClient> client =
- new AudioPlaybackClient(*attr, *output, uid, pid, session, *portId, *selectedDeviceId, *stream);
- mAudioPlaybackClients.add(*portId, client);
+ sp<AudioPlaybackClient> client =
+ new AudioPlaybackClient(attr, output, adjIdentity, session,
+ portId, selectedDeviceId, stream);
+ mAudioPlaybackClients.add(portId, client);
+
+ _aidl_return->output = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_io_handle_t_int32_t(output));
+ _aidl_return->stream = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_stream_type_t_AudioStreamType(stream));
+ _aidl_return->selectedDeviceId = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_port_handle_t_int32_t(selectedDeviceId));
+ _aidl_return->portId = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_port_handle_t_int32_t(portId));
+ _aidl_return->secondaryOutputs = VALUE_OR_RETURN_BINDER_STATUS(
+ convertContainer<std::vector<int32_t>>(secondaryOutputs,
+ legacy2aidl_audio_io_handle_t_int32_t));
}
- return result;
+ return binderStatusFromStatusT(result);
}
void AudioPolicyService::getPlaybackClientAndEffects(audio_port_handle_t portId,
@@ -309,10 +405,12 @@
effects = mAudioPolicyEffects;
}
-status_t AudioPolicyService::startOutput(audio_port_handle_t portId)
+Status AudioPolicyService::startOutput(int32_t portIdAidl)
{
+ audio_port_handle_t portId = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_port_handle_t(portIdAidl));
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
ALOGV("startOutput()");
sp<AudioPlaybackClient> client;
@@ -334,17 +432,19 @@
if (status == NO_ERROR) {
client->active = true;
}
- return status;
+ return binderStatusFromStatusT(status);
}
-status_t AudioPolicyService::stopOutput(audio_port_handle_t portId)
+Status AudioPolicyService::stopOutput(int32_t portIdAidl)
{
+ audio_port_handle_t portId = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_port_handle_t(portIdAidl));
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
ALOGV("stopOutput()");
mOutputCommandThread->stopOutputCommand(portId);
- return NO_ERROR;
+ return Status::ok();
}
status_t AudioPolicyService::doStopOutput(audio_port_handle_t portId)
@@ -372,13 +472,16 @@
return status;
}
-void AudioPolicyService::releaseOutput(audio_port_handle_t portId)
+Status AudioPolicyService::releaseOutput(int32_t portIdAidl)
{
+ audio_port_handle_t portId = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_port_handle_t(portIdAidl));
if (mAudioPolicyManager == NULL) {
- return;
+ return binderStatusFromStatusT(NO_INIT);
}
ALOGV("releaseOutput()");
mOutputCommandThread->releaseOutputCommand(portId);
+ return Status::ok();
}
void AudioPolicyService::doReleaseOutput(audio_port_handle_t portId)
@@ -401,28 +504,40 @@
mAudioPolicyManager->releaseOutput(portId);
}
-status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr,
- audio_io_handle_t *input,
- audio_unique_id_t riid,
- audio_session_t session,
- pid_t pid,
- uid_t uid,
- const String16& opPackageName,
- const audio_config_base_t *config,
- audio_input_flags_t flags,
- audio_port_handle_t *selectedDeviceId,
- audio_port_handle_t *portId)
-{
+Status AudioPolicyService::getInputForAttr(const media::AudioAttributesInternal& attrAidl,
+ int32_t inputAidl,
+ int32_t riidAidl,
+ int32_t sessionAidl,
+ const Identity& identity,
+ const media::AudioConfigBase& configAidl,
+ int32_t flagsAidl,
+ int32_t selectedDeviceIdAidl,
+ media::GetInputForAttrResponse* _aidl_return) {
+ audio_attributes_t attr = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioAttributesInternal_audio_attributes_t(attrAidl));
+ audio_io_handle_t input = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_io_handle_t(inputAidl));
+ audio_unique_id_t riid = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_unique_id_t(riidAidl));
+ audio_session_t session = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_session_t(sessionAidl));
+ audio_config_base_t config = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioConfigBase_audio_config_base_t(configAidl));
+ audio_input_flags_t flags = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_input_flags_t_mask(flagsAidl));
+ audio_port_handle_t selectedDeviceId = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_port_handle_t(selectedDeviceIdAidl));
+
+ audio_port_handle_t portId;
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
- status_t result = validateUsage(attr->usage, pid, uid);
- if (result != NO_ERROR) {
- return result;
- }
+ RETURN_IF_BINDER_ERROR(
+ binderStatusFromStatusT(AudioValidator::validateAudioAttributes(attr, "68953950")));
- audio_source_t inputSource = attr->source;
+ audio_source_t inputSource = attr.source;
if (inputSource == AUDIO_SOURCE_DEFAULT) {
inputSource = AUDIO_SOURCE_MIC;
}
@@ -433,48 +548,69 @@
&& inputSource != AUDIO_SOURCE_HOTWORD
&& inputSource != AUDIO_SOURCE_FM_TUNER
&& inputSource != AUDIO_SOURCE_ECHO_REFERENCE)) {
- return BAD_VALUE;
+ return binderStatusFromStatusT(BAD_VALUE);
}
- bool updatePid = (pid == -1);
- const uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ // Make sure identity represents the current caller
+ Identity adjIdentity = identity;
+ // TODO b/182392553: refactor or remove
+ bool updatePid = (identity.pid == -1);
+ const uid_t callingUid =IPCThreadState::self()->getCallingUid();
+ const uid_t currentUid = VALUE_OR_RETURN_BINDER_STATUS(aidl2legacy_int32_t_uid_t(identity.uid));
if (!isAudioServerOrMediaServerUid(callingUid)) {
- ALOGW_IF(uid != (uid_t)-1 && uid != callingUid,
- "%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid, uid);
- uid = callingUid;
+ ALOGW_IF(currentUid != (uid_t)-1 && currentUid != callingUid,
+ "%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid,
+ currentUid);
+ adjIdentity.uid = VALUE_OR_RETURN_BINDER_STATUS(legacy2aidl_uid_t_int32_t(callingUid));
updatePid = true;
}
if (updatePid) {
- const pid_t callingPid = IPCThreadState::self()->getCallingPid();
- ALOGW_IF(pid != (pid_t)-1 && pid != callingPid,
+ const int32_t callingPid = VALUE_OR_RETURN_BINDER_STATUS(legacy2aidl_pid_t_int32_t(
+ IPCThreadState::self()->getCallingPid()));
+ ALOGW_IF(identity.pid != -1 && identity.pid != callingPid,
"%s uid %d pid %d tried to pass itself off as pid %d",
- __func__, callingUid, callingPid, pid);
- pid = callingPid;
+ __func__, adjIdentity.uid, callingPid, identity.pid);
+ adjIdentity.pid = callingPid;
}
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(validateUsage(attr.usage, adjIdentity)));
+
// 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;
+ // Capturing from FM_TUNER source is controlled by captureTunerAudioInputAllowed() and
+ // captureAudioOutputAllowed() (deprecated) as this does not affect users privacy
+ // as does capturing from an actual microphone.
+ if (!(recordingAllowed(adjIdentity) || attr.source == AUDIO_SOURCE_FM_TUNER)) {
+ ALOGE("%s permission denied: recording not allowed for %s",
+ __func__, adjIdentity.toString().c_str());
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
- bool canCaptureOutput = captureAudioOutputAllowed(pid, uid);
+ bool canCaptureOutput = captureAudioOutputAllowed(adjIdentity);
if ((inputSource == AUDIO_SOURCE_VOICE_UPLINK ||
inputSource == AUDIO_SOURCE_VOICE_DOWNLINK ||
inputSource == AUDIO_SOURCE_VOICE_CALL ||
- inputSource == AUDIO_SOURCE_ECHO_REFERENCE||
- inputSource == AUDIO_SOURCE_FM_TUNER) &&
- !canCaptureOutput) {
- return PERMISSION_DENIED;
+ inputSource == AUDIO_SOURCE_ECHO_REFERENCE)
+ && !canCaptureOutput) {
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
- bool canCaptureHotword = captureHotwordAllowed(opPackageName, pid, uid);
+ if (inputSource == AUDIO_SOURCE_FM_TUNER
+ && !captureTunerAudioInputAllowed(adjIdentity)
+ && !canCaptureOutput) {
+ return binderStatusFromStatusT(PERMISSION_DENIED);
+ }
+
+ bool canCaptureHotword = captureHotwordAllowed(adjIdentity);
if ((inputSource == AUDIO_SOURCE_HOTWORD) && !canCaptureHotword) {
- return BAD_VALUE;
+ return binderStatusFromStatusT(PERMISSION_DENIED);
+ }
+
+ if (((flags & AUDIO_INPUT_FLAG_HW_HOTWORD) != 0)
+ && !canCaptureHotword) {
+ ALOGE("%s: permission denied: hotword mode not allowed"
+ " for uid %d pid %d", __func__, adjIdentity.uid, adjIdentity.pid);
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
sp<AudioPolicyEffects>audioPolicyEffects;
@@ -486,10 +622,11 @@
{
AutoCallerClear acc;
// the audio_in_acoustics_t parameter is ignored by get_input()
- status = mAudioPolicyManager->getInputForAttr(attr, input, riid, session, uid,
- config,
- flags, selectedDeviceId,
- &inputType, portId);
+ status = mAudioPolicyManager->getInputForAttr(&attr, &input, riid, session,
+ adjIdentity, &config,
+ flags, &selectedDeviceId,
+ &inputType, &portId);
+
}
audioPolicyEffects = mAudioPolicyEffects;
@@ -510,7 +647,7 @@
}
break;
case AudioPolicyInterface::API_INPUT_MIX_EXT_POLICY_REROUTE:
- if (!modifyAudioRoutingAllowed(pid, uid)) {
+ if (!modifyAudioRoutingAllowed(adjIdentity)) {
ALOGE("getInputForAttr() permission denied: modify audio routing not allowed");
status = PERMISSION_DENIED;
}
@@ -525,29 +662,36 @@
if (status != NO_ERROR) {
if (status == PERMISSION_DENIED) {
AutoCallerClear acc;
- mAudioPolicyManager->releaseInput(*portId);
+ mAudioPolicyManager->releaseInput(portId);
}
- return status;
+ return binderStatusFromStatusT(status);
}
- sp<AudioRecordClient> client = new AudioRecordClient(*attr, *input, uid, pid, session, *portId,
- *selectedDeviceId, opPackageName,
+ sp<AudioRecordClient> client = new AudioRecordClient(attr, input, session, portId,
+ selectedDeviceId, adjIdentity,
canCaptureOutput, canCaptureHotword);
- mAudioRecordClients.add(*portId, client);
+ mAudioRecordClients.add(portId, client);
}
if (audioPolicyEffects != 0) {
// create audio pre processors according to input source
- status_t status = audioPolicyEffects->addInputEffects(*input, inputSource, session);
+ status_t status = audioPolicyEffects->addInputEffects(input, inputSource, session);
if (status != NO_ERROR && status != ALREADY_EXISTS) {
- ALOGW("Failed to add effects on input %d", *input);
+ ALOGW("Failed to add effects on input %d", input);
}
}
- return NO_ERROR;
+
+ _aidl_return->input = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_io_handle_t_int32_t(input));
+ _aidl_return->selectedDeviceId = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_port_handle_t_int32_t(selectedDeviceId));
+ _aidl_return->portId = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_port_handle_t_int32_t(portId));
+ return Status::ok();
}
std::string AudioPolicyService::getDeviceTypeStrForPortId(audio_port_handle_t portId) {
- struct audio_port port = {};
+ struct audio_port_v7 port = {};
port.id = portId;
status_t status = mAudioPolicyManager->getAudioPort(&port);
if (status == NO_ERROR && port.type == AUDIO_PORT_TYPE_DEVICE) {
@@ -556,10 +700,13 @@
return {};
}
-status_t AudioPolicyService::startInput(audio_port_handle_t portId)
+Status AudioPolicyService::startInput(int32_t portIdAidl)
{
+ audio_port_handle_t portId = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_port_handle_t(portIdAidl));
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
sp<AudioRecordClient> client;
{
@@ -567,18 +714,21 @@
ssize_t index = mAudioRecordClients.indexOfKey(portId);
if (index < 0) {
- return INVALID_OPERATION;
+ return binderStatusFromStatusT(INVALID_OPERATION);
}
client = mAudioRecordClients.valueAt(index);
}
+ std::stringstream msg;
+ msg << "Audio recording on session " << client->session;
+
// check calling permissions
- if (!(startRecording(client->opPackageName, client->pid, client->uid,
- client->attributes.source == AUDIO_SOURCE_HOTWORD)
+ if (!(startRecording(client->identity, String16(msg.str().c_str()),
+ client->attributes.source)
|| 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;
+ ALOGE("%s permission denied: recording not allowed for identity %s",
+ __func__, client->identity.toString().c_str());
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
Mutex::Autolock _l(mLock);
@@ -621,11 +771,13 @@
item->setCString(kAudioPolicyRqstSrc,
toString(client->attributes.source).c_str());
item->setInt32(kAudioPolicyRqstSession, client->session);
- if (client->opPackageName.size() != 0) {
+ if (client->identity.packageName.has_value() &&
+ client->identity.packageName.value().size() != 0) {
item->setCString(kAudioPolicyRqstPkg,
- std::string(String8(client->opPackageName).string()).c_str());
+ client->identity.packageName.value().c_str());
} else {
- item->setCString(kAudioPolicyRqstPkg, std::to_string(client->uid).c_str());
+ item->setCString(kAudioPolicyRqstPkg,
+ std::to_string(client->identity.uid).c_str());
}
item->setCString(
kAudioPolicyRqstDevice, getDeviceTypeStrForPortId(client->deviceId).c_str());
@@ -641,12 +793,13 @@
item->setCString(kAudioPolicyActiveSrc,
toString(other->attributes.source).c_str());
item->setInt32(kAudioPolicyActiveSession, other->session);
- if (other->opPackageName.size() != 0) {
+ if (other->identity.packageName.has_value() &&
+ other->identity.packageName.value().size() != 0) {
item->setCString(kAudioPolicyActivePkg,
- std::string(String8(other->opPackageName).string()).c_str());
+ other->identity.packageName.value().c_str());
} else {
- item->setCString(kAudioPolicyRqstPkg,
- std::to_string(other->uid).c_str());
+ item->setCString(kAudioPolicyRqstPkg, std::to_string(
+ other->identity.uid).c_str());
}
item->setCString(kAudioPolicyActiveDevice,
getDeviceTypeStrForPortId(other->deviceId).c_str());
@@ -662,24 +815,26 @@
client->active = false;
client->startTimeNs = 0;
updateUidStates_l();
- finishRecording(client->opPackageName, client->uid,
- client->attributes.source == AUDIO_SOURCE_HOTWORD);
+ finishRecording(client->identity, client->attributes.source);
}
- return status;
+ return binderStatusFromStatusT(status);
}
-status_t AudioPolicyService::stopInput(audio_port_handle_t portId)
+Status AudioPolicyService::stopInput(int32_t portIdAidl)
{
+ audio_port_handle_t portId = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_port_handle_t(portIdAidl));
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
ssize_t index = mAudioRecordClients.indexOfKey(portId);
if (index < 0) {
- return INVALID_OPERATION;
+ return binderStatusFromStatusT(INVALID_OPERATION);
}
sp<AudioRecordClient> client = mAudioRecordClients.valueAt(index);
@@ -689,16 +844,18 @@
updateUidStates_l();
// finish the recording app op
- finishRecording(client->opPackageName, client->uid,
- client->attributes.source == AUDIO_SOURCE_HOTWORD);
+ finishRecording(client->identity, client->attributes.source);
AutoCallerClear acc;
- return mAudioPolicyManager->stopInput(portId);
+ return binderStatusFromStatusT(mAudioPolicyManager->stopInput(portId));
}
-void AudioPolicyService::releaseInput(audio_port_handle_t portId)
+Status AudioPolicyService::releaseInput(int32_t portIdAidl)
{
+ audio_port_handle_t portId = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_port_handle_t(portIdAidl));
+
if (mAudioPolicyManager == NULL) {
- return;
+ return binderStatusFromStatusT(NO_INIT);
}
sp<AudioPolicyEffects>audioPolicyEffects;
sp<AudioRecordClient> client;
@@ -707,7 +864,7 @@
audioPolicyEffects = mAudioPolicyEffects;
ssize_t index = mAudioRecordClients.indexOfKey(portId);
if (index < 0) {
- return;
+ return Status::ok();
}
client = mAudioRecordClients.valueAt(index);
@@ -721,7 +878,7 @@
mAudioRecordClients.removeItem(portId);
}
if (client == 0) {
- return;
+ return Status::ok();
}
if (audioPolicyEffects != 0) {
// release audio processors from the input
@@ -735,239 +892,354 @@
AutoCallerClear acc;
mAudioPolicyManager->releaseInput(portId);
}
+ return Status::ok();
}
-status_t AudioPolicyService::initStreamVolume(audio_stream_type_t stream,
- int indexMin,
- int indexMax)
-{
+Status AudioPolicyService::initStreamVolume(media::AudioStreamType streamAidl,
+ int32_t indexMinAidl,
+ int32_t indexMaxAidl) {
+ audio_stream_type_t stream = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioStreamType_audio_stream_type_t(streamAidl));
+ int indexMin = VALUE_OR_RETURN_BINDER_STATUS(convertIntegral<int>(indexMinAidl));
+ int indexMax = VALUE_OR_RETURN_BINDER_STATUS(convertIntegral<int>(indexMaxAidl));
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
if (!settingsAllowed()) {
- return PERMISSION_DENIED;
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
- return BAD_VALUE;
+ return binderStatusFromStatusT(BAD_VALUE);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
mAudioPolicyManager->initStreamVolume(stream, indexMin, indexMax);
- return NO_ERROR;
+ return binderStatusFromStatusT(NO_ERROR);
}
-status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream,
- int index,
- audio_devices_t device)
-{
+Status AudioPolicyService::setStreamVolumeIndex(media::AudioStreamType streamAidl,
+ int32_t deviceAidl, int32_t indexAidl) {
+ audio_stream_type_t stream = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioStreamType_audio_stream_type_t(streamAidl));
+ int index = VALUE_OR_RETURN_BINDER_STATUS(convertIntegral<int>(indexAidl));
+ audio_devices_t device = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_devices_t(deviceAidl));
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
if (!settingsAllowed()) {
- return PERMISSION_DENIED;
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
- return BAD_VALUE;
+ return binderStatusFromStatusT(BAD_VALUE);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->setStreamVolumeIndex(stream,
- index,
- device);
+ return binderStatusFromStatusT(mAudioPolicyManager->setStreamVolumeIndex(stream,
+ index,
+ device));
}
-status_t AudioPolicyService::getStreamVolumeIndex(audio_stream_type_t stream,
- int *index,
- audio_devices_t device)
-{
+Status AudioPolicyService::getStreamVolumeIndex(media::AudioStreamType streamAidl,
+ int32_t deviceAidl, int32_t* _aidl_return) {
+ audio_stream_type_t stream = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioStreamType_audio_stream_type_t(streamAidl));
+ audio_devices_t device = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_devices_t(deviceAidl));
+ int index;
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
- return BAD_VALUE;
+ return binderStatusFromStatusT(BAD_VALUE);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->getStreamVolumeIndex(stream,
- index,
- device);
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ mAudioPolicyManager->getStreamVolumeIndex(stream, &index, device)));
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(convertIntegral<int32_t>(index));
+ return Status::ok();
}
-status_t AudioPolicyService::setVolumeIndexForAttributes(const audio_attributes_t &attributes,
- int index, audio_devices_t device)
-{
+Status AudioPolicyService::setVolumeIndexForAttributes(
+ const media::AudioAttributesInternal& attrAidl, int32_t deviceAidl, int32_t indexAidl) {
+ audio_attributes_t attributes = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioAttributesInternal_audio_attributes_t(attrAidl));
+ int index = VALUE_OR_RETURN_BINDER_STATUS(convertIntegral<int>(indexAidl));
+ audio_devices_t device = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_devices_t(deviceAidl));
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ AudioValidator::validateAudioAttributes(attributes, "169572641")));
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
if (!settingsAllowed()) {
- return PERMISSION_DENIED;
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->setVolumeIndexForAttributes(attributes, index, device);
+ return binderStatusFromStatusT(
+ mAudioPolicyManager->setVolumeIndexForAttributes(attributes, index, device));
}
-status_t AudioPolicyService::getVolumeIndexForAttributes(const audio_attributes_t &attributes,
- int &index, audio_devices_t device)
-{
+Status AudioPolicyService::getVolumeIndexForAttributes(
+ const media::AudioAttributesInternal& attrAidl, int32_t deviceAidl, int32_t* _aidl_return) {
+ audio_attributes_t attributes = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioAttributesInternal_audio_attributes_t(attrAidl));
+ audio_devices_t device = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_devices_t(deviceAidl));
+ int index;
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ AudioValidator::validateAudioAttributes(attributes, "169572641")));
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->getVolumeIndexForAttributes(attributes, index, device);
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ mAudioPolicyManager->getVolumeIndexForAttributes(attributes, index, device)));
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(convertIntegral<int32_t>(index));
+ return Status::ok();
}
-status_t AudioPolicyService::getMinVolumeIndexForAttributes(const audio_attributes_t &attributes,
- int &index)
-{
+Status AudioPolicyService::getMinVolumeIndexForAttributes(
+ const media::AudioAttributesInternal& attrAidl, int32_t* _aidl_return) {
+ audio_attributes_t attributes = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioAttributesInternal_audio_attributes_t(attrAidl));
+ int index;
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ AudioValidator::validateAudioAttributes(attributes, "169572641")));
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->getMinVolumeIndexForAttributes(attributes, index);
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ mAudioPolicyManager->getMinVolumeIndexForAttributes(attributes, index)));
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(convertIntegral<int32_t>(index));
+ return Status::ok();
}
-status_t AudioPolicyService::getMaxVolumeIndexForAttributes(const audio_attributes_t &attributes,
- int &index)
-{
+Status AudioPolicyService::getMaxVolumeIndexForAttributes(
+ const media::AudioAttributesInternal& attrAidl, int32_t* _aidl_return) {
+ audio_attributes_t attributes = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioAttributesInternal_audio_attributes_t(attrAidl));
+ int index;
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ AudioValidator::validateAudioAttributes(attributes, "169572641")));
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->getMaxVolumeIndexForAttributes(attributes, index);
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ mAudioPolicyManager->getMaxVolumeIndexForAttributes(attributes, index)));
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(convertIntegral<int32_t>(index));
+ return Status::ok();
}
-uint32_t AudioPolicyService::getStrategyForStream(audio_stream_type_t stream)
-{
+Status AudioPolicyService::getStrategyForStream(media::AudioStreamType streamAidl,
+ int32_t* _aidl_return) {
+ audio_stream_type_t stream = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioStreamType_audio_stream_type_t(streamAidl));
+
if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
- return PRODUCT_STRATEGY_NONE;
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(
+ convertReinterpret<int32_t>(PRODUCT_STRATEGY_NONE));
+ return Status::ok();
}
if (mAudioPolicyManager == NULL) {
- return PRODUCT_STRATEGY_NONE;
+ return binderStatusFromStatusT(NO_INIT);
}
+
// DO NOT LOCK, may be called from AudioFlinger with lock held, reaching deadlock
AutoCallerClear acc;
- return mAudioPolicyManager->getStrategyForStream(stream);
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_product_strategy_t_int32_t(
+ mAudioPolicyManager->getStrategyForStream(stream)));
+ return Status::ok();
}
//audio policy: use audio_device_t appropriately
-audio_devices_t AudioPolicyService::getDevicesForStream(audio_stream_type_t stream)
-{
+Status AudioPolicyService::getDevicesForStream(media::AudioStreamType streamAidl,
+ int32_t* _aidl_return) {
+ audio_stream_type_t stream = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioStreamType_audio_stream_type_t(streamAidl));
+
if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
- return AUDIO_DEVICE_NONE;
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_devices_t_int32_t(AUDIO_DEVICE_NONE));
+ return Status::ok();
}
if (mAudioPolicyManager == NULL) {
- return AUDIO_DEVICE_NONE;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->getDevicesForStream(stream);
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_devices_t_int32_t(mAudioPolicyManager->getDevicesForStream(stream)));
+ return Status::ok();
}
-status_t AudioPolicyService::getDevicesForAttributes(const AudioAttributes &aa,
- AudioDeviceTypeAddrVector *devices) const
+Status AudioPolicyService::getDevicesForAttributes(const media::AudioAttributesEx& attrAidl,
+ std::vector<media::AudioDevice>* _aidl_return)
{
+ AudioAttributes aa = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioAttributesEx_AudioAttributes(attrAidl));
+ AudioDeviceTypeAddrVector devices;
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->getDevicesForAttributes(aa.getAttributes(), devices);
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ mAudioPolicyManager->getDevicesForAttributes(aa.getAttributes(), &devices)));
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(
+ convertContainer<std::vector<media::AudioDevice>>(devices,
+ legacy2aidl_AudioDeviceTypeAddress));
+ return Status::ok();
}
-audio_io_handle_t AudioPolicyService::getOutputForEffect(const effect_descriptor_t *desc)
-{
- // FIXME change return type to status_t, and return NO_INIT here
+Status AudioPolicyService::getOutputForEffect(const media::EffectDescriptor& descAidl,
+ int32_t* _aidl_return) {
+ effect_descriptor_t desc = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_EffectDescriptor_effect_descriptor_t(descAidl));
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ AudioValidator::validateEffectDescriptor(desc, "73126106")));
+
if (mAudioPolicyManager == NULL) {
- return 0;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->getOutputForEffect(desc);
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_io_handle_t_int32_t(mAudioPolicyManager->getOutputForEffect(&desc)));
+ return Status::ok();
}
-status_t AudioPolicyService::registerEffect(const effect_descriptor_t *desc,
- audio_io_handle_t io,
- uint32_t strategy,
- audio_session_t session,
- int id)
-{
+Status AudioPolicyService::registerEffect(const media::EffectDescriptor& descAidl, int32_t ioAidl,
+ int32_t strategyAidl, int32_t sessionAidl,
+ int32_t idAidl) {
+ effect_descriptor_t desc = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_EffectDescriptor_effect_descriptor_t(descAidl));
+ audio_io_handle_t io = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_io_handle_t(ioAidl));
+ product_strategy_t strategy = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_product_strategy_t(strategyAidl));
+ audio_session_t session = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_session_t(sessionAidl));
+ int id = VALUE_OR_RETURN_BINDER_STATUS(convertReinterpret<int>(idAidl));
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ AudioValidator::validateEffectDescriptor(desc, "73126106")));
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->registerEffect(desc, io, strategy, session, id);
+ return binderStatusFromStatusT(
+ mAudioPolicyManager->registerEffect(&desc, io, strategy, session, id));
}
-status_t AudioPolicyService::unregisterEffect(int id)
+Status AudioPolicyService::unregisterEffect(int32_t idAidl)
{
+ int id = VALUE_OR_RETURN_BINDER_STATUS(convertReinterpret<int>(idAidl));
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->unregisterEffect(id);
+ return binderStatusFromStatusT(mAudioPolicyManager->unregisterEffect(id));
}
-status_t AudioPolicyService::setEffectEnabled(int id, bool enabled)
+Status AudioPolicyService::setEffectEnabled(int32_t idAidl, bool enabled)
{
+ int id = VALUE_OR_RETURN_BINDER_STATUS(convertReinterpret<int>(idAidl));
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->setEffectEnabled(id, enabled);
+ return binderStatusFromStatusT(mAudioPolicyManager->setEffectEnabled(id, enabled));
}
-status_t AudioPolicyService::moveEffectsToIo(const std::vector<int>& ids, audio_io_handle_t io)
+Status AudioPolicyService::moveEffectsToIo(const std::vector<int32_t>& idsAidl, int32_t ioAidl)
+
{
+ const std::vector<int>& ids = VALUE_OR_RETURN_BINDER_STATUS(
+ convertContainer<std::vector<int>>(idsAidl, convertReinterpret<int, int32_t>));
+ audio_io_handle_t io = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_io_handle_t(ioAidl));
+ if (ids.size() > MAX_ITEMS_PER_LIST) {
+ return binderStatusFromStatusT(BAD_VALUE);
+ }
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->moveEffectsToIo(ids, io);
+ return binderStatusFromStatusT(mAudioPolicyManager->moveEffectsToIo(ids, io));
}
-bool AudioPolicyService::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const
-{
+Status AudioPolicyService::isStreamActive(media::AudioStreamType streamAidl, int32_t inPastMsAidl,
+ bool* _aidl_return) {
+ audio_stream_type_t stream = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioStreamType_audio_stream_type_t(streamAidl));
+ uint32_t inPastMs = VALUE_OR_RETURN_BINDER_STATUS(convertIntegral<uint32_t>(inPastMsAidl));
+
if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
- return false;
+ *_aidl_return = false;
+ return Status::ok();
}
if (mAudioPolicyManager == NULL) {
- return false;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->isStreamActive(stream, inPastMs);
+ *_aidl_return = mAudioPolicyManager->isStreamActive(stream, inPastMs);
+ return Status::ok();
}
-bool AudioPolicyService::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const
-{
+Status AudioPolicyService::isStreamActiveRemotely(media::AudioStreamType streamAidl,
+ int32_t inPastMsAidl,
+ bool* _aidl_return) {
+ audio_stream_type_t stream = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioStreamType_audio_stream_type_t(streamAidl));
+ uint32_t inPastMs = VALUE_OR_RETURN_BINDER_STATUS(convertIntegral<uint32_t>(inPastMsAidl));
+
if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
- return false;
+ *_aidl_return = false;
+ return Status::ok();
}
if (mAudioPolicyManager == NULL) {
- return false;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->isStreamActiveRemotely(stream, inPastMs);
+ *_aidl_return = mAudioPolicyManager->isStreamActiveRemotely(stream, inPastMs);
+ return Status::ok();
}
-bool AudioPolicyService::isSourceActive(audio_source_t source) const
-{
+Status AudioPolicyService::isSourceActive(media::AudioSourceType sourceAidl, bool* _aidl_return) {
+ audio_source_t source = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioSourceType_audio_source_t(sourceAidl));
if (mAudioPolicyManager == NULL) {
- return false;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->isSourceActive(source);
+ *_aidl_return = mAudioPolicyManager->isSourceActive(source);
+ return Status::ok();
}
status_t AudioPolicyService::getAudioPolicyEffects(sp<AudioPolicyEffects>& audioPolicyEffects)
@@ -986,246 +1258,378 @@
return OK;
}
-status_t AudioPolicyService::queryDefaultPreProcessing(audio_session_t audioSession,
- effect_descriptor_t *descriptors,
- uint32_t *count)
-{
- sp<AudioPolicyEffects>audioPolicyEffects;
- status_t status = getAudioPolicyEffects(audioPolicyEffects);
- if (status != OK) {
- *count = 0;
- return status;
+Status AudioPolicyService::queryDefaultPreProcessing(
+ int32_t audioSessionAidl,
+ media::Int* countAidl,
+ std::vector<media::EffectDescriptor>* _aidl_return) {
+ audio_session_t audioSession = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_session_t(audioSessionAidl));
+ uint32_t count = VALUE_OR_RETURN_BINDER_STATUS(convertIntegral<uint32_t>(countAidl->value));
+ if (count > AudioEffect::kMaxPreProcessing) {
+ count = AudioEffect::kMaxPreProcessing;
}
- return audioPolicyEffects->queryDefaultInputEffects(
- (audio_session_t)audioSession, descriptors, count);
+ uint32_t countReq = count;
+ std::unique_ptr<effect_descriptor_t[]> descriptors(new effect_descriptor_t[count]);
+
+ sp<AudioPolicyEffects> audioPolicyEffects;
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(getAudioPolicyEffects(audioPolicyEffects)));
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(audioPolicyEffects->queryDefaultInputEffects(
+ (audio_session_t) audioSession, descriptors.get(), &count)));
+ countReq = std::min(count, countReq);
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ convertRange(descriptors.get(), descriptors.get() + countReq,
+ std::back_inserter(*_aidl_return),
+ legacy2aidl_effect_descriptor_t_EffectDescriptor)));
+ countAidl->value = VALUE_OR_RETURN_BINDER_STATUS(convertIntegral<uint32_t>(count));
+ return Status::ok();
}
-status_t AudioPolicyService::addSourceDefaultEffect(const effect_uuid_t *type,
- const String16& opPackageName,
- const effect_uuid_t *uuid,
- int32_t priority,
- audio_source_t source,
- audio_unique_id_t* id)
-{
+Status AudioPolicyService::addSourceDefaultEffect(const media::AudioUuid& typeAidl,
+ const std::string& opPackageNameAidl,
+ const media::AudioUuid& uuidAidl,
+ int32_t priority,
+ media::AudioSourceType sourceAidl,
+ int32_t* _aidl_return) {
+ effect_uuid_t type = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioUuid_audio_uuid_t(typeAidl));
+ String16 opPackageName = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_string_view_String16(opPackageNameAidl));
+ effect_uuid_t uuid = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioUuid_audio_uuid_t(uuidAidl));
+ audio_source_t source = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioSourceType_audio_source_t(sourceAidl));
+ audio_unique_id_t id;
+
sp<AudioPolicyEffects>audioPolicyEffects;
- status_t status = getAudioPolicyEffects(audioPolicyEffects);
- if (status != OK) {
- return status;
- }
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(getAudioPolicyEffects(audioPolicyEffects)));
if (!modifyDefaultAudioEffectsAllowed()) {
- return PERMISSION_DENIED;
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
- return audioPolicyEffects->addSourceDefaultEffect(
- type, opPackageName, uuid, priority, source, id);
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(audioPolicyEffects->addSourceDefaultEffect(
+ &type, opPackageName, &uuid, priority, source, &id)));
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(legacy2aidl_audio_unique_id_t_int32_t(id));
+ return Status::ok();
}
-status_t AudioPolicyService::addStreamDefaultEffect(const effect_uuid_t *type,
- const String16& opPackageName,
- const effect_uuid_t *uuid,
- int32_t priority,
- audio_usage_t usage,
- audio_unique_id_t* id)
-{
- sp<AudioPolicyEffects>audioPolicyEffects;
- status_t status = getAudioPolicyEffects(audioPolicyEffects);
- if (status != OK) {
- return status;
- }
+Status AudioPolicyService::addStreamDefaultEffect(const media::AudioUuid& typeAidl,
+ const std::string& opPackageNameAidl,
+ const media::AudioUuid& uuidAidl,
+ int32_t priority, media::AudioUsage usageAidl,
+ int32_t* _aidl_return) {
+ effect_uuid_t type = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioUuid_audio_uuid_t(typeAidl));
+ String16 opPackageName = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_string_view_String16(opPackageNameAidl));
+ effect_uuid_t uuid = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioUuid_audio_uuid_t(uuidAidl));
+ audio_usage_t usage = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioUsage_audio_usage_t(usageAidl));
+ audio_unique_id_t id;
+
+ sp<AudioPolicyEffects> audioPolicyEffects;
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(getAudioPolicyEffects(audioPolicyEffects)));
if (!modifyDefaultAudioEffectsAllowed()) {
- return PERMISSION_DENIED;
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
- return audioPolicyEffects->addStreamDefaultEffect(
- type, opPackageName, uuid, priority, usage, id);
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(audioPolicyEffects->addStreamDefaultEffect(
+ &type, opPackageName, &uuid, priority, usage, &id)));
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(legacy2aidl_audio_unique_id_t_int32_t(id));
+ return Status::ok();
}
-status_t AudioPolicyService::removeSourceDefaultEffect(audio_unique_id_t id)
+Status AudioPolicyService::removeSourceDefaultEffect(int32_t idAidl)
{
+ audio_unique_id_t id = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_unique_id_t(idAidl));
sp<AudioPolicyEffects>audioPolicyEffects;
- status_t status = getAudioPolicyEffects(audioPolicyEffects);
- if (status != OK) {
- return status;
- }
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(getAudioPolicyEffects(audioPolicyEffects)));
if (!modifyDefaultAudioEffectsAllowed()) {
- return PERMISSION_DENIED;
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
- return audioPolicyEffects->removeSourceDefaultEffect(id);
+ return binderStatusFromStatusT(audioPolicyEffects->removeSourceDefaultEffect(id));
}
-status_t AudioPolicyService::removeStreamDefaultEffect(audio_unique_id_t id)
+Status AudioPolicyService::removeStreamDefaultEffect(int32_t idAidl)
{
+ audio_unique_id_t id = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_unique_id_t(idAidl));
sp<AudioPolicyEffects>audioPolicyEffects;
- status_t status = getAudioPolicyEffects(audioPolicyEffects);
- if (status != OK) {
- return status;
- }
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(getAudioPolicyEffects(audioPolicyEffects)));
if (!modifyDefaultAudioEffectsAllowed()) {
- return PERMISSION_DENIED;
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
- return audioPolicyEffects->removeStreamDefaultEffect(id);
+ return binderStatusFromStatusT(audioPolicyEffects->removeStreamDefaultEffect(id));
}
-status_t AudioPolicyService::setSupportedSystemUsages(const std::vector<audio_usage_t>& systemUsages) {
+Status AudioPolicyService::setSupportedSystemUsages(
+ const std::vector<media::AudioUsage>& systemUsagesAidl) {
+ size_t size = systemUsagesAidl.size();
+ if (size > MAX_ITEMS_PER_LIST) {
+ size = MAX_ITEMS_PER_LIST;
+ }
+ std::vector<audio_usage_t> systemUsages;
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ convertRange(systemUsagesAidl.begin(), systemUsagesAidl.begin() + size,
+ std::back_inserter(systemUsages), aidl2legacy_AudioUsage_audio_usage_t)));
+
Mutex::Autolock _l(mLock);
if(!modifyAudioRoutingAllowed()) {
- return PERMISSION_DENIED;
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
bool areAllSystemUsages = std::all_of(begin(systemUsages), end(systemUsages),
[](audio_usage_t usage) { return isSystemUsage(usage); });
if (!areAllSystemUsages) {
- return BAD_VALUE;
+ return binderStatusFromStatusT(BAD_VALUE);
}
mSupportedSystemUsages = systemUsages;
- return NO_ERROR;
+ return Status::ok();
}
-status_t AudioPolicyService::setAllowedCapturePolicy(uid_t uid, audio_flags_mask_t capturePolicy) {
+Status AudioPolicyService::setAllowedCapturePolicy(int32_t uidAidl, int32_t capturePolicyAidl) {
+ uid_t uid = VALUE_OR_RETURN_BINDER_STATUS(aidl2legacy_int32_t_uid_t(uidAidl));
+ audio_flags_mask_t capturePolicy = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_flags_mask_t_mask(capturePolicyAidl));
+
Mutex::Autolock _l(mLock);
if (mAudioPolicyManager == NULL) {
ALOGV("%s() mAudioPolicyManager == NULL", __func__);
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
- return mAudioPolicyManager->setAllowedCapturePolicy(uid, capturePolicy);
+ return binderStatusFromStatusT(
+ mAudioPolicyManager->setAllowedCapturePolicy(uid, capturePolicy));
}
-bool AudioPolicyService::isOffloadSupported(const audio_offload_info_t& info)
-{
+Status AudioPolicyService::getOffloadSupport(const media::AudioOffloadInfo& infoAidl,
+ media::AudioOffloadMode* _aidl_return) {
+ audio_offload_info_t info = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioOffloadInfo_audio_offload_info_t(infoAidl));
if (mAudioPolicyManager == NULL) {
ALOGV("mAudioPolicyManager == NULL");
- return false;
+ return binderStatusFromStatusT(AUDIO_OFFLOAD_NOT_SUPPORTED);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->isOffloadSupported(info);
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(legacy2aidl_audio_offload_mode_t_AudioOffloadMode(
+ mAudioPolicyManager->getOffloadSupport(info)));
+ return Status::ok();
}
-bool AudioPolicyService::isDirectOutputSupported(const audio_config_base_t& config,
- const audio_attributes_t& attributes) {
+Status AudioPolicyService::isDirectOutputSupported(
+ const media::AudioConfigBase& configAidl,
+ const media::AudioAttributesInternal& attributesAidl,
+ bool* _aidl_return) {
+ audio_config_base_t config = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioConfigBase_audio_config_base_t(configAidl));
+ audio_attributes_t attributes = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioAttributesInternal_audio_attributes_t(attributesAidl));
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ AudioValidator::validateAudioAttributes(attributes, "169572641")));
+
if (mAudioPolicyManager == NULL) {
ALOGV("mAudioPolicyManager == NULL");
- return false;
+ return binderStatusFromStatusT(NO_INIT);
}
- status_t result = validateUsage(attributes.usage);
- if (result != NO_ERROR) {
- return result;
- }
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(validateUsage(attributes.usage)));
Mutex::Autolock _l(mLock);
- return mAudioPolicyManager->isDirectOutputSupported(config, attributes);
+ *_aidl_return = mAudioPolicyManager->isDirectOutputSupported(config, attributes);
+ return Status::ok();
}
-status_t AudioPolicyService::listAudioPorts(audio_port_role_t role,
- audio_port_type_t type,
- unsigned int *num_ports,
- struct audio_port *ports,
- unsigned int *generation)
-{
+Status AudioPolicyService::listAudioPorts(media::AudioPortRole roleAidl,
+ media::AudioPortType typeAidl, media::Int* count,
+ std::vector<media::AudioPort>* portsAidl,
+ int32_t* _aidl_return) {
+ audio_port_role_t role = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioPortRole_audio_port_role_t(roleAidl));
+ audio_port_type_t type = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioPortType_audio_port_type_t(typeAidl));
+ unsigned int num_ports = VALUE_OR_RETURN_BINDER_STATUS(
+ convertIntegral<unsigned int>(count->value));
+ if (num_ports > MAX_ITEMS_PER_LIST) {
+ num_ports = MAX_ITEMS_PER_LIST;
+ }
+ unsigned int numPortsReq = num_ports;
+ std::unique_ptr<audio_port_v7[]> ports(new audio_port_v7[num_ports]);
+ unsigned int generation;
+
Mutex::Autolock _l(mLock);
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
AutoCallerClear acc;
- return mAudioPolicyManager->listAudioPorts(role, type, num_ports, ports, generation);
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ mAudioPolicyManager->listAudioPorts(role, type, &num_ports, ports.get(), &generation)));
+ numPortsReq = std::min(numPortsReq, num_ports);
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ convertRange(ports.get(), ports.get() + numPortsReq, std::back_inserter(*portsAidl),
+ legacy2aidl_audio_port_v7_AudioPort)));
+ count->value = VALUE_OR_RETURN_BINDER_STATUS(convertIntegral<int32_t>(num_ports));
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(convertIntegral<int32_t>(generation));
+ return Status::ok();
}
-status_t AudioPolicyService::getAudioPort(struct audio_port *port)
-{
+Status AudioPolicyService::getAudioPort(const media::AudioPort& portAidl,
+ media::AudioPort* _aidl_return) {
+ audio_port_v7 port = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioPort_audio_port_v7(portAidl));
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(AudioValidator::validateAudioPort(port)));
+
Mutex::Autolock _l(mLock);
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
AutoCallerClear acc;
- return mAudioPolicyManager->getAudioPort(port);
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(mAudioPolicyManager->getAudioPort(&port)));
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(legacy2aidl_audio_port_v7_AudioPort(port));
+ return Status::ok();
}
-status_t AudioPolicyService::createAudioPatch(const struct audio_patch *patch,
- audio_patch_handle_t *handle)
-{
+Status AudioPolicyService::createAudioPatch(const media::AudioPatch& patchAidl, int32_t handleAidl,
+ int32_t* _aidl_return) {
+ audio_patch patch = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioPatch_audio_patch(patchAidl));
+ audio_patch_handle_t handle = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_port_handle_t(handleAidl));
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(AudioValidator::validateAudioPatch(patch)));
+
Mutex::Autolock _l(mLock);
if(!modifyAudioRoutingAllowed()) {
- return PERMISSION_DENIED;
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
AutoCallerClear acc;
- return mAudioPolicyManager->createAudioPatch(patch, handle,
- IPCThreadState::self()->getCallingUid());
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ mAudioPolicyManager->createAudioPatch(&patch, &handle,
+ IPCThreadState::self()->getCallingUid())));
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(legacy2aidl_audio_patch_handle_t_int32_t(handle));
+ return Status::ok();
}
-status_t AudioPolicyService::releaseAudioPatch(audio_patch_handle_t handle)
+Status AudioPolicyService::releaseAudioPatch(int32_t handleAidl)
{
+ audio_patch_handle_t handle = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_patch_handle_t(handleAidl));
Mutex::Autolock _l(mLock);
if(!modifyAudioRoutingAllowed()) {
- return PERMISSION_DENIED;
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
AutoCallerClear acc;
- return mAudioPolicyManager->releaseAudioPatch(handle,
- IPCThreadState::self()->getCallingUid());
+ return binderStatusFromStatusT(
+ mAudioPolicyManager->releaseAudioPatch(handle,
+ IPCThreadState::self()->getCallingUid()));
}
-status_t AudioPolicyService::listAudioPatches(unsigned int *num_patches,
- struct audio_patch *patches,
- unsigned int *generation)
-{
+Status AudioPolicyService::listAudioPatches(media::Int* count,
+ std::vector<media::AudioPatch>* patchesAidl,
+ int32_t* _aidl_return) {
+ unsigned int num_patches = VALUE_OR_RETURN_BINDER_STATUS(
+ convertIntegral<unsigned int>(count->value));
+ if (num_patches > MAX_ITEMS_PER_LIST) {
+ num_patches = MAX_ITEMS_PER_LIST;
+ }
+ unsigned int numPatchesReq = num_patches;
+ std::unique_ptr<audio_patch[]> patches(new audio_patch[num_patches]);
+ unsigned int generation;
+
Mutex::Autolock _l(mLock);
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
AutoCallerClear acc;
- return mAudioPolicyManager->listAudioPatches(num_patches, patches, generation);
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ mAudioPolicyManager->listAudioPatches(&num_patches, patches.get(), &generation)));
+ numPatchesReq = std::min(numPatchesReq, num_patches);
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ convertRange(patches.get(), patches.get() + numPatchesReq,
+ std::back_inserter(*patchesAidl), legacy2aidl_audio_patch_AudioPatch)));
+ count->value = VALUE_OR_RETURN_BINDER_STATUS(convertIntegral<int32_t>(num_patches));
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(convertIntegral<int32_t>(generation));
+ return Status::ok();
}
-status_t AudioPolicyService::setAudioPortConfig(const struct audio_port_config *config)
+Status AudioPolicyService::setAudioPortConfig(const media::AudioPortConfig& configAidl)
{
+ audio_port_config config = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioPortConfig_audio_port_config(configAidl));
+ RETURN_IF_BINDER_ERROR(
+ binderStatusFromStatusT(AudioValidator::validateAudioPortConfig(config)));
+
Mutex::Autolock _l(mLock);
if(!modifyAudioRoutingAllowed()) {
- return PERMISSION_DENIED;
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
AutoCallerClear acc;
- return mAudioPolicyManager->setAudioPortConfig(config);
+ return binderStatusFromStatusT(mAudioPolicyManager->setAudioPortConfig(&config));
}
-status_t AudioPolicyService::acquireSoundTriggerSession(audio_session_t *session,
- audio_io_handle_t *ioHandle,
- audio_devices_t *device)
+Status AudioPolicyService::acquireSoundTriggerSession(media::SoundTriggerSession* _aidl_return)
{
+ audio_session_t session;
+ audio_io_handle_t ioHandle;
+ audio_devices_t device;
+
+ {
+ Mutex::Autolock _l(mLock);
+ if (mAudioPolicyManager == NULL) {
+ return binderStatusFromStatusT(NO_INIT);
+ }
+ AutoCallerClear acc;
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ mAudioPolicyManager->acquireSoundTriggerSession(&session, &ioHandle, &device)));
+ }
+
+ _aidl_return->session = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_session_t_int32_t(session));
+ _aidl_return->ioHandle = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_io_handle_t_int32_t(ioHandle));
+ _aidl_return->device = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_audio_devices_t_int32_t(device));
+ return Status::ok();
+}
+
+Status AudioPolicyService::releaseSoundTriggerSession(int32_t sessionAidl)
+{
+ audio_session_t session = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_session_t(sessionAidl));
Mutex::Autolock _l(mLock);
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
AutoCallerClear acc;
- return mAudioPolicyManager->acquireSoundTriggerSession(session, ioHandle, device);
+ return binderStatusFromStatusT(mAudioPolicyManager->releaseSoundTriggerSession(session));
}
-status_t AudioPolicyService::releaseSoundTriggerSession(audio_session_t session)
-{
- Mutex::Autolock _l(mLock);
- if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+Status AudioPolicyService::registerPolicyMixes(const std::vector<media::AudioMix>& mixesAidl,
+ bool registration) {
+ size_t size = mixesAidl.size();
+ if (size > MAX_MIXES_PER_POLICY) {
+ size = MAX_MIXES_PER_POLICY;
}
- AutoCallerClear acc;
- return mAudioPolicyManager->releaseSoundTriggerSession(session);
-}
+ Vector<AudioMix> mixes;
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ convertRange(mixesAidl.begin(), mixesAidl.begin() + size, std::back_inserter(mixes),
+ aidl2legacy_AudioMix)));
-status_t AudioPolicyService::registerPolicyMixes(const Vector<AudioMix>& mixes, bool registration)
-{
Mutex::Autolock _l(mLock);
// loopback|render only need a MediaProjection (checked in caller AudioService.java)
bool needModifyAudioRouting = std::any_of(mixes.begin(), mixes.end(), [](auto& mix) {
return !is_mix_loopback_render(mix.mRouteFlags); });
if (needModifyAudioRouting && !modifyAudioRoutingAllowed()) {
- return PERMISSION_DENIED;
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
// If one of the mixes has needCaptureVoiceCommunicationOutput set to true, then we
@@ -1235,358 +1639,556 @@
return mix.mVoiceCommunicationCaptureAllowed; });
bool needCaptureMediaOutput = std::any_of(mixes.begin(), mixes.end(), [](auto& mix) {
- return mix.mAllowPrivilegedPlaybackCapture; });
+ return mix.mAllowPrivilegedMediaPlaybackCapture; });
- const uid_t callingUid = IPCThreadState::self()->getCallingUid();
- const pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ const Identity identity = getCallingIdentity();
- if (needCaptureMediaOutput && !captureMediaOutputAllowed(callingPid, callingUid)) {
- return PERMISSION_DENIED;
+
+ if (needCaptureMediaOutput && !captureMediaOutputAllowed(identity)) {
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (needCaptureVoiceCommunicationOutput &&
- !captureVoiceCommunicationOutputAllowed(callingPid, callingUid)) {
- return PERMISSION_DENIED;
+ !captureVoiceCommunicationOutputAllowed(identity)) {
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
AutoCallerClear acc;
if (registration) {
- return mAudioPolicyManager->registerPolicyMixes(mixes);
+ return binderStatusFromStatusT(mAudioPolicyManager->registerPolicyMixes(mixes));
} else {
- return mAudioPolicyManager->unregisterPolicyMixes(mixes);
+ return binderStatusFromStatusT(mAudioPolicyManager->unregisterPolicyMixes(mixes));
}
}
-status_t AudioPolicyService::setUidDeviceAffinities(uid_t uid,
- const AudioDeviceTypeAddrVector& devices) {
+Status AudioPolicyService::setUidDeviceAffinities(
+ int32_t uidAidl,
+ const std::vector<media::AudioDevice>& devicesAidl) {
+ uid_t uid = VALUE_OR_RETURN_BINDER_STATUS(aidl2legacy_int32_t_uid_t(uidAidl));
+ AudioDeviceTypeAddrVector devices = VALUE_OR_RETURN_BINDER_STATUS(
+ convertContainer<AudioDeviceTypeAddrVector>(devicesAidl,
+ aidl2legacy_AudioDeviceTypeAddress));
+
Mutex::Autolock _l(mLock);
if(!modifyAudioRoutingAllowed()) {
- return PERMISSION_DENIED;
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
AutoCallerClear acc;
- return mAudioPolicyManager->setUidDeviceAffinities(uid, devices);
+ return binderStatusFromStatusT(mAudioPolicyManager->setUidDeviceAffinities(uid, devices));
}
-status_t AudioPolicyService::removeUidDeviceAffinities(uid_t uid) {
+Status AudioPolicyService::removeUidDeviceAffinities(int32_t uidAidl) {
+ uid_t uid = VALUE_OR_RETURN_BINDER_STATUS(aidl2legacy_int32_t_uid_t(uidAidl));
+
Mutex::Autolock _l(mLock);
if(!modifyAudioRoutingAllowed()) {
- return PERMISSION_DENIED;
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
AutoCallerClear acc;
- return mAudioPolicyManager->removeUidDeviceAffinities(uid);
+ return binderStatusFromStatusT(mAudioPolicyManager->removeUidDeviceAffinities(uid));
}
-status_t AudioPolicyService::setUserIdDeviceAffinities(int userId,
- const AudioDeviceTypeAddrVector& devices) {
+Status AudioPolicyService::setUserIdDeviceAffinities(
+ int32_t userIdAidl,
+ const std::vector<media::AudioDevice>& devicesAidl) {
+ int userId = VALUE_OR_RETURN_BINDER_STATUS(convertReinterpret<int>(userIdAidl));
+ AudioDeviceTypeAddrVector devices = VALUE_OR_RETURN_BINDER_STATUS(
+ convertContainer<AudioDeviceTypeAddrVector>(devicesAidl,
+ aidl2legacy_AudioDeviceTypeAddress));
+
Mutex::Autolock _l(mLock);
if(!modifyAudioRoutingAllowed()) {
- return PERMISSION_DENIED;
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
AutoCallerClear acc;
- return mAudioPolicyManager->setUserIdDeviceAffinities(userId, devices);
+ return binderStatusFromStatusT(mAudioPolicyManager->setUserIdDeviceAffinities(userId, devices));
}
-status_t AudioPolicyService::removeUserIdDeviceAffinities(int userId) {
+Status AudioPolicyService::removeUserIdDeviceAffinities(int32_t userIdAidl) {
+ int userId = VALUE_OR_RETURN_BINDER_STATUS(convertReinterpret<int>(userIdAidl));
+
Mutex::Autolock _l(mLock);
if(!modifyAudioRoutingAllowed()) {
- return PERMISSION_DENIED;
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
AutoCallerClear acc;
- return mAudioPolicyManager->removeUserIdDeviceAffinities(userId);
+ return binderStatusFromStatusT(mAudioPolicyManager->removeUserIdDeviceAffinities(userId));
}
-status_t AudioPolicyService::startAudioSource(const struct audio_port_config *source,
- const audio_attributes_t *attributes,
- audio_port_handle_t *portId)
-{
+Status AudioPolicyService::startAudioSource(const media::AudioPortConfig& sourceAidl,
+ const media::AudioAttributesInternal& attributesAidl,
+ int32_t* _aidl_return) {
+ audio_port_config source = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioPortConfig_audio_port_config(sourceAidl));
+ audio_attributes_t attributes = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioAttributesInternal_audio_attributes_t(attributesAidl));
+ audio_port_handle_t portId;
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ AudioValidator::validateAudioPortConfig(source)));
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ AudioValidator::validateAudioAttributes(attributes, "68953950")));
+
Mutex::Autolock _l(mLock);
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
- status_t result = validateUsage(attributes->usage);
- if (result != NO_ERROR) {
- return result;
- }
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(validateUsage(attributes.usage)));
// startAudioSource should be created as the calling uid
const uid_t callingUid = IPCThreadState::self()->getCallingUid();
AutoCallerClear acc;
- return mAudioPolicyManager->startAudioSource(source, attributes, portId, callingUid);
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ mAudioPolicyManager->startAudioSource(&source, &attributes, &portId, callingUid)));
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(legacy2aidl_audio_port_handle_t_int32_t(portId));
+ return Status::ok();
}
-status_t AudioPolicyService::stopAudioSource(audio_port_handle_t portId)
+Status AudioPolicyService::stopAudioSource(int32_t portIdAidl)
{
+ audio_port_handle_t portId = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_port_handle_t(portIdAidl));
+
Mutex::Autolock _l(mLock);
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
AutoCallerClear acc;
- return mAudioPolicyManager->stopAudioSource(portId);
+ return binderStatusFromStatusT(mAudioPolicyManager->stopAudioSource(portId));
}
-status_t AudioPolicyService::setMasterMono(bool mono)
+Status AudioPolicyService::setMasterMono(bool mono)
{
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
if (!settingsAllowed()) {
- return PERMISSION_DENIED;
+ return binderStatusFromStatusT(PERMISSION_DENIED);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->setMasterMono(mono);
+ return binderStatusFromStatusT(mAudioPolicyManager->setMasterMono(mono));
}
-status_t AudioPolicyService::getMasterMono(bool *mono)
+Status AudioPolicyService::getMasterMono(bool* _aidl_return)
{
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->getMasterMono(mono);
+ return binderStatusFromStatusT(mAudioPolicyManager->getMasterMono(_aidl_return));
}
-float AudioPolicyService::getStreamVolumeDB(
- audio_stream_type_t stream, int index, audio_devices_t device)
-{
+Status AudioPolicyService::getStreamVolumeDB(media::AudioStreamType streamAidl, int32_t indexAidl,
+ int32_t deviceAidl, float* _aidl_return) {
+ audio_stream_type_t stream = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioStreamType_audio_stream_type_t(streamAidl));
+ int index = VALUE_OR_RETURN_BINDER_STATUS(convertIntegral<int>(indexAidl));
+ audio_devices_t device = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_devices_t(deviceAidl));
+
if (mAudioPolicyManager == NULL) {
- return NAN;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->getStreamVolumeDB(stream, index, device);
+ *_aidl_return = mAudioPolicyManager->getStreamVolumeDB(stream, index, device);
+ return Status::ok();
}
-status_t AudioPolicyService::getSurroundFormats(unsigned int *numSurroundFormats,
- audio_format_t *surroundFormats,
- bool *surroundFormatsEnabled,
- bool reported)
-{
+Status AudioPolicyService::getSurroundFormats(media::Int* count,
+ std::vector<media::audio::common::AudioFormat>* formats,
+ std::vector<bool>* formatsEnabled) {
+ unsigned int numSurroundFormats = VALUE_OR_RETURN_BINDER_STATUS(
+ convertIntegral<unsigned int>(count->value));
+ if (numSurroundFormats > MAX_ITEMS_PER_LIST) {
+ numSurroundFormats = MAX_ITEMS_PER_LIST;
+ }
+ unsigned int numSurroundFormatsReq = numSurroundFormats;
+ std::unique_ptr<audio_format_t[]>surroundFormats(new audio_format_t[numSurroundFormats]);
+ std::unique_ptr<bool[]>surroundFormatsEnabled(new bool[numSurroundFormats]);
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->getSurroundFormats(numSurroundFormats, surroundFormats,
- surroundFormatsEnabled, reported);
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ mAudioPolicyManager->getSurroundFormats(&numSurroundFormats, surroundFormats.get(),
+ surroundFormatsEnabled.get())));
+ numSurroundFormatsReq = std::min(numSurroundFormats, numSurroundFormatsReq);
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ convertRange(surroundFormats.get(), surroundFormats.get() + numSurroundFormatsReq,
+ std::back_inserter(*formats), legacy2aidl_audio_format_t_AudioFormat)));
+ formatsEnabled->insert(
+ formatsEnabled->begin(),
+ surroundFormatsEnabled.get(),
+ surroundFormatsEnabled.get() + numSurroundFormatsReq);
+ count->value = VALUE_OR_RETURN_BINDER_STATUS(convertIntegral<uint32_t>(numSurroundFormats));
+ return Status::ok();
}
-status_t AudioPolicyService::getHwOffloadEncodingFormatsSupportedForA2DP(
- std::vector<audio_format_t> *formats)
-{
+Status AudioPolicyService::getReportedSurroundFormats(
+ media::Int* count, std::vector<media::audio::common::AudioFormat>* formats) {
+ unsigned int numSurroundFormats = VALUE_OR_RETURN_BINDER_STATUS(
+ convertIntegral<unsigned int>(count->value));
+ if (numSurroundFormats > MAX_ITEMS_PER_LIST) {
+ numSurroundFormats = MAX_ITEMS_PER_LIST;
+ }
+ unsigned int numSurroundFormatsReq = numSurroundFormats;
+ std::unique_ptr<audio_format_t[]>surroundFormats(new audio_format_t[numSurroundFormats]);
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->getHwOffloadEncodingFormatsSupportedForA2DP(formats);
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ mAudioPolicyManager->getReportedSurroundFormats(
+ &numSurroundFormats, surroundFormats.get())));
+ numSurroundFormatsReq = std::min(numSurroundFormats, numSurroundFormatsReq);
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ convertRange(surroundFormats.get(), surroundFormats.get() + numSurroundFormatsReq,
+ std::back_inserter(*formats), legacy2aidl_audio_format_t_AudioFormat)));
+ count->value = VALUE_OR_RETURN_BINDER_STATUS(convertIntegral<uint32_t>(numSurroundFormats));
+ return Status::ok();
}
-status_t AudioPolicyService::setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled)
-{
+Status AudioPolicyService::getHwOffloadEncodingFormatsSupportedForA2DP(
+ std::vector<media::audio::common::AudioFormat>* _aidl_return) {
+ std::vector<audio_format_t> formats;
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->setSurroundFormatEnabled(audioFormat, enabled);
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ mAudioPolicyManager->getHwOffloadEncodingFormatsSupportedForA2DP(&formats)));
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(
+ convertContainer<std::vector<media::audio::common::AudioFormat>>(
+ formats,
+ legacy2aidl_audio_format_t_AudioFormat));
+ return Status::ok();
}
-status_t AudioPolicyService::setAssistantUid(uid_t uid)
+Status AudioPolicyService::setSurroundFormatEnabled(
+ media::audio::common::AudioFormat audioFormatAidl, bool enabled) {
+ audio_format_t audioFormat = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioFormat_audio_format_t(audioFormatAidl));
+ if (mAudioPolicyManager == NULL) {
+ return binderStatusFromStatusT(NO_INIT);
+ }
+ Mutex::Autolock _l(mLock);
+ AutoCallerClear acc;
+ return binderStatusFromStatusT(
+ mAudioPolicyManager->setSurroundFormatEnabled(audioFormat, enabled));
+}
+
+Status AudioPolicyService::setAssistantUid(int32_t uidAidl)
{
+ uid_t uid = VALUE_OR_RETURN_BINDER_STATUS(aidl2legacy_int32_t_uid_t(uidAidl));
Mutex::Autolock _l(mLock);
mUidPolicy->setAssistantUid(uid);
- return NO_ERROR;
+ return Status::ok();
}
-status_t AudioPolicyService::setA11yServicesUids(const std::vector<uid_t>& uids)
+Status AudioPolicyService::setA11yServicesUids(const std::vector<int32_t>& uidsAidl)
{
+ size_t size = uidsAidl.size();
+ if (size > MAX_ITEMS_PER_LIST) {
+ size = MAX_ITEMS_PER_LIST;
+ }
+ std::vector<uid_t> uids;
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ convertRange(uidsAidl.begin(),
+ uidsAidl.begin() + size,
+ std::back_inserter(uids),
+ aidl2legacy_int32_t_uid_t)));
Mutex::Autolock _l(mLock);
mUidPolicy->setA11yUids(uids);
- return NO_ERROR;
+ return Status::ok();
}
-status_t AudioPolicyService::setCurrentImeUid(uid_t uid)
+Status AudioPolicyService::setCurrentImeUid(int32_t uidAidl)
{
+ uid_t uid = VALUE_OR_RETURN_BINDER_STATUS(aidl2legacy_int32_t_uid_t(uidAidl));
Mutex::Autolock _l(mLock);
mUidPolicy->setCurrentImeUid(uid);
- return NO_ERROR;
+ return Status::ok();
}
-bool AudioPolicyService::isHapticPlaybackSupported()
+Status AudioPolicyService::isHapticPlaybackSupported(bool* _aidl_return)
{
if (mAudioPolicyManager == NULL) {
- ALOGW("%s, mAudioPolicyManager == NULL", __func__);
- return false;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->isHapticPlaybackSupported();
+ *_aidl_return = mAudioPolicyManager->isHapticPlaybackSupported();
+ return Status::ok();
}
-status_t AudioPolicyService::listAudioProductStrategies(AudioProductStrategyVector &strategies)
-{
+Status AudioPolicyService::listAudioProductStrategies(
+ std::vector<media::AudioProductStrategy>* _aidl_return) {
+ AudioProductStrategyVector strategies;
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
- return mAudioPolicyManager->listAudioProductStrategies(strategies);
+ RETURN_IF_BINDER_ERROR(
+ binderStatusFromStatusT(mAudioPolicyManager->listAudioProductStrategies(strategies)));
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(
+ convertContainer<std::vector<media::AudioProductStrategy>>(
+ strategies,
+ legacy2aidl_AudioProductStrategy));
+ return Status::ok();
}
-status_t AudioPolicyService::getProductStrategyFromAudioAttributes(
- const AudioAttributes &aa, product_strategy_t &productStrategy)
-{
+Status AudioPolicyService::getProductStrategyFromAudioAttributes(
+ const media::AudioAttributesEx& aaAidl, bool fallbackOnDefault, int32_t* _aidl_return) {
+ AudioAttributes aa = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioAttributesEx_AudioAttributes(aaAidl));
+ product_strategy_t productStrategy;
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
- return mAudioPolicyManager->getProductStrategyFromAudioAttributes(aa, productStrategy);
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ mAudioPolicyManager->getProductStrategyFromAudioAttributes(
+ aa, productStrategy, fallbackOnDefault)));
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(
+ legacy2aidl_product_strategy_t_int32_t(productStrategy));
+ return Status::ok();
}
-status_t AudioPolicyService::listAudioVolumeGroups(AudioVolumeGroupVector &groups)
+Status AudioPolicyService::listAudioVolumeGroups(std::vector<media::AudioVolumeGroup>* _aidl_return)
{
+ AudioVolumeGroupVector groups;
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
- return mAudioPolicyManager->listAudioVolumeGroups(groups);
+ RETURN_IF_BINDER_ERROR(
+ binderStatusFromStatusT(mAudioPolicyManager->listAudioVolumeGroups(groups)));
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(
+ convertContainer<std::vector<media::AudioVolumeGroup>>(groups,
+ legacy2aidl_AudioVolumeGroup));
+ return Status::ok();
}
-status_t AudioPolicyService::getVolumeGroupFromAudioAttributes(const AudioAttributes &aa,
- volume_group_t &volumeGroup)
-{
+Status AudioPolicyService::getVolumeGroupFromAudioAttributes(
+ const media::AudioAttributesEx& aaAidl, bool fallbackOnDefault, int32_t* _aidl_return) {
+ AudioAttributes aa = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioAttributesEx_AudioAttributes(aaAidl));
+ volume_group_t volumeGroup;
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
- return mAudioPolicyManager->getVolumeGroupFromAudioAttributes(aa, volumeGroup);
+ RETURN_IF_BINDER_ERROR(
+ binderStatusFromStatusT(
+ mAudioPolicyManager->getVolumeGroupFromAudioAttributes(
+ aa, volumeGroup, fallbackOnDefault)));
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(legacy2aidl_volume_group_t_int32_t(volumeGroup));
+ return Status::ok();
}
-status_t AudioPolicyService::setRttEnabled(bool enabled)
+Status AudioPolicyService::setRttEnabled(bool enabled)
{
Mutex::Autolock _l(mLock);
mUidPolicy->setRttEnabled(enabled);
- return NO_ERROR;
+ return Status::ok();
}
-bool AudioPolicyService::isCallScreenModeSupported()
+Status AudioPolicyService::isCallScreenModeSupported(bool* _aidl_return)
{
if (mAudioPolicyManager == NULL) {
- ALOGW("%s, mAudioPolicyManager == NULL", __func__);
- return false;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->isCallScreenModeSupported();
+ *_aidl_return = mAudioPolicyManager->isCallScreenModeSupported();
+ return Status::ok();
}
-status_t AudioPolicyService::setDevicesRoleForStrategy(product_strategy_t strategy,
- device_role_t role,
- const AudioDeviceTypeAddrVector &devices)
-{
+Status AudioPolicyService::setDevicesRoleForStrategy(
+ int32_t strategyAidl,
+ media::DeviceRole roleAidl,
+ const std::vector<media::AudioDevice>& devicesAidl) {
+ product_strategy_t strategy = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_product_strategy_t(strategyAidl));
+ device_role_t role = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_DeviceRole_device_role_t(roleAidl));
+ AudioDeviceTypeAddrVector devices = VALUE_OR_RETURN_BINDER_STATUS(
+ convertContainer<AudioDeviceTypeAddrVector>(devicesAidl,
+ aidl2legacy_AudioDeviceTypeAddress));
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
- return mAudioPolicyManager->setDevicesRoleForStrategy(strategy, role, devices);
+ return binderStatusFromStatusT(
+ mAudioPolicyManager->setDevicesRoleForStrategy(strategy, role, devices));
}
-status_t AudioPolicyService::removeDevicesRoleForStrategy(product_strategy_t strategy,
- device_role_t role)
-{
+Status AudioPolicyService::removeDevicesRoleForStrategy(int32_t strategyAidl,
+ media::DeviceRole roleAidl) {
+ product_strategy_t strategy = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_product_strategy_t(strategyAidl));
+ device_role_t role = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_DeviceRole_device_role_t(roleAidl));
+ if (mAudioPolicyManager == NULL) {
+ return binderStatusFromStatusT(NO_INIT);
+ }
+ Mutex::Autolock _l(mLock);
+ return binderStatusFromStatusT(
+ mAudioPolicyManager->removeDevicesRoleForStrategy(strategy, role));
+}
+
+Status AudioPolicyService::getDevicesForRoleAndStrategy(
+ int32_t strategyAidl,
+ media::DeviceRole roleAidl,
+ std::vector<media::AudioDevice>* _aidl_return) {
+ product_strategy_t strategy = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_product_strategy_t(strategyAidl));
+ device_role_t role = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_DeviceRole_device_role_t(roleAidl));
+ AudioDeviceTypeAddrVector devices;
+
if (mAudioPolicyManager == NULL) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
- return mAudioPolicyManager->removeDevicesRoleForStrategy(strategy, role);
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ mAudioPolicyManager->getDevicesForRoleAndStrategy(strategy, role, devices)));
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(
+ convertContainer<std::vector<media::AudioDevice>>(devices,
+ legacy2aidl_AudioDeviceTypeAddress));
+ return Status::ok();
}
-status_t AudioPolicyService::getDevicesForRoleAndStrategy(product_strategy_t strategy,
- device_role_t role,
- AudioDeviceTypeAddrVector &devices)
-{
- if (mAudioPolicyManager == NULL) {
- return NO_INIT;
- }
- Mutex::Autolock _l(mLock);
- return mAudioPolicyManager->getDevicesForRoleAndStrategy(strategy, role, devices);
+Status AudioPolicyService::registerSoundTriggerCaptureStateListener(
+ const sp<media::ICaptureStateListener>& listener, bool* _aidl_return) {
+ *_aidl_return = mCaptureStateNotifier.RegisterListener(listener);
+ return Status::ok();
}
-status_t AudioPolicyService::registerSoundTriggerCaptureStateListener(
- const sp<media::ICaptureStateListener>& listener,
- bool* result)
-{
- *result = mCaptureStateNotifier.RegisterListener(listener);
- return NO_ERROR;
-}
+Status AudioPolicyService::setDevicesRoleForCapturePreset(
+ media::AudioSourceType audioSourceAidl,
+ media::DeviceRole roleAidl,
+ const std::vector<media::AudioDevice>& devicesAidl) {
+ audio_source_t audioSource = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioSourceType_audio_source_t(audioSourceAidl));
+ device_role_t role = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_DeviceRole_device_role_t(roleAidl));
+ AudioDeviceTypeAddrVector devices = VALUE_OR_RETURN_BINDER_STATUS(
+ convertContainer<AudioDeviceTypeAddrVector>(devicesAidl,
+ aidl2legacy_AudioDeviceTypeAddress));
-status_t AudioPolicyService::setDevicesRoleForCapturePreset(
- audio_source_t audioSource, device_role_t role, const AudioDeviceTypeAddrVector &devices)
-{
if (mAudioPolicyManager == nullptr) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
- return mAudioPolicyManager->setDevicesRoleForCapturePreset(audioSource, role, devices);
+ return binderStatusFromStatusT(
+ mAudioPolicyManager->setDevicesRoleForCapturePreset(audioSource, role, devices));
}
-status_t AudioPolicyService::addDevicesRoleForCapturePreset(
- audio_source_t audioSource, device_role_t role, const AudioDeviceTypeAddrVector &devices)
-{
+Status AudioPolicyService::addDevicesRoleForCapturePreset(
+ media::AudioSourceType audioSourceAidl,
+ media::DeviceRole roleAidl,
+ const std::vector<media::AudioDevice>& devicesAidl) {
+ audio_source_t audioSource = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioSourceType_audio_source_t(audioSourceAidl));
+ device_role_t role = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_DeviceRole_device_role_t(roleAidl));
+ AudioDeviceTypeAddrVector devices = VALUE_OR_RETURN_BINDER_STATUS(
+ convertContainer<AudioDeviceTypeAddrVector>(devicesAidl,
+ aidl2legacy_AudioDeviceTypeAddress));
+
if (mAudioPolicyManager == nullptr) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
- return mAudioPolicyManager->addDevicesRoleForCapturePreset(audioSource, role, devices);
+ return binderStatusFromStatusT(
+ mAudioPolicyManager->addDevicesRoleForCapturePreset(audioSource, role, devices));
}
-status_t AudioPolicyService::removeDevicesRoleForCapturePreset(
- audio_source_t audioSource, device_role_t role, const AudioDeviceTypeAddrVector& devices)
-{
- if (mAudioPolicyManager == nullptr) {
- return NO_INIT;
+Status AudioPolicyService::removeDevicesRoleForCapturePreset(
+ media::AudioSourceType audioSourceAidl,
+ media::DeviceRole roleAidl,
+ const std::vector<media::AudioDevice>& devicesAidl) {
+ audio_source_t audioSource = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioSourceType_audio_source_t(audioSourceAidl));
+ device_role_t role = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_DeviceRole_device_role_t(roleAidl));
+ AudioDeviceTypeAddrVector devices = VALUE_OR_RETURN_BINDER_STATUS(
+ convertContainer<AudioDeviceTypeAddrVector>(devicesAidl,
+ aidl2legacy_AudioDeviceTypeAddress));
+
+ if (mAudioPolicyManager == nullptr) {
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
- return mAudioPolicyManager->removeDevicesRoleForCapturePreset(audioSource, role, devices);
+ return binderStatusFromStatusT(
+ mAudioPolicyManager->removeDevicesRoleForCapturePreset(audioSource, role, devices));
}
-status_t AudioPolicyService::clearDevicesRoleForCapturePreset(audio_source_t audioSource,
- device_role_t role)
-{
+Status AudioPolicyService::clearDevicesRoleForCapturePreset(media::AudioSourceType audioSourceAidl,
+ media::DeviceRole roleAidl) {
+ audio_source_t audioSource = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioSourceType_audio_source_t(audioSourceAidl));
+ device_role_t role = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_DeviceRole_device_role_t(roleAidl));
+
if (mAudioPolicyManager == nullptr) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
- return mAudioPolicyManager->clearDevicesRoleForCapturePreset(audioSource, role);
+ return binderStatusFromStatusT(
+ mAudioPolicyManager->clearDevicesRoleForCapturePreset(audioSource, role));
}
-status_t AudioPolicyService::getDevicesForRoleAndCapturePreset(audio_source_t audioSource,
- device_role_t role,
- AudioDeviceTypeAddrVector &devices)
-{
+Status AudioPolicyService::getDevicesForRoleAndCapturePreset(
+ media::AudioSourceType audioSourceAidl,
+ media::DeviceRole roleAidl,
+ std::vector<media::AudioDevice>* _aidl_return) {
+ audio_source_t audioSource = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioSourceType_audio_source_t(audioSourceAidl));
+ device_role_t role = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_DeviceRole_device_role_t(roleAidl));
+ AudioDeviceTypeAddrVector devices;
+
if (mAudioPolicyManager == nullptr) {
- return NO_INIT;
+ return binderStatusFromStatusT(NO_INIT);
}
Mutex::Autolock _l(mLock);
- return mAudioPolicyManager->getDevicesForRoleAndCapturePreset(audioSource, role, devices);
+ RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(
+ mAudioPolicyManager->getDevicesForRoleAndCapturePreset(audioSource, role, devices)));
+ *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(
+ convertContainer<std::vector<media::AudioDevice>>(devices,
+ legacy2aidl_AudioDeviceTypeAddress));
+ return Status::ok();
}
} // namespace android
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index a6e8989..fb38e3d 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -23,6 +23,7 @@
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#include <sys/time.h>
+#include <dlfcn.h>
#include <audio_utils/clock.h>
#include <binder/IServiceManager.h>
@@ -35,18 +36,23 @@
#include <utils/threads.h>
#include "AudioPolicyService.h"
#include <hardware_legacy/power.h>
+#include <media/AidlConversion.h>
#include <media/AudioEffect.h>
#include <media/AudioParameter.h>
#include <mediautils/ServiceUtilities.h>
+#include <mediautils/TimeCheck.h>
#include <sensorprivacy/SensorPrivacyManager.h>
#include <system/audio.h>
#include <system/audio_policy.h>
+#include <AudioPolicyManager.h>
namespace android {
+using binder::Status;
static const char kDeadlockedString[] = "AudioPolicyService may be deadlocked\n";
static const char kCmdDeadlockedString[] = "AudioPolicyService command thread may be deadlocked\n";
+static const char kAudioPolicyManagerCustomPath[] = "libaudiopolicymanagercustom.so";
static const int kDumpLockTimeoutNs = 1 * NANOS_PER_SECOND;
@@ -56,12 +62,54 @@
// ----------------------------------------------------------------------------
+static AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface)
+{
+ AudioPolicyManager *apm = new AudioPolicyManager(clientInterface);
+ status_t status = apm->initialize();
+ if (status != NO_ERROR) {
+ delete apm;
+ apm = nullptr;
+ }
+ return apm;
+}
+
+static void destroyAudioPolicyManager(AudioPolicyInterface *interface)
+{
+ delete interface;
+}
+// ----------------------------------------------------------------------------
+
AudioPolicyService::AudioPolicyService()
: BnAudioPolicyService(),
mAudioPolicyManager(NULL),
mAudioPolicyClient(NULL),
mPhoneState(AUDIO_MODE_INVALID),
- mCaptureStateNotifier(false) {
+ mCaptureStateNotifier(false),
+ mCreateAudioPolicyManager(createAudioPolicyManager),
+ mDestroyAudioPolicyManager(destroyAudioPolicyManager) {
+}
+
+void AudioPolicyService::loadAudioPolicyManager()
+{
+ mLibraryHandle = dlopen(kAudioPolicyManagerCustomPath, RTLD_NOW);
+ if (mLibraryHandle != nullptr) {
+ ALOGI("%s loading %s", __func__, kAudioPolicyManagerCustomPath);
+ mCreateAudioPolicyManager = reinterpret_cast<CreateAudioPolicyManagerInstance>
+ (dlsym(mLibraryHandle, "createAudioPolicyManager"));
+ const char *lastError = dlerror();
+ ALOGW_IF(mCreateAudioPolicyManager == nullptr, "%s createAudioPolicyManager is null %s",
+ __func__, lastError != nullptr ? lastError : "no error");
+
+ mDestroyAudioPolicyManager = reinterpret_cast<DestroyAudioPolicyManagerInstance>(
+ dlsym(mLibraryHandle, "destroyAudioPolicyManager"));
+ lastError = dlerror();
+ ALOGW_IF(mDestroyAudioPolicyManager == nullptr, "%s destroyAudioPolicyManager is null %s",
+ __func__, lastError != nullptr ? lastError : "no error");
+ if (mCreateAudioPolicyManager == nullptr || mDestroyAudioPolicyManager == nullptr){
+ unloadAudioPolicyManager();
+ LOG_ALWAYS_FATAL("could not find audiopolicymanager interface methods");
+ }
+ }
}
void AudioPolicyService::onFirstRef()
@@ -75,7 +123,9 @@
mOutputCommandThread = new AudioCommandThread(String8("ApmOutput"), this);
mAudioPolicyClient = new AudioPolicyClient(this);
- mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient);
+
+ loadAudioPolicyManager();
+ mAudioPolicyManager = mCreateAudioPolicyManager(mAudioPolicyClient);
}
// load audio processing modules
sp<AudioPolicyEffects> audioPolicyEffects = new AudioPolicyEffects();
@@ -91,12 +141,25 @@
sensorPrivacyPolicy->registerSelf();
}
+void AudioPolicyService::unloadAudioPolicyManager()
+{
+ ALOGV("%s ", __func__);
+ if (mLibraryHandle != nullptr) {
+ dlclose(mLibraryHandle);
+ }
+ mLibraryHandle = nullptr;
+ mCreateAudioPolicyManager = nullptr;
+ mDestroyAudioPolicyManager = nullptr;
+}
+
AudioPolicyService::~AudioPolicyService()
{
mAudioCommandThread->exit();
mOutputCommandThread->exit();
- destroyAudioPolicyManager(mAudioPolicyManager);
+ mDestroyAudioPolicyManager(mAudioPolicyManager);
+ unloadAudioPolicyManager();
+
delete mAudioPolicyClient;
mNotificationClients.clear();
@@ -111,11 +174,11 @@
// A notification client is always registered by AudioSystem when the client process
// connects to AudioPolicyService.
-void AudioPolicyService::registerClient(const sp<IAudioPolicyServiceClient>& client)
+Status AudioPolicyService::registerClient(const sp<media::IAudioPolicyServiceClient>& client)
{
if (client == 0) {
ALOGW("%s got NULL client", __FUNCTION__);
- return;
+ return Status::ok();
}
Mutex::Autolock _l(mNotificationClientsLock);
@@ -135,9 +198,10 @@
sp<IBinder> binder = IInterface::asBinder(client);
binder->linkToDeath(notificationClient);
}
+ return Status::ok();
}
-void AudioPolicyService::setAudioPortCallbacksEnabled(bool enabled)
+Status AudioPolicyService::setAudioPortCallbacksEnabled(bool enabled)
{
Mutex::Autolock _l(mNotificationClientsLock);
@@ -146,12 +210,13 @@
int64_t token = ((int64_t)uid<<32) | pid;
if (mNotificationClients.indexOfKey(token) < 0) {
- return;
+ return Status::ok();
}
mNotificationClients.valueFor(token)->setAudioPortCallbacksEnabled(enabled);
+ return Status::ok();
}
-void AudioPolicyService::setAudioVolumeGroupCallbacksEnabled(bool enabled)
+Status AudioPolicyService::setAudioVolumeGroupCallbacksEnabled(bool enabled)
{
Mutex::Autolock _l(mNotificationClientsLock);
@@ -160,9 +225,10 @@
int64_t token = ((int64_t)uid<<32) | pid;
if (mNotificationClients.indexOfKey(token) < 0) {
- return;
+ return Status::ok();
}
mNotificationClients.valueFor(token)->setAudioVolumeGroupCallbacksEnabled(enabled);
+ return Status::ok();
}
// removeNotificationClient() is called when the client process dies.
@@ -274,6 +340,19 @@
}
}
+void AudioPolicyService::onRoutingUpdated()
+{
+ mOutputCommandThread->routingChangedCommand();
+}
+
+void AudioPolicyService::doOnRoutingUpdated()
+{
+ Mutex::Autolock _l(mNotificationClientsLock);
+ for (size_t i = 0; i < mNotificationClients.size(); i++) {
+ mNotificationClients.valueAt(i)->onRoutingUpdated();
+ }
+}
+
status_t AudioPolicyService::clientCreateAudioPatch(const struct audio_patch *patch,
audio_patch_handle_t *handle,
int delayMs)
@@ -293,10 +372,11 @@
return mAudioCommandThread->setAudioPortConfigCommand(config, delayMs);
}
-AudioPolicyService::NotificationClient::NotificationClient(const sp<AudioPolicyService>& service,
- const sp<IAudioPolicyServiceClient>& client,
- uid_t uid,
- pid_t pid)
+AudioPolicyService::NotificationClient::NotificationClient(
+ const sp<AudioPolicyService>& service,
+ const sp<media::IAudioPolicyServiceClient>& client,
+ uid_t uid,
+ pid_t pid)
: mService(service), mUid(uid), mPid(pid), mAudioPolicyServiceClient(client),
mAudioPortCallbacksEnabled(false), mAudioVolumeGroupCallbacksEnabled(false)
{
@@ -342,7 +422,8 @@
const String8& regId, int32_t state)
{
if (mAudioPolicyServiceClient != 0 && isServiceUid(mUid)) {
- mAudioPolicyServiceClient->onDynamicPolicyMixStateUpdate(regId, state);
+ mAudioPolicyServiceClient->onDynamicPolicyMixStateUpdate(
+ legacy2aidl_String8_string(regId).value(), state);
}
}
@@ -357,8 +438,37 @@
audio_source_t source)
{
if (mAudioPolicyServiceClient != 0 && isServiceUid(mUid)) {
- mAudioPolicyServiceClient->onRecordingConfigurationUpdate(event, clientInfo,
- clientConfig, clientEffects, deviceConfig, effects, patchHandle, source);
+ status_t status = [&]() -> status_t {
+ int32_t eventAidl = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(event));
+ media::RecordClientInfo clientInfoAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_record_client_info_t_RecordClientInfo(*clientInfo));
+ media::AudioConfigBase clientConfigAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_config_base_t_AudioConfigBase(*clientConfig));
+ std::vector<media::EffectDescriptor> clientEffectsAidl = VALUE_OR_RETURN_STATUS(
+ convertContainer<std::vector<media::EffectDescriptor>>(
+ clientEffects,
+ legacy2aidl_effect_descriptor_t_EffectDescriptor));
+ media::AudioConfigBase deviceConfigAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_config_base_t_AudioConfigBase(*deviceConfig));
+ std::vector<media::EffectDescriptor> effectsAidl = VALUE_OR_RETURN_STATUS(
+ convertContainer<std::vector<media::EffectDescriptor>>(
+ effects,
+ legacy2aidl_effect_descriptor_t_EffectDescriptor));
+ int32_t patchHandleAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_patch_handle_t_int32_t(patchHandle));
+ media::AudioSourceType sourceAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_source_t_AudioSourceType(source));
+ return aidl_utils::statusTFromBinderStatus(
+ mAudioPolicyServiceClient->onRecordingConfigurationUpdate(eventAidl,
+ clientInfoAidl,
+ clientConfigAidl,
+ clientEffectsAidl,
+ deviceConfigAidl,
+ effectsAidl,
+ patchHandleAidl,
+ sourceAidl));
+ }();
+ ALOGW_IF(status != OK, "onRecordingConfigurationUpdate() failed: %d", status);
}
}
@@ -372,6 +482,13 @@
mAudioVolumeGroupCallbacksEnabled = enabled;
}
+void AudioPolicyService::NotificationClient::onRoutingUpdated()
+{
+ if (mAudioPolicyServiceClient != 0 && isServiceUid(mUid)) {
+ mAudioPolicyServiceClient->onRoutingUpdated();
+ }
+}
+
void AudioPolicyService::binderDied(const wp<IBinder>& who) {
ALOGW("binderDied() %p, calling pid %d", who.unsafe_get(),
IPCThreadState::self()->getCallingPid());
@@ -453,7 +570,7 @@
sp<AudioRecordClient> topActive;
sp<AudioRecordClient> latestActive;
sp<AudioRecordClient> topSensitiveActive;
- sp<AudioRecordClient> latestSensitiveActive;
+ sp<AudioRecordClient> latestSensitiveActiveOrComm;
nsecs_t topStartNs = 0;
nsecs_t latestStartNs = 0;
@@ -467,6 +584,7 @@
bool rttCallActive = (isInCall || isInCommunication)
&& mUidPolicy->isRttEnabled();
bool onlyHotwordActive = true;
+ bool isPhoneStateOwnerActive = false;
// if Sensor Privacy is enabled then all recordings should be silenced.
if (mSensorPrivacyPolicy->isSensorPrivacyEnabled()) {
@@ -476,24 +594,26 @@
for (size_t i =0; i < mAudioRecordClients.size(); i++) {
sp<AudioRecordClient> current = mAudioRecordClients[i];
+ uid_t currentUid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(current->identity.uid));
if (!current->active) {
continue;
}
- app_state_t appState = apmStatFromAmState(mUidPolicy->getUidState(current->uid));
+ app_state_t appState = apmStatFromAmState(mUidPolicy->getUidState(currentUid));
// clients which app is in IDLE state are not eligible for top active or
// latest active
if (appState == APP_STATE_IDLE) {
continue;
}
- bool isAccessibility = mUidPolicy->isA11yUid(current->uid);
+ bool isAccessibility = mUidPolicy->isA11yUid(currentUid);
// Clients capturing for Accessibility services or virtual sources are not considered
// for top or latest active to avoid masking regular clients started before
if (!isAccessibility && !isVirtualSource(current->attributes.source)) {
- bool isAssistant = mUidPolicy->isAssistantUid(current->uid);
+ bool isAssistant = mUidPolicy->isAssistantUid(currentUid);
bool isPrivacySensitive =
(current->attributes.flags & AUDIO_FLAG_CAPTURE_PRIVATE) != 0;
+
if (appState == APP_STATE_TOP) {
if (isPrivacySensitive) {
if (current->startTimeNs > topSensitiveStartNs) {
@@ -515,9 +635,17 @@
if (!(current->attributes.source == AUDIO_SOURCE_HOTWORD
|| ((isA11yOnTop || rttCallActive) && isAssistant))) {
if (isPrivacySensitive) {
- if (current->startTimeNs > latestSensitiveStartNs) {
- latestSensitiveActive = current;
- latestSensitiveStartNs = current->startTimeNs;
+ // if audio mode is IN_COMMUNICATION, make sure the audio mode owner
+ // is marked latest sensitive active even if another app qualifies.
+ if (current->startTimeNs > latestSensitiveStartNs
+ || (isInCommunication && currentUid == mPhoneStateOwnerUid)) {
+ if (!isInCommunication || latestSensitiveActiveOrComm == nullptr
+ || VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(
+ latestSensitiveActiveOrComm->identity.uid))
+ != mPhoneStateOwnerUid) {
+ latestSensitiveActiveOrComm = current;
+ latestSensitiveStartNs = current->startTimeNs;
+ }
}
isSensitiveActive = true;
} else {
@@ -531,6 +659,9 @@
if (current->attributes.source != AUDIO_SOURCE_HOTWORD) {
onlyHotwordActive = false;
}
+ if (currentUid == mPhoneStateOwnerUid) {
+ isPhoneStateOwnerActive = true;
+ }
}
// if no active client with UI on Top, consider latest active as top
@@ -539,8 +670,17 @@
topStartNs = latestStartNs;
}
if (topSensitiveActive == nullptr) {
- topSensitiveActive = latestSensitiveActive;
+ topSensitiveActive = latestSensitiveActiveOrComm;
topSensitiveStartNs = latestSensitiveStartNs;
+ } else if (latestSensitiveActiveOrComm != nullptr) {
+ // if audio mode is IN_COMMUNICATION, favor audio mode owner over an app with
+ // foreground UI in case both are capturing with privacy sensitive flag.
+ uid_t latestActiveUid = VALUE_OR_FATAL(
+ aidl2legacy_int32_t_uid_t(latestSensitiveActiveOrComm->identity.uid));
+ if (isInCommunication && latestActiveUid == mPhoneStateOwnerUid) {
+ topSensitiveActive = latestSensitiveActiveOrComm;
+ topSensitiveStartNs = latestSensitiveStartNs;
+ }
}
// If both privacy sensitive and regular capture are active:
@@ -555,24 +695,27 @@
for (size_t i =0; i < mAudioRecordClients.size(); i++) {
sp<AudioRecordClient> current = mAudioRecordClients[i];
+ uid_t currentUid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(
+ current->identity.uid));
if (!current->active) {
continue;
}
audio_source_t source = current->attributes.source;
- bool isTopOrLatestActive = topActive == nullptr ? false : current->uid == topActive->uid;
- bool isTopOrLatestSensitive = topSensitiveActive == nullptr ?
- false : current->uid == topSensitiveActive->uid;
+ bool isTopOrLatestActive = topActive == nullptr ? false :
+ current->identity.uid == topActive->identity.uid;
+ bool isTopOrLatestSensitive = topSensitiveActive == nullptr ? false :
+ current->identity.uid == topSensitiveActive->identity.uid;
auto canCaptureIfInCallOrCommunication = [&](const auto &recordClient) REQUIRES(mLock) {
+ uid_t recordUid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(
+ recordClient->identity.uid));
bool canCaptureCall = recordClient->canCaptureOutput;
- return !(isInCall && !canCaptureCall);
-//TODO(b/160260850): restore restriction to mode owner once fix for misbehaving apps is merged
-// bool canCaptureCommunication = recordClient->canCaptureOutput
-// || recordClient->uid == mPhoneStateOwnerUid
-// || isServiceUid(mPhoneStateOwnerUid);
-// return !(isInCall && !canCaptureCall)
-// && !(isInCommunication && !canCaptureCommunication);
+ bool canCaptureCommunication = recordClient->canCaptureOutput
+ || !isPhoneStateOwnerActive
+ || recordUid == mPhoneStateOwnerUid;
+ return !(isInCall && !canCaptureCall)
+ && !(isInCommunication && !canCaptureCommunication);
};
// By default allow capture if:
@@ -589,7 +732,7 @@
if (isVirtualSource(source)) {
// Allow capture for virtual (remote submix, call audio TX or RX...) sources
allowCapture = true;
- } else if (mUidPolicy->isAssistantUid(current->uid)) {
+ } else if (mUidPolicy->isAssistantUid(currentUid)) {
// For assistant allow capture if:
// An accessibility service is on TOP or a RTT call is active
// AND the source is VOICE_RECOGNITION or HOTWORD
@@ -609,7 +752,7 @@
allowCapture = true;
}
}
- } else if (mUidPolicy->isA11yUid(current->uid)) {
+ } else if (mUidPolicy->isA11yUid(currentUid)) {
// For accessibility service allow capture if:
// The assistant is not on TOP
// AND there is no active privacy sensitive capture or call
@@ -635,7 +778,7 @@
&& canCaptureIfInCallOrCommunication(current)) {
allowCapture = true;
}
- } else if (mUidPolicy->isCurrentImeUid(current->uid)) {
+ } else if (mUidPolicy->isCurrentImeUid(currentUid)) {
// For current InputMethodService allow capture if:
// A RTT call is active AND the source is VOICE_RECOGNITION
if (rttCallActive && source == AUDIO_SOURCE_VOICE_RECOGNITION) {
@@ -643,7 +786,7 @@
}
}
setAppState_l(current->portId,
- allowCapture ? apmStatFromAmState(mUidPolicy->getUidState(current->uid)) :
+ allowCapture ? apmStatFromAmState(mUidPolicy->getUidState(currentUid)) :
APP_STATE_IDLE);
}
}
@@ -743,6 +886,93 @@
status_t AudioPolicyService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+ // make sure transactions reserved to AudioFlinger do not come from other processes
+ switch (code) {
+ case TRANSACTION_startOutput:
+ case TRANSACTION_stopOutput:
+ case TRANSACTION_releaseOutput:
+ case TRANSACTION_getInputForAttr:
+ case TRANSACTION_startInput:
+ case TRANSACTION_stopInput:
+ case TRANSACTION_releaseInput:
+ case TRANSACTION_getOutputForEffect:
+ case TRANSACTION_registerEffect:
+ case TRANSACTION_unregisterEffect:
+ case TRANSACTION_setEffectEnabled:
+ case TRANSACTION_getStrategyForStream:
+ case TRANSACTION_getOutputForAttr:
+ case TRANSACTION_moveEffectsToIo:
+ ALOGW("%s: transaction %d received from PID %d",
+ __func__, code, IPCThreadState::self()->getCallingPid());
+ return INVALID_OPERATION;
+ default:
+ break;
+ }
+
+ // make sure the following transactions come from system components
+ switch (code) {
+ case TRANSACTION_setDeviceConnectionState:
+ case TRANSACTION_handleDeviceConfigChange:
+ case TRANSACTION_setPhoneState:
+//FIXME: Allow setForceUse calls from system apps until a better use case routing API is available
+// case TRANSACTION_setForceUse:
+ case TRANSACTION_initStreamVolume:
+ case TRANSACTION_setStreamVolumeIndex:
+ case TRANSACTION_setVolumeIndexForAttributes:
+ case TRANSACTION_getStreamVolumeIndex:
+ case TRANSACTION_getVolumeIndexForAttributes:
+ case TRANSACTION_getMinVolumeIndexForAttributes:
+ case TRANSACTION_getMaxVolumeIndexForAttributes:
+ case TRANSACTION_isStreamActive:
+ case TRANSACTION_isStreamActiveRemotely:
+ case TRANSACTION_isSourceActive:
+ case TRANSACTION_getDevicesForStream:
+ case TRANSACTION_registerPolicyMixes:
+ case TRANSACTION_setMasterMono:
+ case TRANSACTION_getSurroundFormats:
+ case TRANSACTION_getReportedSurroundFormats:
+ case TRANSACTION_setSurroundFormatEnabled:
+ case TRANSACTION_setAssistantUid:
+ case TRANSACTION_setA11yServicesUids:
+ case TRANSACTION_setUidDeviceAffinities:
+ case TRANSACTION_removeUidDeviceAffinities:
+ case TRANSACTION_setUserIdDeviceAffinities:
+ case TRANSACTION_removeUserIdDeviceAffinities:
+ case TRANSACTION_getHwOffloadEncodingFormatsSupportedForA2DP:
+ case TRANSACTION_listAudioVolumeGroups:
+ case TRANSACTION_getVolumeGroupFromAudioAttributes:
+ case TRANSACTION_acquireSoundTriggerSession:
+ case TRANSACTION_releaseSoundTriggerSession:
+ case TRANSACTION_setRttEnabled:
+ case TRANSACTION_isCallScreenModeSupported:
+ case TRANSACTION_setDevicesRoleForStrategy:
+ case TRANSACTION_setSupportedSystemUsages:
+ case TRANSACTION_removeDevicesRoleForStrategy:
+ case TRANSACTION_getDevicesForRoleAndStrategy:
+ case TRANSACTION_getDevicesForAttributes:
+ case TRANSACTION_setAllowedCapturePolicy:
+ case TRANSACTION_onNewAudioModulesAvailable:
+ case TRANSACTION_setCurrentImeUid:
+ case TRANSACTION_registerSoundTriggerCaptureStateListener:
+ case TRANSACTION_setDevicesRoleForCapturePreset:
+ case TRANSACTION_addDevicesRoleForCapturePreset:
+ case TRANSACTION_removeDevicesRoleForCapturePreset:
+ case TRANSACTION_clearDevicesRoleForCapturePreset:
+ case TRANSACTION_getDevicesForRoleAndCapturePreset: {
+ if (!isServiceUid(IPCThreadState::self()->getCallingUid())) {
+ ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
+ __func__, code, IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid());
+ return INVALID_OPERATION;
+ }
+ } break;
+ default:
+ break;
+ }
+
+ std::string tag("IAudioPolicyService command " + std::to_string(code));
+ TimeCheck check(tag.c_str());
+
switch (code) {
case SHELL_COMMAND_TRANSACTION: {
int in = data.readFileDescriptor();
@@ -789,6 +1019,9 @@
return handleResetUidState(args, err);
} else if (args.size() >= 2 && args[0] == String16("get-uid-state")) {
return handleGetUidState(args, out, err);
+ } else if (args.size() >= 1 && args[0] == String16("purge_permission-cache")) {
+ purgePermissionCache();
+ return NO_ERROR;
} else if (args.size() == 1 && args[0] == String16("help")) {
printHelp(out);
return NO_ERROR;
@@ -1143,6 +1376,14 @@
spm.addSensorPrivacyListener(this);
}
+void AudioPolicyService::SensorPrivacyPolicy::registerSelfForMicrophoneOnly(int userId) {
+ SensorPrivacyManager spm;
+ mSensorPrivacyEnabled = spm.isIndividualSensorPrivacyEnabled(userId,
+ SensorPrivacyManager::INDIVIDUAL_SENSOR_MICROPHONE);
+ spm.addIndividualSensorPrivacyListener(userId,
+ SensorPrivacyManager::INDIVIDUAL_SENSOR_MICROPHONE, this);
+}
+
void AudioPolicyService::SensorPrivacyPolicy::unregisterSelf() {
SensorPrivacyManager spm;
spm.removeSensorPrivacyListener(this);
@@ -1366,6 +1607,16 @@
svc->doOnNewAudioModulesAvailable();
mLock.lock();
} break;
+ case ROUTING_UPDATED: {
+ ALOGV("AudioCommandThread() processing routing update");
+ svc = mService.promote();
+ if (svc == 0) {
+ break;
+ }
+ mLock.unlock();
+ svc->doOnRoutingUpdated();
+ mLock.lock();
+ } break;
default:
ALOGW("AudioCommandThread() unknown command %d", command->mCommand);
@@ -1662,6 +1913,14 @@
sendCommand(command);
}
+void AudioPolicyService::AudioCommandThread::routingChangedCommand()
+{
+ sp<AudioCommand>command = new AudioCommand();
+ command->mCommand = ROUTING_UPDATED;
+ ALOGV("AudioCommandThread() adding routing update");
+ sendCommand(command);
+}
+
status_t AudioPolicyService::AudioCommandThread::sendCommand(sp<AudioCommand>& command, int delayMs)
{
{
@@ -1825,6 +2084,10 @@
} break;
+ case ROUTING_UPDATED: {
+
+ } break;
+
default:
break;
}
@@ -1909,9 +2172,10 @@
mAudioCommandThread->setEffectSuspendedCommand(effectId, sessionId, suspended);
}
-void AudioPolicyService::onNewAudioModulesAvailable()
+Status AudioPolicyService::onNewAudioModulesAvailable()
{
mOutputCommandThread->audioModulesUpdateCommand();
+ return Status::ok();
}
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 0b218c2..00d9670 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_AUDIOPOLICYSERVICE_H
#define ANDROID_AUDIOPOLICYSERVICE_H
+#include <android/media/BnAudioPolicyService.h>
#include <android-base/thread_annotations.h>
#include <cutils/misc.h>
#include <cutils/config_utils.h>
@@ -29,7 +30,6 @@
#include <binder/IUidObserver.h>
#include <system/audio.h>
#include <system/audio_policy.h>
-#include <media/IAudioPolicyService.h>
#include <media/ToneGenerator.h>
#include <media/AudioEffect.h>
#include <media/AudioPolicy.h>
@@ -38,6 +38,7 @@
#include "CaptureStateNotifier.h"
#include <AudioPolicyInterface.h>
#include <android/hardware/BnSensorPrivacyListener.h>
+#include <android/media/permission/Identity.h>
#include <unordered_map>
@@ -47,7 +48,7 @@
class AudioPolicyService :
public BinderService<AudioPolicyService>,
- public BnAudioPolicyService,
+ public media::BnAudioPolicyService,
public IBinder::DeathRecipient
{
friend class BinderService<AudioPolicyService>;
@@ -61,108 +62,181 @@
//
// BnAudioPolicyService (see AudioPolicyInterface for method descriptions)
//
-
- void onNewAudioModulesAvailable() override;
- virtual status_t setDeviceConnectionState(audio_devices_t device,
- audio_policy_dev_state_t state,
- const char *device_address,
- const char *device_name,
- audio_format_t encodedFormat);
- virtual audio_policy_dev_state_t getDeviceConnectionState(
- audio_devices_t device,
- const char *device_address);
- virtual status_t handleDeviceConfigChange(audio_devices_t device,
- const char *device_address,
- const char *device_name,
- audio_format_t encodedFormat);
- virtual status_t setPhoneState(audio_mode_t state, uid_t uid);
- virtual status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config);
- virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage);
- virtual audio_io_handle_t getOutput(audio_stream_type_t stream);
- status_t getOutputForAttr(audio_attributes_t *attr,
- audio_io_handle_t *output,
- audio_session_t session,
- audio_stream_type_t *stream,
- pid_t pid,
- uid_t uid,
- const audio_config_t *config,
- audio_output_flags_t flags,
- audio_port_handle_t *selectedDeviceId,
- audio_port_handle_t *portId,
- std::vector<audio_io_handle_t> *secondaryOutputs) 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);
- virtual status_t getInputForAttr(const audio_attributes_t *attr,
- audio_io_handle_t *input,
- audio_unique_id_t riid,
- audio_session_t session,
- pid_t pid,
- uid_t uid,
- const String16& opPackageName,
- const audio_config_base_t *config,
- audio_input_flags_t flags,
- audio_port_handle_t *selectedDeviceId = NULL,
- audio_port_handle_t *portId = NULL);
- virtual status_t startInput(audio_port_handle_t portId);
- virtual status_t stopInput(audio_port_handle_t portId);
- virtual void releaseInput(audio_port_handle_t portId);
- virtual status_t initStreamVolume(audio_stream_type_t stream,
- int indexMin,
- int indexMax);
- virtual status_t setStreamVolumeIndex(audio_stream_type_t stream,
- int index,
- audio_devices_t device);
- virtual status_t getStreamVolumeIndex(audio_stream_type_t stream,
- int *index,
- audio_devices_t device);
-
- virtual status_t setVolumeIndexForAttributes(const audio_attributes_t &attr,
- int index,
- audio_devices_t device);
- virtual status_t getVolumeIndexForAttributes(const audio_attributes_t &attr,
- int &index,
- audio_devices_t device);
- virtual status_t getMinVolumeIndexForAttributes(const audio_attributes_t &attr,
- int &index);
- virtual status_t getMaxVolumeIndexForAttributes(const audio_attributes_t &attr,
- int &index);
-
- 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,
- audio_io_handle_t io,
- uint32_t strategy,
- audio_session_t session,
- int id);
- virtual status_t unregisterEffect(int id);
- virtual status_t setEffectEnabled(int id, bool enabled);
- status_t moveEffectsToIo(const std::vector<int>& ids, audio_io_handle_t io) override;
- virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const;
- virtual bool isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs = 0) const;
- virtual bool isSourceActive(audio_source_t source) const;
-
- virtual status_t queryDefaultPreProcessing(audio_session_t audioSession,
- effect_descriptor_t *descriptors,
- uint32_t *count);
- virtual status_t addSourceDefaultEffect(const effect_uuid_t *type,
- const String16& opPackageName,
- const effect_uuid_t *uuid,
- int32_t priority,
- audio_source_t source,
- audio_unique_id_t* id);
- virtual status_t addStreamDefaultEffect(const effect_uuid_t *type,
- const String16& opPackageName,
- const effect_uuid_t *uuid,
- int32_t priority,
- audio_usage_t usage,
- audio_unique_id_t* id);
- virtual status_t removeSourceDefaultEffect(audio_unique_id_t id);
- virtual status_t removeStreamDefaultEffect(audio_unique_id_t id);
+ binder::Status onNewAudioModulesAvailable() override;
+ binder::Status setDeviceConnectionState(
+ const media::AudioDevice& device,
+ media::AudioPolicyDeviceState state,
+ const std::string& deviceName,
+ media::audio::common::AudioFormat encodedFormat) override;
+ binder::Status getDeviceConnectionState(const media::AudioDevice& device,
+ media::AudioPolicyDeviceState* _aidl_return) override;
+ binder::Status handleDeviceConfigChange(
+ const media::AudioDevice& device,
+ const std::string& deviceName,
+ media::audio::common::AudioFormat encodedFormat) override;
+ binder::Status setPhoneState(media::AudioMode state, int32_t uid) override;
+ binder::Status setForceUse(media::AudioPolicyForceUse usage,
+ media::AudioPolicyForcedConfig config) override;
+ binder::Status getForceUse(media::AudioPolicyForceUse usage,
+ media::AudioPolicyForcedConfig* _aidl_return) override;
+ binder::Status getOutput(media::AudioStreamType stream, int32_t* _aidl_return) override;
+ binder::Status getOutputForAttr(const media::AudioAttributesInternal& attr, int32_t session,
+ const media::permission::Identity &identity,
+ const media::AudioConfig& config,
+ int32_t flags, int32_t selectedDeviceId,
+ media::GetOutputForAttrResponse* _aidl_return) override;
+ binder::Status startOutput(int32_t portId) override;
+ binder::Status stopOutput(int32_t portId) override;
+ binder::Status releaseOutput(int32_t portId) override;
+ binder::Status getInputForAttr(const media::AudioAttributesInternal& attr, int32_t input,
+ int32_t riid, int32_t session,
+ const media::permission::Identity &identity,
+ const media::AudioConfigBase& config, int32_t flags,
+ int32_t selectedDeviceId,
+ media::GetInputForAttrResponse* _aidl_return) override;
+ binder::Status startInput(int32_t portId) override;
+ binder::Status stopInput(int32_t portId) override;
+ binder::Status releaseInput(int32_t portId) override;
+ binder::Status initStreamVolume(media::AudioStreamType stream, int32_t indexMin,
+ int32_t indexMax) override;
+ binder::Status setStreamVolumeIndex(media::AudioStreamType stream, int32_t device,
+ int32_t index) override;
+ binder::Status getStreamVolumeIndex(media::AudioStreamType stream, int32_t device,
+ int32_t* _aidl_return) override;
+ binder::Status setVolumeIndexForAttributes(const media::AudioAttributesInternal& attr,
+ int32_t device, int32_t index) override;
+ binder::Status getVolumeIndexForAttributes(const media::AudioAttributesInternal& attr,
+ int32_t device, int32_t* _aidl_return) override;
+ binder::Status getMaxVolumeIndexForAttributes(const media::AudioAttributesInternal& attr,
+ int32_t* _aidl_return) override;
+ binder::Status getMinVolumeIndexForAttributes(const media::AudioAttributesInternal& attr,
+ int32_t* _aidl_return) override;
+ binder::Status getStrategyForStream(media::AudioStreamType stream,
+ int32_t* _aidl_return) override;
+ binder::Status getDevicesForStream(media::AudioStreamType stream,
+ int32_t* _aidl_return) override;
+ binder::Status getDevicesForAttributes(const media::AudioAttributesEx& attr,
+ std::vector<media::AudioDevice>* _aidl_return) override;
+ binder::Status getOutputForEffect(const media::EffectDescriptor& desc,
+ int32_t* _aidl_return) override;
+ binder::Status registerEffect(const media::EffectDescriptor& desc, int32_t io, int32_t strategy,
+ int32_t session, int32_t id) override;
+ binder::Status unregisterEffect(int32_t id) override;
+ binder::Status setEffectEnabled(int32_t id, bool enabled) override;
+ binder::Status moveEffectsToIo(const std::vector<int32_t>& ids, int32_t io) override;
+ binder::Status isStreamActive(media::AudioStreamType stream, int32_t inPastMs,
+ bool* _aidl_return) override;
+ binder::Status isStreamActiveRemotely(media::AudioStreamType stream, int32_t inPastMs,
+ bool* _aidl_return) override;
+ binder::Status isSourceActive(media::AudioSourceType source, bool* _aidl_return) override;
+ binder::Status queryDefaultPreProcessing(
+ int32_t audioSession, media::Int* count,
+ std::vector<media::EffectDescriptor>* _aidl_return) override;
+ binder::Status addSourceDefaultEffect(const media::AudioUuid& type,
+ const std::string& opPackageName,
+ const media::AudioUuid& uuid, int32_t priority,
+ media::AudioSourceType source,
+ int32_t* _aidl_return) override;
+ binder::Status addStreamDefaultEffect(const media::AudioUuid& type,
+ const std::string& opPackageName,
+ const media::AudioUuid& uuid, int32_t priority,
+ media::AudioUsage usage, int32_t* _aidl_return) override;
+ binder::Status removeSourceDefaultEffect(int32_t id) override;
+ binder::Status removeStreamDefaultEffect(int32_t id) override;
+ binder::Status setSupportedSystemUsages(
+ const std::vector<media::AudioUsage>& systemUsages) override;
+ binder::Status setAllowedCapturePolicy(int32_t uid, int32_t capturePolicy) override;
+ binder::Status getOffloadSupport(const media::AudioOffloadInfo& info,
+ media::AudioOffloadMode* _aidl_return) override;
+ binder::Status isDirectOutputSupported(const media::AudioConfigBase& config,
+ const media::AudioAttributesInternal& attributes,
+ bool* _aidl_return) override;
+ binder::Status listAudioPorts(media::AudioPortRole role, media::AudioPortType type,
+ media::Int* count, std::vector<media::AudioPort>* ports,
+ int32_t* _aidl_return) override;
+ binder::Status getAudioPort(const media::AudioPort& port,
+ media::AudioPort* _aidl_return) override;
+ binder::Status createAudioPatch(const media::AudioPatch& patch, int32_t handle,
+ int32_t* _aidl_return) override;
+ binder::Status releaseAudioPatch(int32_t handle) override;
+ binder::Status listAudioPatches(media::Int* count, std::vector<media::AudioPatch>* patches,
+ int32_t* _aidl_return) override;
+ binder::Status setAudioPortConfig(const media::AudioPortConfig& config) override;
+ binder::Status registerClient(const sp<media::IAudioPolicyServiceClient>& client) override;
+ binder::Status setAudioPortCallbacksEnabled(bool enabled) override;
+ binder::Status setAudioVolumeGroupCallbacksEnabled(bool enabled) override;
+ binder::Status acquireSoundTriggerSession(media::SoundTriggerSession* _aidl_return) override;
+ binder::Status releaseSoundTriggerSession(int32_t session) override;
+ binder::Status getPhoneState(media::AudioMode* _aidl_return) override;
+ binder::Status registerPolicyMixes(const std::vector<media::AudioMix>& mixes,
+ bool registration) override;
+ binder::Status setUidDeviceAffinities(int32_t uid,
+ const std::vector<media::AudioDevice>& devices) override;
+ binder::Status removeUidDeviceAffinities(int32_t uid) override;
+ binder::Status setUserIdDeviceAffinities(
+ int32_t userId,
+ const std::vector<media::AudioDevice>& devices) override;
+ binder::Status removeUserIdDeviceAffinities(int32_t userId) override;
+ binder::Status startAudioSource(const media::AudioPortConfig& source,
+ const media::AudioAttributesInternal& attributes,
+ int32_t* _aidl_return) override;
+ binder::Status stopAudioSource(int32_t portId) override;
+ binder::Status setMasterMono(bool mono) override;
+ binder::Status getMasterMono(bool* _aidl_return) override;
+ binder::Status getStreamVolumeDB(media::AudioStreamType stream, int32_t index, int32_t device,
+ float* _aidl_return) override;
+ binder::Status getSurroundFormats(media::Int* count,
+ std::vector<media::audio::common::AudioFormat>* formats,
+ std::vector<bool>* formatsEnabled) override;
+ binder::Status getReportedSurroundFormats(
+ media::Int* count, std::vector<media::audio::common::AudioFormat>* formats) override;
+ binder::Status getHwOffloadEncodingFormatsSupportedForA2DP(
+ std::vector<media::audio::common::AudioFormat>* _aidl_return) override;
+ binder::Status setSurroundFormatEnabled(media::audio::common::AudioFormat audioFormat,
+ bool enabled) override;
+ binder::Status setAssistantUid(int32_t uid) override;
+ binder::Status setA11yServicesUids(const std::vector<int32_t>& uids) override;
+ binder::Status setCurrentImeUid(int32_t uid) override;
+ binder::Status isHapticPlaybackSupported(bool* _aidl_return) override;
+ binder::Status listAudioProductStrategies(
+ std::vector<media::AudioProductStrategy>* _aidl_return) override;
+ binder::Status getProductStrategyFromAudioAttributes(const media::AudioAttributesEx& aa,
+ bool fallbackOnDefault,
+ int32_t* _aidl_return) override;
+ binder::Status listAudioVolumeGroups(
+ std::vector<media::AudioVolumeGroup>* _aidl_return) override;
+ binder::Status getVolumeGroupFromAudioAttributes(const media::AudioAttributesEx& aa,
+ bool fallbackOnDefault,
+ int32_t* _aidl_return) override;
+ binder::Status setRttEnabled(bool enabled) override;
+ binder::Status isCallScreenModeSupported(bool* _aidl_return) override;
+ binder::Status setDevicesRoleForStrategy(
+ int32_t strategy, media::DeviceRole role,
+ const std::vector<media::AudioDevice>& devices) override;
+ binder::Status removeDevicesRoleForStrategy(int32_t strategy, media::DeviceRole role) override;
+ binder::Status getDevicesForRoleAndStrategy(
+ int32_t strategy, media::DeviceRole role,
+ std::vector<media::AudioDevice>* _aidl_return) override;
+ binder::Status setDevicesRoleForCapturePreset(
+ media::AudioSourceType audioSource,
+ media::DeviceRole role,
+ const std::vector<media::AudioDevice>& devices) override;
+ binder::Status addDevicesRoleForCapturePreset(
+ media::AudioSourceType audioSource,
+ media::DeviceRole role,
+ const std::vector<media::AudioDevice>& devices) override;
+ binder::Status removeDevicesRoleForCapturePreset(
+ media::AudioSourceType audioSource,
+ media::DeviceRole role,
+ const std::vector<media::AudioDevice>& devices) override;
+ binder::Status clearDevicesRoleForCapturePreset(media::AudioSourceType audioSource,
+ media::DeviceRole role) override;
+ binder::Status getDevicesForRoleAndCapturePreset(
+ media::AudioSourceType audioSource,
+ media::DeviceRole role,
+ std::vector<media::AudioDevice>* _aidl_return) override;
+ binder::Status registerSoundTriggerCaptureStateListener(
+ const sp<media::ICaptureStateListener>& listener, bool* _aidl_return) override;
virtual status_t onTransact(
uint32_t code,
@@ -190,164 +264,53 @@
audio_io_handle_t output,
int delayMs = 0);
virtual status_t setVoiceVolume(float volume, int delayMs = 0);
- status_t setSupportedSystemUsages(const std::vector<audio_usage_t>& systemUsages);
- status_t setAllowedCapturePolicy(uint_t uid, audio_flags_mask_t capturePolicy) override;
- virtual bool isOffloadSupported(const audio_offload_info_t &config);
- virtual bool isDirectOutputSupported(const audio_config_base_t& config,
- const audio_attributes_t& attributes);
- virtual status_t listAudioPorts(audio_port_role_t role,
- audio_port_type_t type,
- unsigned int *num_ports,
- struct audio_port *ports,
- unsigned int *generation);
- virtual status_t getAudioPort(struct audio_port *port);
- virtual status_t createAudioPatch(const struct audio_patch *patch,
- audio_patch_handle_t *handle);
- virtual status_t releaseAudioPatch(audio_patch_handle_t handle);
- virtual status_t listAudioPatches(unsigned int *num_patches,
- struct audio_patch *patches,
- unsigned int *generation);
- virtual status_t setAudioPortConfig(const struct audio_port_config *config);
+ void doOnNewAudioModulesAvailable();
+ status_t doStopOutput(audio_port_handle_t portId);
+ void doReleaseOutput(audio_port_handle_t portId);
- virtual void registerClient(const sp<IAudioPolicyServiceClient>& client);
+ status_t clientCreateAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle,
+ int delayMs);
+ status_t clientReleaseAudioPatch(audio_patch_handle_t handle,
+ int delayMs);
+ virtual status_t clientSetAudioPortConfig(const struct audio_port_config *config,
+ int delayMs);
- virtual void setAudioPortCallbacksEnabled(bool enabled);
+ void removeNotificationClient(uid_t uid, pid_t pid);
+ void onAudioPortListUpdate();
+ void doOnAudioPortListUpdate();
+ void onAudioPatchListUpdate();
+ void doOnAudioPatchListUpdate();
- virtual void setAudioVolumeGroupCallbacksEnabled(bool enabled);
+ void onDynamicPolicyMixStateUpdate(const String8& regId, int32_t state);
+ void doOnDynamicPolicyMixStateUpdate(const String8& regId, int32_t state);
+ void onRecordingConfigurationUpdate(int event,
+ const record_client_info_t *clientInfo,
+ const audio_config_base_t *clientConfig,
+ std::vector<effect_descriptor_t> clientEffects,
+ const audio_config_base_t *deviceConfig,
+ std::vector<effect_descriptor_t> effects,
+ audio_patch_handle_t patchHandle,
+ audio_source_t source);
+ void doOnRecordingConfigurationUpdate(int event,
+ const record_client_info_t *clientInfo,
+ const audio_config_base_t *clientConfig,
+ std::vector<effect_descriptor_t> clientEffects,
+ const audio_config_base_t *deviceConfig,
+ std::vector<effect_descriptor_t> effects,
+ audio_patch_handle_t patchHandle,
+ audio_source_t source);
- virtual status_t acquireSoundTriggerSession(audio_session_t *session,
- audio_io_handle_t *ioHandle,
- audio_devices_t *device);
+ void onAudioVolumeGroupChanged(volume_group_t group, int flags);
+ void doOnAudioVolumeGroupChanged(volume_group_t group, int flags);
- virtual status_t releaseSoundTriggerSession(audio_session_t session);
+ void onRoutingUpdated();
+ void doOnRoutingUpdated();
- virtual audio_mode_t getPhoneState();
-
- virtual status_t registerPolicyMixes(const Vector<AudioMix>& mixes, bool registration);
-
- virtual status_t setUidDeviceAffinities(uid_t uid, const AudioDeviceTypeAddrVector& devices);
-
- virtual status_t removeUidDeviceAffinities(uid_t uid);
-
- virtual status_t setDevicesRoleForStrategy(product_strategy_t strategy,
- device_role_t role,
- const AudioDeviceTypeAddrVector &devices);
-
- virtual status_t removeDevicesRoleForStrategy(product_strategy_t strategy, device_role_t role);
-
- virtual status_t getDevicesForRoleAndStrategy(product_strategy_t strategy,
- device_role_t role,
- AudioDeviceTypeAddrVector &devices);
-
- virtual status_t setDevicesRoleForCapturePreset(audio_source_t audioSource,
- device_role_t role,
- const AudioDeviceTypeAddrVector &devices);
-
- virtual status_t addDevicesRoleForCapturePreset(audio_source_t audioSource,
- device_role_t role,
- const AudioDeviceTypeAddrVector &devices);
-
- virtual status_t removeDevicesRoleForCapturePreset(
- audio_source_t audioSource, device_role_t role,
- const AudioDeviceTypeAddrVector& devices);
-
- virtual status_t clearDevicesRoleForCapturePreset(audio_source_t audioSource,
- device_role_t role);
-
- virtual status_t getDevicesForRoleAndCapturePreset(audio_source_t audioSource,
- device_role_t role,
- AudioDeviceTypeAddrVector &devices);
-
- virtual status_t setUserIdDeviceAffinities(int userId,
- const AudioDeviceTypeAddrVector& devices);
-
- virtual status_t removeUserIdDeviceAffinities(int userId);
-
- virtual status_t startAudioSource(const struct audio_port_config *source,
- const audio_attributes_t *attributes,
- audio_port_handle_t *portId);
- virtual status_t stopAudioSource(audio_port_handle_t portId);
-
- virtual status_t setMasterMono(bool mono);
- virtual status_t getMasterMono(bool *mono);
-
- virtual float getStreamVolumeDB(
- audio_stream_type_t stream, int index, audio_devices_t device);
-
- virtual status_t getSurroundFormats(unsigned int *numSurroundFormats,
- audio_format_t *surroundFormats,
- bool *surroundFormatsEnabled,
- bool reported);
- virtual status_t getHwOffloadEncodingFormatsSupportedForA2DP(
- std::vector<audio_format_t> *formats);
- virtual status_t setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled);
-
- virtual status_t setAssistantUid(uid_t uid);
- virtual status_t setA11yServicesUids(const std::vector<uid_t>& uids);
- virtual status_t setCurrentImeUid(uid_t uid);
-
- virtual bool isHapticPlaybackSupported();
-
- virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies);
- virtual status_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa,
- product_strategy_t &productStrategy);
-
- virtual status_t listAudioVolumeGroups(AudioVolumeGroupVector &groups);
-
- virtual status_t getVolumeGroupFromAudioAttributes(const AudioAttributes &aa,
- volume_group_t &volumeGroup);
-
- status_t registerSoundTriggerCaptureStateListener(
- const sp<media::ICaptureStateListener>& listener,
- bool* result) override;
-
- virtual status_t setRttEnabled(bool enabled);
-
- bool isCallScreenModeSupported() override;
-
- void doOnNewAudioModulesAvailable();
- status_t doStopOutput(audio_port_handle_t portId);
- void doReleaseOutput(audio_port_handle_t portId);
-
- status_t clientCreateAudioPatch(const struct audio_patch *patch,
- audio_patch_handle_t *handle,
- int delayMs);
- status_t clientReleaseAudioPatch(audio_patch_handle_t handle,
- int delayMs);
- virtual status_t clientSetAudioPortConfig(const struct audio_port_config *config,
- int delayMs);
-
- void removeNotificationClient(uid_t uid, pid_t pid);
- void onAudioPortListUpdate();
- void doOnAudioPortListUpdate();
- void onAudioPatchListUpdate();
- void doOnAudioPatchListUpdate();
-
- void onDynamicPolicyMixStateUpdate(const String8& regId, int32_t state);
- void doOnDynamicPolicyMixStateUpdate(const String8& regId, int32_t state);
- void onRecordingConfigurationUpdate(int event,
- const record_client_info_t *clientInfo,
- const audio_config_base_t *clientConfig,
- std::vector<effect_descriptor_t> clientEffects,
- const audio_config_base_t *deviceConfig,
- std::vector<effect_descriptor_t> effects,
- audio_patch_handle_t patchHandle,
- audio_source_t source);
- void doOnRecordingConfigurationUpdate(int event,
- const record_client_info_t *clientInfo,
- const audio_config_base_t *clientConfig,
- std::vector<effect_descriptor_t> clientEffects,
- const audio_config_base_t *deviceConfig,
- std::vector<effect_descriptor_t> effects,
- audio_patch_handle_t patchHandle,
- audio_source_t source);
-
- void onAudioVolumeGroupChanged(volume_group_t group, int flags);
- void doOnAudioVolumeGroupChanged(volume_group_t group, int flags);
- void setEffectSuspended(int effectId,
- audio_session_t sessionId,
- bool suspended);
+ void setEffectSuspended(int effectId,
+ audio_session_t sessionId,
+ bool suspended);
private:
AudioPolicyService() ANDROID_API;
@@ -381,7 +344,7 @@
bool isSupportedSystemUsage(audio_usage_t usage);
status_t validateUsage(audio_usage_t usage);
- status_t validateUsage(audio_usage_t usage, pid_t pid, uid_t uid);
+ status_t validateUsage(audio_usage_t usage, const media::permission::Identity& identity);
void updateUidStates();
void updateUidStates_l() REQUIRES(mLock);
@@ -462,6 +425,7 @@
: mService(service) {}
void registerSelf();
+ void registerSelfForMicrophoneOnly(int userId);
void unregisterSelf();
bool isSensorPrivacyEnabled();
@@ -497,6 +461,7 @@
RECORDING_CONFIGURATION_UPDATE,
SET_EFFECT_SUSPENDED,
AUDIO_MODULES_UPDATE,
+ ROUTING_UPDATED,
};
AudioCommandThread (String8 name, const wp<AudioPolicyService>& service);
@@ -543,6 +508,7 @@
audio_session_t sessionId,
bool suspended);
void audioModulesUpdateCommand();
+ void routingChangedCommand();
void insertCommand_l(AudioCommand *command, int delayMs = 0);
private:
class AudioCommandData;
@@ -761,10 +727,14 @@
virtual void onAudioVolumeGroupChanged(volume_group_t group, int flags);
+ virtual void onRoutingUpdated();
+
virtual audio_unique_id_t newAudioUniqueId(audio_unique_id_use_t use);
void setSoundTriggerCaptureState(bool active) override;
+ status_t getAudioPort(struct audio_port_v7 *port) override;
+
private:
AudioPolicyService *mAudioPolicyService;
};
@@ -773,7 +743,7 @@
class NotificationClient : public IBinder::DeathRecipient {
public:
NotificationClient(const sp<AudioPolicyService>& service,
- const sp<IAudioPolicyServiceClient>& client,
+ const sp<media::IAudioPolicyServiceClient>& client,
uid_t uid, pid_t pid);
virtual ~NotificationClient();
@@ -791,6 +761,7 @@
std::vector<effect_descriptor_t> effects,
audio_patch_handle_t patchHandle,
audio_source_t source);
+ void onRoutingUpdated();
void setAudioPortCallbacksEnabled(bool enabled);
void setAudioVolumeGroupCallbacksEnabled(bool enabled);
@@ -805,29 +776,29 @@
NotificationClient(const NotificationClient&);
NotificationClient& operator = (const NotificationClient&);
- const wp<AudioPolicyService> mService;
- const uid_t mUid;
- const pid_t mPid;
- const sp<IAudioPolicyServiceClient> mAudioPolicyServiceClient;
- bool mAudioPortCallbacksEnabled;
- bool mAudioVolumeGroupCallbacksEnabled;
+ const wp<AudioPolicyService> mService;
+ const uid_t mUid;
+ const pid_t mPid;
+ const sp<media::IAudioPolicyServiceClient> mAudioPolicyServiceClient;
+ bool mAudioPortCallbacksEnabled;
+ bool mAudioVolumeGroupCallbacksEnabled;
};
class AudioClient : public virtual RefBase {
public:
AudioClient(const audio_attributes_t attributes,
- const audio_io_handle_t io, uid_t uid, pid_t pid,
+ const audio_io_handle_t io,
+ const media::permission::Identity& identity,
const audio_session_t session, audio_port_handle_t portId,
const audio_port_handle_t deviceId) :
- attributes(attributes), io(io), uid(uid), pid(pid),
+ attributes(attributes), io(io), identity(identity),
session(session), portId(portId), deviceId(deviceId), active(false) {}
~AudioClient() override = default;
const audio_attributes_t attributes; // source, flags ...
const audio_io_handle_t io; // audio HAL stream IO handle
- const uid_t uid; // client UID
- const pid_t pid; // client PID
+ const media::permission::Identity& identity; //client identity
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
@@ -840,16 +811,17 @@
class AudioRecordClient : public AudioClient {
public:
AudioRecordClient(const audio_attributes_t attributes,
- const audio_io_handle_t io, uid_t uid, pid_t pid,
+ const audio_io_handle_t io,
const audio_session_t session, audio_port_handle_t portId,
- const audio_port_handle_t deviceId, const String16& opPackageName,
+ const audio_port_handle_t deviceId,
+ const media::permission::Identity& identity,
bool canCaptureOutput, bool canCaptureHotword) :
- AudioClient(attributes, io, uid, pid, session, portId, deviceId),
- opPackageName(opPackageName), startTimeNs(0),
+ AudioClient(attributes, io, identity,
+ session, portId, deviceId), identity(identity), startTimeNs(0),
canCaptureOutput(canCaptureOutput), canCaptureHotword(canCaptureHotword) {}
~AudioRecordClient() override = default;
- const String16 opPackageName; // client package name
+ const media::permission::Identity identity; // identity of client
nsecs_t startTimeNs;
const bool canCaptureOutput;
const bool canCaptureHotword;
@@ -861,10 +833,11 @@
class AudioPlaybackClient : public AudioClient {
public:
AudioPlaybackClient(const audio_attributes_t attributes,
- const audio_io_handle_t io, uid_t uid, pid_t pid,
+ const audio_io_handle_t io, media::permission::Identity identity,
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) {}
+ AudioClient(attributes, io, identity, session, portId,
+ deviceId), stream(stream) {}
~AudioPlaybackClient() override = default;
const audio_stream_type_t stream;
@@ -894,7 +867,8 @@
// Internal dump utilities.
status_t dumpPermissionDenial(int fd);
-
+ void loadAudioPolicyManager();
+ void unloadAudioPolicyManager();
mutable Mutex mLock; // prevents concurrent access to AudioPolicy manager functions changing
// device connection state or routing
@@ -928,6 +902,10 @@
MediaPackageManager mPackageManager; // To check allowPlaybackCapture
CaptureStateNotifier mCaptureStateNotifier;
+
+ void *mLibraryHandle = nullptr;
+ CreateAudioPolicyManagerInstance mCreateAudioPolicyManager;
+ DestroyAudioPolicyManagerInstance mDestroyAudioPolicyManager;
};
} // namespace android
diff --git a/services/audiopolicy/tests/Android.bp b/services/audiopolicy/tests/Android.bp
index 42f0342..f480210 100644
--- a/services/audiopolicy/tests/Android.bp
+++ b/services/audiopolicy/tests/Android.bp
@@ -25,6 +25,7 @@
"libmedia_helper",
"libutils",
"libxml2",
+ "media_permission-aidl-cpp",
],
static_libs: [
@@ -54,6 +55,7 @@
cc_test {
name: "audio_health_tests",
+ require_root: true,
shared_libs: [
"libaudiofoundation",
diff --git a/services/audiopolicy/tests/AudioPolicyManagerTestClient.h b/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
index bdddf06..e2d7d17 100644
--- a/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
+++ b/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
@@ -121,6 +121,19 @@
size_t getAudioPortListUpdateCount() const { return mAudioPortListUpdateCount; }
+ virtual void addSupportedFormat(audio_format_t /* format */) {}
+
+ void onRoutingUpdated() override {
+ mRoutingUpdatedUpdateCount++;
+ }
+
+ void resetRoutingUpdatedCounter() {
+ mRoutingUpdatedUpdateCount = 0;
+ }
+
+ size_t getRoutingUpdatedCounter() const {
+ return mRoutingUpdatedUpdateCount; }
+
private:
audio_module_handle_t mNextModuleHandle = AUDIO_MODULE_HANDLE_NONE + 1;
audio_io_handle_t mNextIoHandle = AUDIO_IO_HANDLE_NONE + 1;
@@ -128,6 +141,7 @@
std::map<audio_patch_handle_t, struct audio_patch> mActivePatches;
std::set<std::string> mAllowedModuleNames;
size_t mAudioPortListUpdateCount = 0;
+ size_t mRoutingUpdatedUpdateCount = 0;
};
} // namespace android
diff --git a/services/audiopolicy/tests/AudioPolicyManagerTestClientForHdmi.h b/services/audiopolicy/tests/AudioPolicyManagerTestClientForHdmi.h
new file mode 100644
index 0000000..a5ad9b1
--- /dev/null
+++ b/services/audiopolicy/tests/AudioPolicyManagerTestClientForHdmi.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <map>
+#include <set>
+
+#include <system/audio.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include "AudioPolicyTestClient.h"
+
+namespace android {
+
+class AudioPolicyManagerTestClientForHdmi : public AudioPolicyManagerTestClient {
+public:
+ String8 getParameters(audio_io_handle_t /* ioHandle */, const String8& /* keys*/ ) override {
+ return mAudioParameters.toString();
+ }
+
+ void addSupportedFormat(audio_format_t format) override {
+ mAudioParameters.add(
+ String8(AudioParameter::keyStreamSupportedFormats),
+ String8(audio_format_to_string(format)));
+ mAudioParameters.addInt(String8(AudioParameter::keyStreamSupportedSamplingRates), 48000);
+ mAudioParameters.add(String8(AudioParameter::keyStreamSupportedChannels), String8(""));
+ }
+
+private:
+ AudioParameter mAudioParameters;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/audiopolicy/tests/AudioPolicyTestClient.h b/services/audiopolicy/tests/AudioPolicyTestClient.h
index c628e70..d289e15 100644
--- a/services/audiopolicy/tests/AudioPolicyTestClient.h
+++ b/services/audiopolicy/tests/AudioPolicyTestClient.h
@@ -83,10 +83,14 @@
std::vector<effect_descriptor_t> effects __unused,
audio_patch_handle_t patchHandle __unused,
audio_source_t source __unused) override { }
+ void onRoutingUpdated() override { }
void setEffectSuspended(int effectId __unused,
audio_session_t sessionId __unused,
bool suspended __unused) {}
void setSoundTriggerCaptureState(bool active __unused) override {};
+ status_t getAudioPort(struct audio_port_v7 *port __unused) override {
+ return INVALID_OPERATION;
+ };
};
} // namespace android
diff --git a/services/audiopolicy/tests/AudioPolicyTestManager.h b/services/audiopolicy/tests/AudioPolicyTestManager.h
index 6150206..7f67940 100644
--- a/services/audiopolicy/tests/AudioPolicyTestManager.h
+++ b/services/audiopolicy/tests/AudioPolicyTestManager.h
@@ -29,6 +29,7 @@
using AudioPolicyManager::getOutputs;
using AudioPolicyManager::getAvailableOutputDevices;
using AudioPolicyManager::getAvailableInputDevices;
+ using AudioPolicyManager::setSurroundFormatEnabled;
using AudioPolicyManager::releaseMsdOutputPatches;
using AudioPolicyManager::setMsdOutputPatches;
using AudioPolicyManager::getAudioPatches;
diff --git a/services/audiopolicy/tests/audio_health_tests.cpp b/services/audiopolicy/tests/audio_health_tests.cpp
index e1634cf..df4389b 100644
--- a/services/audiopolicy/tests/audio_health_tests.cpp
+++ b/services/audiopolicy/tests/audio_health_tests.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "AudioPolicy_Boot_Test"
+#include <string>
#include <unordered_set>
#include <gtest/gtest.h>
@@ -34,7 +35,7 @@
unsigned int numPorts;
unsigned int generation1;
unsigned int generation;
- struct audio_port *audioPorts = NULL;
+ struct audio_port_v7 *audioPorts = nullptr;
int attempts = 10;
do {
if (attempts-- < 0) {
@@ -43,13 +44,14 @@
}
numPorts = 0;
ASSERT_EQ(NO_ERROR, AudioSystem::listAudioPorts(
- AUDIO_PORT_ROLE_NONE, AUDIO_PORT_TYPE_DEVICE, &numPorts, NULL, &generation1));
+ AUDIO_PORT_ROLE_NONE, AUDIO_PORT_TYPE_DEVICE, &numPorts, nullptr, &generation1));
if (numPorts == 0) {
free(audioPorts);
GTEST_FAIL() << "Number of audio ports should not be zero";
}
- audioPorts = (struct audio_port *)realloc(audioPorts, numPorts * sizeof(struct audio_port));
+ audioPorts = (struct audio_port_v7 *)realloc(
+ audioPorts, numPorts * sizeof(struct audio_port_v7));
status_t status = AudioSystem::listAudioPorts(
AUDIO_PORT_ROLE_NONE, AUDIO_PORT_TYPE_DEVICE, &numPorts, audioPorts, &generation);
if (status != NO_ERROR) {
@@ -84,3 +86,42 @@
}
}
+TEST(AudioHealthTest, ConnectSupportedDevice) {
+ AudioPolicyManagerTestClient client;
+ AudioPolicyTestManager manager(&client);
+ manager.loadConfig();
+ ASSERT_NE("AudioPolicyConfig::setDefault", manager.getConfig().getSource());
+
+ DeviceVector devices;
+ for (const auto& hwModule : manager.getConfig().getHwModules()) {
+ for (const auto& profile : hwModule->getOutputProfiles()) {
+ devices.merge(profile->getSupportedDevices());
+ }
+ for (const auto& profile : hwModule->getInputProfiles()) {
+ devices.merge(profile->getSupportedDevices());
+ }
+ }
+ for (const auto& device : devices) {
+ if (!audio_is_bluetooth_out_sco_device(device->type()) &&
+ !audio_is_bluetooth_in_sco_device(device->type())) {
+ // There are two reasons to only test connecting BT devices.
+ // 1) It is easier to construct a fake address.
+ // 2) This test will be run in presubmit. In that case, it makes sense to make the test
+ // processing time short.
+ continue;
+ }
+ std::string address = "11:22:33:44:55:66";
+ ASSERT_EQ(AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ AudioSystem::getDeviceConnectionState(device->type(), address.c_str()));
+ ASSERT_EQ(NO_ERROR, AudioSystem::setDeviceConnectionState(
+ device->type(), AUDIO_POLICY_DEVICE_STATE_AVAILABLE, address.c_str(),
+ "" /*device_name*/, AUDIO_FORMAT_DEFAULT));
+ ASSERT_EQ(AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+ AudioSystem::getDeviceConnectionState(device->type(), address.c_str()));
+ ASSERT_EQ(NO_ERROR, AudioSystem::setDeviceConnectionState(
+ device->type(), AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, address.c_str(),
+ "" /*device_name*/, AUDIO_FORMAT_DEFAULT));
+ ASSERT_EQ(AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ AudioSystem::getDeviceConnectionState(device->type(), address.c_str()));
+ }
+}
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index a3d6db0..8f12ecf 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -25,6 +25,7 @@
#define LOG_TAG "APM_Test"
#include <Serializer.h>
#include <android-base/file.h>
+#include <android/media/permission/Identity.h>
#include <media/AudioPolicy.h>
#include <media/PatchBuilder.h>
#include <media/RecordingActivityTracker.h>
@@ -33,11 +34,13 @@
#include "AudioPolicyInterface.h"
#include "AudioPolicyManagerTestClient.h"
+#include "AudioPolicyManagerTestClientForHdmi.h"
#include "AudioPolicyTestClient.h"
#include "AudioPolicyTestManager.h"
using namespace android;
using testing::UnorderedElementsAre;
+using media::permission::Identity;
TEST(AudioPolicyManagerTestInit, EngineFailure) {
AudioPolicyTestClient client;
@@ -137,15 +140,16 @@
// Tries to find a device port. If 'foundPort' isn't nullptr,
// will generate a failure if the port hasn't been found.
bool findDevicePort(audio_port_role_t role, audio_devices_t deviceType,
- const std::string &address, audio_port *foundPort);
+ const std::string &address, audio_port_v7 *foundPort);
static audio_port_handle_t getDeviceIdFromPatch(const struct audio_patch* patch);
+ virtual AudioPolicyManagerTestClient* getClient() { return new AudioPolicyManagerTestClient; }
std::unique_ptr<AudioPolicyManagerTestClient> mClient;
std::unique_ptr<AudioPolicyTestManager> mManager;
};
void AudioPolicyManagerTest::SetUp() {
- mClient.reset(new AudioPolicyManagerTestClient);
+ mClient.reset(getClient());
mManager.reset(new AudioPolicyTestManager(mClient.get()));
SetUpManagerConfig(); // Subclasses may want to customize the config.
ASSERT_EQ(NO_ERROR, mManager->initialize());
@@ -212,8 +216,11 @@
if (!portId) portId = &localPortId;
*portId = AUDIO_PORT_HANDLE_NONE;
AudioPolicyInterface::output_type_t outputType;
+ // TODO b/182392769: use identity util
+ Identity i = Identity();
+ i.uid = 0;
ASSERT_EQ(OK, mManager->getOutputForAttr(
- &attr, output, AUDIO_SESSION_NONE, &stream, 0 /*uid*/, &config, &flags,
+ &attr, output, AUDIO_SESSION_NONE, &stream, i, &config, &flags,
selectedDeviceId, portId, {}, &outputType));
ASSERT_NE(AUDIO_PORT_HANDLE_NONE, *portId);
ASSERT_NE(AUDIO_IO_HANDLE_NONE, *output);
@@ -237,14 +244,17 @@
if (!portId) portId = &localPortId;
*portId = AUDIO_PORT_HANDLE_NONE;
AudioPolicyInterface::input_type_t inputType;
+ // TODO b/182392769: use identity util
+ Identity i = Identity();
+ i.uid = 0;
ASSERT_EQ(OK, mManager->getInputForAttr(
- &attr, &input, riid, AUDIO_SESSION_NONE, 0 /*uid*/, &config, flags,
+ &attr, &input, riid, AUDIO_SESSION_NONE, i, &config, flags,
selectedDeviceId, &inputType, portId));
ASSERT_NE(AUDIO_PORT_HANDLE_NONE, *portId);
}
bool AudioPolicyManagerTest::findDevicePort(audio_port_role_t role,
- audio_devices_t deviceType, const std::string &address, audio_port *foundPort) {
+ audio_devices_t deviceType, const std::string &address, audio_port_v7 *foundPort) {
uint32_t numPorts = 0;
uint32_t generation1;
status_t ret;
@@ -254,7 +264,7 @@
if (HasFailure()) return false;
uint32_t generation2;
- struct audio_port ports[numPorts];
+ struct audio_port_v7 ports[numPorts];
ret = mManager->listAudioPorts(role, AUDIO_PORT_TYPE_DEVICE, &numPorts, ports, &generation2);
EXPECT_EQ(NO_ERROR, ret) << "mManager->listAudioPorts returned error";
EXPECT_EQ(generation1, generation2) << "Generations changed during ports retrieval";
@@ -790,6 +800,188 @@
ASSERT_EQ(INVALID_OPERATION, ret);
}
+class AudioPolicyManagerTestForHdmi
+ : public AudioPolicyManagerTestWithConfigurationFile {
+protected:
+ void SetUp() override;
+ std::string getConfigFile() override { return sTvConfig; }
+ std::map<audio_format_t, bool> getSurroundFormatsHelper();
+ std::vector<audio_format_t> getReportedSurroundFormatsHelper();
+ std::unordered_set<audio_format_t> getFormatsFromPorts();
+ AudioPolicyManagerTestClient* getClient() override {
+ return new AudioPolicyManagerTestClientForHdmi;
+ }
+ void TearDown() override;
+
+ static const std::string sTvConfig;
+
+};
+
+const std::string AudioPolicyManagerTestForHdmi::sTvConfig =
+ AudioPolicyManagerTestForHdmi::sExecutableDir +
+ "test_settop_box_surround_configuration.xml";
+
+void AudioPolicyManagerTestForHdmi::SetUp() {
+ AudioPolicyManagerTest::SetUp();
+ mClient->addSupportedFormat(AUDIO_FORMAT_E_AC3);
+ mManager->setDeviceConnectionState(
+ AUDIO_DEVICE_OUT_HDMI, AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+ "" /*address*/, "" /*name*/, AUDIO_FORMAT_DEFAULT);
+}
+
+void AudioPolicyManagerTestForHdmi::TearDown() {
+ mManager->setDeviceConnectionState(
+ AUDIO_DEVICE_OUT_HDMI, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ "" /*address*/, "" /*name*/, AUDIO_FORMAT_DEFAULT);
+ AudioPolicyManagerTest::TearDown();
+}
+
+std::map<audio_format_t, bool>
+ AudioPolicyManagerTestForHdmi::getSurroundFormatsHelper() {
+ unsigned int numSurroundFormats = 0;
+ std::map<audio_format_t, bool> surroundFormatsMap;
+ status_t ret = mManager->getSurroundFormats(
+ &numSurroundFormats, nullptr /* surroundFormats */,
+ nullptr /* surroundFormatsEnabled */);
+ EXPECT_EQ(NO_ERROR, ret);
+ if (ret != NO_ERROR) {
+ return surroundFormatsMap;
+ }
+ audio_format_t surroundFormats[numSurroundFormats];
+ memset(surroundFormats, 0, sizeof(audio_format_t) * numSurroundFormats);
+ bool surroundFormatsEnabled[numSurroundFormats];
+ memset(surroundFormatsEnabled, 0, sizeof(bool) * numSurroundFormats);
+ ret = mManager->getSurroundFormats(
+ &numSurroundFormats, surroundFormats, surroundFormatsEnabled);
+ EXPECT_EQ(NO_ERROR, ret);
+ if (ret != NO_ERROR) {
+ return surroundFormatsMap;
+ }
+ for (int i = 0; i< numSurroundFormats; i++) {
+ surroundFormatsMap[surroundFormats[i]] = surroundFormatsEnabled[i];
+ }
+ return surroundFormatsMap;
+}
+
+std::vector<audio_format_t> AudioPolicyManagerTestForHdmi::getReportedSurroundFormatsHelper() {
+ unsigned int numSurroundFormats = 0;
+ std::vector<audio_format_t> surroundFormatsVector;
+ status_t ret = mManager->getReportedSurroundFormats(
+ &numSurroundFormats, nullptr /* surroundFormats */);
+ EXPECT_EQ(NO_ERROR, ret);
+ if (ret != NO_ERROR) {
+ return surroundFormatsVector;
+ }
+ audio_format_t surroundFormats[numSurroundFormats];
+ memset(surroundFormats, 0, sizeof(audio_format_t) * numSurroundFormats);
+ ret = mManager->getReportedSurroundFormats(&numSurroundFormats, surroundFormats);
+ EXPECT_EQ(NO_ERROR, ret);
+ if (ret != NO_ERROR) {
+ return surroundFormatsVector;
+ }
+ for (const auto &surroundFormat : surroundFormats) {
+ surroundFormatsVector.push_back(surroundFormat);
+ }
+ return surroundFormatsVector;
+}
+
+std::unordered_set<audio_format_t>
+ AudioPolicyManagerTestForHdmi::getFormatsFromPorts() {
+ uint32_t numPorts = 0;
+ uint32_t generation1;
+ status_t ret;
+ std::unordered_set<audio_format_t> formats;
+ ret = mManager->listAudioPorts(
+ AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE, &numPorts, nullptr, &generation1);
+ EXPECT_EQ(NO_ERROR, ret) << "mManager->listAudioPorts returned error";
+ if (ret != NO_ERROR) {
+ return formats;
+ }
+ struct audio_port_v7 ports[numPorts];
+ ret = mManager->listAudioPorts(
+ AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE, &numPorts, ports, &generation1);
+ EXPECT_EQ(NO_ERROR, ret) << "mManager->listAudioPorts returned error";
+ if (ret != NO_ERROR) {
+ return formats;
+ }
+ for (const auto &port : ports) {
+ for (size_t i = 0; i < port.num_audio_profiles; ++i) {
+ formats.insert(port.audio_profiles[i].format);
+ }
+ }
+ return formats;
+}
+
+TEST_F(AudioPolicyManagerTestForHdmi, GetSurroundFormatsReturnsSupportedFormats) {
+ mManager->setForceUse(
+ AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND, AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS);
+ auto surroundFormats = getSurroundFormatsHelper();
+ ASSERT_EQ(1, surroundFormats.count(AUDIO_FORMAT_E_AC3));
+}
+
+TEST_F(AudioPolicyManagerTestForHdmi,
+ GetSurroundFormatsReturnsManipulatedFormats) {
+ mManager->setForceUse(
+ AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND, AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL);
+
+ status_t ret =
+ mManager->setSurroundFormatEnabled(AUDIO_FORMAT_E_AC3, false /*enabled*/);
+ ASSERT_EQ(NO_ERROR, ret);
+ auto surroundFormats = getSurroundFormatsHelper();
+ ASSERT_EQ(1, surroundFormats.count(AUDIO_FORMAT_E_AC3));
+ ASSERT_FALSE(surroundFormats[AUDIO_FORMAT_E_AC3]);
+
+ ret = mManager->setSurroundFormatEnabled(AUDIO_FORMAT_E_AC3, true /*enabled*/);
+ ASSERT_EQ(NO_ERROR, ret);
+ surroundFormats = getSurroundFormatsHelper();
+ ASSERT_EQ(1, surroundFormats.count(AUDIO_FORMAT_E_AC3));
+ ASSERT_TRUE(surroundFormats[AUDIO_FORMAT_E_AC3]);
+
+ ret = mManager->setSurroundFormatEnabled(AUDIO_FORMAT_E_AC3, false /*enabled*/);
+ ASSERT_EQ(NO_ERROR, ret);
+ surroundFormats = getSurroundFormatsHelper();
+ ASSERT_EQ(1, surroundFormats.count(AUDIO_FORMAT_E_AC3));
+ ASSERT_FALSE(surroundFormats[AUDIO_FORMAT_E_AC3]);
+}
+
+TEST_F(AudioPolicyManagerTestForHdmi,
+ ListAudioPortsReturnManipulatedHdmiFormats) {
+ mManager->setForceUse(
+ AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND, AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL);
+
+ ASSERT_EQ(NO_ERROR, mManager->setSurroundFormatEnabled(AUDIO_FORMAT_E_AC3, false /*enabled*/));
+ auto formats = getFormatsFromPorts();
+ ASSERT_EQ(0, formats.count(AUDIO_FORMAT_E_AC3));
+
+ ASSERT_EQ(NO_ERROR, mManager->setSurroundFormatEnabled(AUDIO_FORMAT_E_AC3, true /*enabled*/));
+ formats = getFormatsFromPorts();
+ ASSERT_EQ(1, formats.count(AUDIO_FORMAT_E_AC3));
+}
+
+TEST_F(AudioPolicyManagerTestForHdmi,
+ GetReportedSurroundFormatsReturnsHdmiReportedFormats) {
+ mManager->setForceUse(
+ AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND, AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS);
+ auto surroundFormats = getReportedSurroundFormatsHelper();
+ ASSERT_EQ(1, std::count(surroundFormats.begin(), surroundFormats.end(), AUDIO_FORMAT_E_AC3));
+}
+
+TEST_F(AudioPolicyManagerTestForHdmi,
+ GetReportedSurroundFormatsReturnsNonManipulatedHdmiReportedFormats) {
+ mManager->setForceUse(
+ AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND, AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL);
+
+ status_t ret = mManager->setSurroundFormatEnabled(AUDIO_FORMAT_E_AC3, false /*enabled*/);
+ ASSERT_EQ(NO_ERROR, ret);
+ auto surroundFormats = getReportedSurroundFormatsHelper();
+ ASSERT_EQ(1, std::count(surroundFormats.begin(), surroundFormats.end(), AUDIO_FORMAT_E_AC3));
+
+ ret = mManager->setSurroundFormatEnabled(AUDIO_FORMAT_E_AC3, true /*enabled*/);
+ ASSERT_EQ(NO_ERROR, ret);
+ surroundFormats = getReportedSurroundFormatsHelper();
+ ASSERT_EQ(1, std::count(surroundFormats.begin(), surroundFormats.end(), AUDIO_FORMAT_E_AC3));
+}
+
class AudioPolicyManagerTestDPNoRemoteSubmixModule : public AudioPolicyManagerTestDynamicPolicy {
protected:
std::string getConfigFile() override { return sPrimaryOnlyConfig; }
@@ -836,7 +1028,7 @@
{AUDIO_USAGE_ALARM, AUDIO_SOURCE_DEFAULT, RULE_MATCH_ATTRIBUTE_USAGE}
};
- struct audio_port mInjectionPort;
+ struct audio_port_v7 mInjectionPort;
audio_port_handle_t mPortId = AUDIO_PORT_HANDLE_NONE;
};
@@ -853,7 +1045,7 @@
AUDIO_DEVICE_OUT_REMOTE_SUBMIX, mMixAddress, audioConfig, mUsageRules);
ASSERT_EQ(NO_ERROR, ret);
- struct audio_port extractionPort;
+ struct audio_port_v7 extractionPort;
ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SOURCE, AUDIO_DEVICE_IN_REMOTE_SUBMIX,
mMixAddress, &extractionPort));
@@ -1022,7 +1214,7 @@
{AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_VOICE_COMMUNICATION, RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET}
};
- struct audio_port mExtractionPort;
+ struct audio_port_v7 mExtractionPort;
audio_port_handle_t mPortId = AUDIO_PORT_HANDLE_NONE;
};
@@ -1039,7 +1231,7 @@
AUDIO_DEVICE_IN_REMOTE_SUBMIX, mMixAddress, audioConfig, mSourceRules);
ASSERT_EQ(NO_ERROR, ret);
- struct audio_port injectionPort;
+ struct audio_port_v7 injectionPort;
ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SINK, AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
mMixAddress, &injectionPort));
@@ -1145,6 +1337,34 @@
dumpToLog();
}
+TEST_F(AudioPolicyManagerTestDeviceConnection, RoutingUpdate) {
+ mClient->resetRoutingUpdatedCounter();
+ // Connecting a valid output device with valid parameters should trigger a routing update
+ ASSERT_EQ(NO_ERROR, mManager->setDeviceConnectionState(
+ AUDIO_DEVICE_OUT_BLUETOOTH_SCO, AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+ "a", "b", AUDIO_FORMAT_DEFAULT));
+ ASSERT_EQ(1, mClient->getRoutingUpdatedCounter());
+
+ // Disconnecting a connected device should succeed and trigger a routing update
+ ASSERT_EQ(NO_ERROR, mManager->setDeviceConnectionState(
+ AUDIO_DEVICE_OUT_BLUETOOTH_SCO, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ "a", "b", AUDIO_FORMAT_DEFAULT));
+ ASSERT_EQ(2, mClient->getRoutingUpdatedCounter());
+
+ // Disconnecting a disconnected device should fail and not trigger a routing update
+ ASSERT_EQ(INVALID_OPERATION, mManager->setDeviceConnectionState(
+ AUDIO_DEVICE_OUT_BLUETOOTH_SCO, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ "a", "b", AUDIO_FORMAT_DEFAULT));
+ ASSERT_EQ(2, mClient->getRoutingUpdatedCounter());
+
+ // Changing force use should trigger an update
+ auto config = mManager->getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA);
+ auto newConfig = config == AUDIO_POLICY_FORCE_BT_A2DP ?
+ AUDIO_POLICY_FORCE_NONE : AUDIO_POLICY_FORCE_BT_A2DP;
+ mManager->setForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA, newConfig);
+ ASSERT_EQ(3, mClient->getRoutingUpdatedCounter());
+}
+
TEST_P(AudioPolicyManagerTestDeviceConnection, SetDeviceConnectionState) {
const audio_devices_t type = std::get<0>(GetParam());
const std::string name = std::get<1>(GetParam());
@@ -1190,7 +1410,7 @@
type, AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
address.c_str(), name.c_str(), AUDIO_FORMAT_DEFAULT));
- audio_port devicePort;
+ audio_port_v7 devicePort;
const audio_port_role_t role = audio_is_output_device(type)
? AUDIO_PORT_ROLE_SINK : AUDIO_PORT_ROLE_SOURCE;
ASSERT_TRUE(findDevicePort(role, type, address, &devicePort));
@@ -1251,7 +1471,7 @@
flags, &output, &portId);
sp<SwAudioOutputDescriptor> outDesc = mManager->getOutputs().valueFor(output);
ASSERT_NE(nullptr, outDesc.get());
- audio_port port = {};
+ audio_port_v7 port = {};
outDesc->toAudioPort(&port);
mManager->releaseOutput(portId);
ASSERT_EQ(NO_ERROR, mManager->setDeviceConnectionState(
@@ -1333,7 +1553,7 @@
findDevicePort(AUDIO_PORT_ROLE_SOURCE, AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0", nullptr));
mClient->swapAllowedModuleNames({"primary", "r_submix"});
mManager->onNewAudioModulesAvailable();
- struct audio_port port;
+ struct audio_port_v7 port;
ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SOURCE, AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0", &port));
}
diff --git a/services/audiopolicy/tests/resources/Android.bp b/services/audiopolicy/tests/resources/Android.bp
index 630bbcf..ff4d568 100644
--- a/services/audiopolicy/tests/resources/Android.bp
+++ b/services/audiopolicy/tests/resources/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_av_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_av_license"],
}
@@ -16,5 +14,6 @@
"test_audio_policy_primary_only_configuration.xml",
"test_invalid_audio_policy_configuration.xml",
"test_tv_apm_configuration.xml",
+ "test_settop_box_surround_configuration.xml",
],
}
diff --git a/services/audiopolicy/tests/resources/test_settop_box_surround_configuration.xml b/services/audiopolicy/tests/resources/test_settop_box_surround_configuration.xml
new file mode 100644
index 0000000..6f7375e
--- /dev/null
+++ b/services/audiopolicy/tests/resources/test_settop_box_surround_configuration.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <globalConfiguration speaker_drc_enabled="false"/>
+ <modules>
+ <module name="primary" halVersion="2.0">
+ <attachedDevices>
+ <item>Stub</item>
+ </attachedDevices>
+ <defaultOutputDevice>Stub</defaultOutputDevice>
+ <mixPorts>
+ <mixPort name="primary pcm" role="source"
+ flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="multichannel output" role="source"
+ flags="AUDIO_OUTPUT_FLAG_DIRECT">
+ <profile name="" />
+ </mixPort>
+ </mixPorts>
+ <devicePorts>
+ <devicePort tagName="Stub" type="AUDIO_DEVICE_OUT_STUB" role="sink" />
+ <devicePort tagName="HDMI" type="AUDIO_DEVICE_OUT_HDMI" role="sink" />
+ </devicePorts>
+ <routes>
+ <route type="mix" sink="Stub" sources="primary pcm"/>
+ <route type="mix" sink="HDMI" sources="primary pcm,multichannel output"/>
+ </routes>
+ </module>
+ </modules>
+</audioPolicyConfiguration>
diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp
index d16ea1a..c28c24b 100644
--- a/services/camera/libcameraservice/Android.bp
+++ b/services/camera/libcameraservice/Android.bp
@@ -49,7 +49,6 @@
"common/CameraProviderManager.cpp",
"common/DepthPhotoProcessor.cpp",
"common/FrameProcessorBase.cpp",
- "api1/CameraClient.cpp",
"api1/Camera2Client.cpp",
"api1/client2/Parameters.cpp",
"api1/client2/FrameProcessor.cpp",
@@ -65,7 +64,6 @@
"api2/DepthCompositeStream.cpp",
"api2/HeicEncoderInfoManager.cpp",
"api2/HeicCompositeStream.cpp",
- "device1/CameraHardwareInterface.cpp",
"device3/BufferUtils.cpp",
"device3/Camera3Device.cpp",
"device3/Camera3OfflineSession.cpp",
@@ -73,7 +71,7 @@
"device3/Camera3IOStreamBase.cpp",
"device3/Camera3InputStream.cpp",
"device3/Camera3OutputStream.cpp",
- "device3/Camera3DummyStream.cpp",
+ "device3/Camera3FakeStream.cpp",
"device3/Camera3SharedOutputStream.cpp",
"device3/StatusTracker.cpp",
"device3/Camera3BufferManager.cpp",
@@ -84,17 +82,20 @@
"device3/RotateAndCropMapper.cpp",
"device3/Camera3OutputStreamInterface.cpp",
"device3/Camera3OutputUtils.cpp",
+ "device3/Camera3DeviceInjectionMethods.cpp",
"gui/RingBufferConsumer.cpp",
"hidl/AidlCameraDeviceCallbacks.cpp",
"hidl/AidlCameraServiceListener.cpp",
"hidl/Convert.cpp",
"hidl/HidlCameraDeviceUser.cpp",
"hidl/HidlCameraService.cpp",
+ "utils/CameraServiceProxyWrapper.cpp",
"utils/CameraThreadState.cpp",
"utils/CameraTraces.cpp",
"utils/AutoConditionLock.cpp",
"utils/ExifUtils.cpp",
"utils/SessionConfigurationUtils.cpp",
+ "utils/SessionStatsBuilder.cpp",
"utils/TagMonitor.cpp",
"utils/LatencyHistogram.cpp",
],
@@ -138,22 +139,27 @@
"android.frameworks.cameraservice.common@2.0",
"android.frameworks.cameraservice.service@2.0",
"android.frameworks.cameraservice.service@2.1",
+ "android.frameworks.cameraservice.service@2.2",
"android.frameworks.cameraservice.device@2.0",
+ "android.frameworks.cameraservice.device@2.1",
"android.hardware.camera.common@1.0",
"android.hardware.camera.provider@2.4",
"android.hardware.camera.provider@2.5",
"android.hardware.camera.provider@2.6",
- "android.hardware.camera.device@1.0",
+ "android.hardware.camera.provider@2.7",
"android.hardware.camera.device@3.2",
"android.hardware.camera.device@3.3",
"android.hardware.camera.device@3.4",
"android.hardware.camera.device@3.5",
- "android.hardware.camera.device@3.6"
+ "android.hardware.camera.device@3.6",
+ "android.hardware.camera.device@3.7",
+ "media_permission-aidl-cpp",
],
static_libs: [
"libprocessinfoservice_aidl",
"libbinderthreadstateutils",
+ "media_permission-aidl-cpp",
],
export_shared_lib_headers: [
diff --git a/services/camera/libcameraservice/CameraFlashlight.cpp b/services/camera/libcameraservice/CameraFlashlight.cpp
index e629cdd..ccdd9e5 100644
--- a/services/camera/libcameraservice/CameraFlashlight.cpp
+++ b/services/camera/libcameraservice/CameraFlashlight.cpp
@@ -59,9 +59,8 @@
if (mProviderManager->supportSetTorchMode(cameraId.string())) {
mFlashControl = new ProviderFlashControl(mProviderManager);
} else {
- // Only HAL1 devices do not support setTorchMode
- mFlashControl =
- new CameraHardwareInterfaceFlashControl(mProviderManager, mCallbacks);
+ ALOGE("Flashlight control not supported by this device!");
+ return NO_INIT;
}
return OK;
@@ -309,271 +308,4 @@
}
// ProviderFlashControl implementation ends
-/////////////////////////////////////////////////////////////////////
-// CameraHardwareInterfaceFlashControl implementation begins
-// Flash control for camera module <= v2.3 and camera HAL v1
-/////////////////////////////////////////////////////////////////////
-
-CameraHardwareInterfaceFlashControl::CameraHardwareInterfaceFlashControl(
- sp<CameraProviderManager> manager,
- CameraProviderManager::StatusListener* callbacks) :
- mProviderManager(manager),
- mCallbacks(callbacks),
- mTorchEnabled(false) {
-}
-
-CameraHardwareInterfaceFlashControl::~CameraHardwareInterfaceFlashControl() {
- disconnectCameraDevice();
-
- mSurface.clear();
- mSurfaceTexture.clear();
- mProducer.clear();
- mConsumer.clear();
-
- if (mTorchEnabled) {
- if (mCallbacks) {
- ALOGV("%s: notify the framework that torch was turned off",
- __FUNCTION__);
- mCallbacks->onTorchStatusChanged(mCameraId, TorchModeStatus::AVAILABLE_OFF);
- }
- }
-}
-
-status_t CameraHardwareInterfaceFlashControl::setTorchMode(
- const String8& cameraId, bool enabled) {
- Mutex::Autolock l(mLock);
-
- // pre-check
- status_t res;
- if (enabled) {
- bool hasFlash = false;
- // Check if it has a flash unit and leave camera device open.
- res = hasFlashUnitLocked(cameraId, &hasFlash, /*keepDeviceOpen*/true);
- // invalid camera?
- if (res) {
- // hasFlashUnitLocked() returns BAD_INDEX if mDevice is connected to
- // another camera device.
- return res == BAD_INDEX ? BAD_INDEX : -EINVAL;
- }
- // no flash unit?
- if (!hasFlash) {
- // Disconnect camera device if it has no flash.
- disconnectCameraDevice();
- return -ENOSYS;
- }
- } else if (mDevice == NULL || cameraId != mCameraId) {
- // disabling the torch mode of an un-opened or different device.
- return OK;
- } else {
- // disabling the torch mode of currently opened device
- disconnectCameraDevice();
- mTorchEnabled = false;
- mCallbacks->onTorchStatusChanged(cameraId, TorchModeStatus::AVAILABLE_OFF);
- return OK;
- }
-
- res = startPreviewAndTorch();
- if (res) {
- return res;
- }
-
- mTorchEnabled = true;
- mCallbacks->onTorchStatusChanged(cameraId, TorchModeStatus::AVAILABLE_ON);
- return OK;
-}
-
-status_t CameraHardwareInterfaceFlashControl::hasFlashUnit(
- const String8& cameraId, bool *hasFlash) {
- Mutex::Autolock l(mLock);
- // Close device after checking if it has a flash unit.
- return hasFlashUnitLocked(cameraId, hasFlash, /*keepDeviceOpen*/false);
-}
-
-status_t CameraHardwareInterfaceFlashControl::hasFlashUnitLocked(
- const String8& cameraId, bool *hasFlash, bool keepDeviceOpen) {
- bool closeCameraDevice = false;
-
- if (!hasFlash) {
- return BAD_VALUE;
- }
-
- status_t res;
- if (mDevice == NULL) {
- // Connect to camera device to query if it has a flash unit.
- res = connectCameraDevice(cameraId);
- if (res) {
- return res;
- }
- // Close camera device only when it is just opened and the caller doesn't want to keep
- // the camera device open.
- closeCameraDevice = !keepDeviceOpen;
- }
-
- if (cameraId != mCameraId) {
- return BAD_INDEX;
- }
-
- const char *flashMode =
- mParameters.get(CameraParameters::KEY_SUPPORTED_FLASH_MODES);
- if (flashMode && strstr(flashMode, CameraParameters::FLASH_MODE_TORCH)) {
- *hasFlash = true;
- } else {
- *hasFlash = false;
- }
-
- if (closeCameraDevice) {
- res = disconnectCameraDevice();
- if (res != OK) {
- ALOGE("%s: Failed to disconnect camera device. %s (%d)", __FUNCTION__,
- strerror(-res), res);
- return res;
- }
- }
-
- return OK;
-}
-
-status_t CameraHardwareInterfaceFlashControl::startPreviewAndTorch() {
- status_t res = OK;
- res = mDevice->startPreview();
- if (res) {
- ALOGE("%s: start preview failed. %s (%d)", __FUNCTION__,
- strerror(-res), res);
- return res;
- }
-
- mParameters.set(CameraParameters::KEY_FLASH_MODE,
- CameraParameters::FLASH_MODE_TORCH);
-
- return mDevice->setParameters(mParameters);
-}
-
-status_t CameraHardwareInterfaceFlashControl::getSmallestSurfaceSize(
- int32_t *width, int32_t *height) {
- if (!width || !height) {
- return BAD_VALUE;
- }
-
- int32_t w = INT32_MAX;
- int32_t h = 1;
- Vector<Size> sizes;
-
- mParameters.getSupportedPreviewSizes(sizes);
- for (size_t i = 0; i < sizes.size(); i++) {
- Size s = sizes[i];
- if (w * h > s.width * s.height) {
- w = s.width;
- h = s.height;
- }
- }
-
- if (w == INT32_MAX) {
- return NAME_NOT_FOUND;
- }
-
- *width = w;
- *height = h;
-
- return OK;
-}
-
-status_t CameraHardwareInterfaceFlashControl::initializePreviewWindow(
- const sp<CameraHardwareInterface>& device, int32_t width, int32_t height) {
- status_t res;
- BufferQueue::createBufferQueue(&mProducer, &mConsumer);
-
- mSurfaceTexture = new GLConsumer(mConsumer, 0, GLConsumer::TEXTURE_EXTERNAL,
- true, true);
- if (mSurfaceTexture == NULL) {
- return NO_MEMORY;
- }
-
- int32_t format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
- res = mSurfaceTexture->setDefaultBufferSize(width, height);
- if (res) {
- return res;
- }
- res = mSurfaceTexture->setDefaultBufferFormat(format);
- if (res) {
- return res;
- }
-
- mSurface = new Surface(mProducer, /*useAsync*/ true);
- if (mSurface == NULL) {
- return NO_MEMORY;
- }
-
- res = native_window_api_connect(mSurface.get(), NATIVE_WINDOW_API_CAMERA);
- if (res) {
- ALOGE("%s: Unable to connect to native window", __FUNCTION__);
- return res;
- }
-
- return device->setPreviewWindow(mSurface);
-}
-
-status_t CameraHardwareInterfaceFlashControl::connectCameraDevice(
- const String8& cameraId) {
- sp<CameraHardwareInterface> device =
- new CameraHardwareInterface(cameraId.string());
-
- status_t res = device->initialize(mProviderManager);
- if (res) {
- ALOGE("%s: initializing camera %s failed", __FUNCTION__,
- cameraId.string());
- return res;
- }
-
- // need to set __get_memory in set_callbacks().
- device->setCallbacks(NULL, NULL, NULL, NULL, NULL);
-
- mParameters = device->getParameters();
-
- int32_t width, height;
- res = getSmallestSurfaceSize(&width, &height);
- if (res) {
- ALOGE("%s: failed to get smallest surface size for camera %s",
- __FUNCTION__, cameraId.string());
- return res;
- }
-
- res = initializePreviewWindow(device, width, height);
- if (res) {
- ALOGE("%s: failed to initialize preview window for camera %s",
- __FUNCTION__, cameraId.string());
- return res;
- }
-
- mCameraId = cameraId;
- mDevice = device;
- return OK;
-}
-
-status_t CameraHardwareInterfaceFlashControl::disconnectCameraDevice() {
- if (mDevice == NULL) {
- return OK;
- }
-
- if (mParameters.get(CameraParameters::KEY_FLASH_MODE)) {
- // There is a flash, turn if off.
- // (If there isn't one, leave the parameter null)
- mParameters.set(CameraParameters::KEY_FLASH_MODE,
- CameraParameters::FLASH_MODE_OFF);
- mDevice->setParameters(mParameters);
- }
- mDevice->stopPreview();
- status_t res = native_window_api_disconnect(mSurface.get(),
- NATIVE_WINDOW_API_CAMERA);
- if (res) {
- ALOGW("%s: native_window_api_disconnect failed: %s (%d)",
- __FUNCTION__, strerror(-res), res);
- }
- mDevice->setPreviewWindow(NULL);
- mDevice->release();
- mDevice = NULL;
-
- return OK;
-}
-// CameraHardwareInterfaceFlashControl implementation ends
-
}
diff --git a/services/camera/libcameraservice/CameraFlashlight.h b/services/camera/libcameraservice/CameraFlashlight.h
index 1baaba2..b97fa5f 100644
--- a/services/camera/libcameraservice/CameraFlashlight.h
+++ b/services/camera/libcameraservice/CameraFlashlight.h
@@ -23,8 +23,6 @@
#include <utils/SortedVector.h>
#include "common/CameraProviderManager.h"
#include "common/CameraDeviceBase.h"
-#include "device1/CameraHardwareInterface.h"
-
namespace android {
@@ -124,59 +122,6 @@
Mutex mLock;
};
-/**
- * Flash control for camera module <= v2.3 and camera HAL v1
- */
-class CameraHardwareInterfaceFlashControl : public FlashControlBase {
- public:
- CameraHardwareInterfaceFlashControl(
- sp<CameraProviderManager> manager,
- CameraProviderManager::StatusListener* callbacks);
- virtual ~CameraHardwareInterfaceFlashControl();
-
- // FlashControlBase
- status_t setTorchMode(const String8& cameraId, bool enabled);
- status_t hasFlashUnit(const String8& cameraId, bool *hasFlash);
-
- private:
- // connect to a camera device
- status_t connectCameraDevice(const String8& cameraId);
-
- // disconnect and free mDevice
- status_t disconnectCameraDevice();
-
- // initialize the preview window
- status_t initializePreviewWindow(const sp<CameraHardwareInterface>& device,
- int32_t width, int32_t height);
-
- // start preview and enable torch
- status_t startPreviewAndTorch();
-
- // get the smallest surface
- status_t getSmallestSurfaceSize(int32_t *width, int32_t *height);
-
- // protected by mLock
- // If this function opens camera device in order to check if it has a flash unit, the
- // camera device will remain open if keepDeviceOpen is true and the camera device will be
- // closed if keepDeviceOpen is false. If camera device is already open when calling this
- // function, keepDeviceOpen is ignored.
- status_t hasFlashUnitLocked(const String8& cameraId, bool *hasFlash, bool keepDeviceOpen);
-
- sp<CameraProviderManager> mProviderManager;
- CameraProviderManager::StatusListener* mCallbacks;
- sp<CameraHardwareInterface> mDevice;
- String8 mCameraId;
- CameraParameters mParameters;
- bool mTorchEnabled;
-
- sp<IGraphicBufferProducer> mProducer;
- sp<IGraphicBufferConsumer> mConsumer;
- sp<GLConsumer> mSurfaceTexture;
- sp<Surface> mSurface;
-
- Mutex mLock;
-};
-
} // namespace android
#endif
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 91dda92..a0448b4 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -21,6 +21,7 @@
#include <algorithm>
#include <climits>
#include <stdio.h>
+#include <cstdlib>
#include <cstring>
#include <ctime>
#include <string>
@@ -37,7 +38,6 @@
#include <binder/ActivityManager.h>
#include <binder/AppOpsManager.h>
#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
#include <binder/MemoryBase.h>
#include <binder/MemoryHeapBase.h>
#include <binder/PermissionController.h>
@@ -70,12 +70,12 @@
#include <system/camera.h>
#include "CameraService.h"
-#include "api1/CameraClient.h"
#include "api1/Camera2Client.h"
#include "api2/CameraDeviceClient.h"
#include "utils/CameraTraces.h"
#include "utils/TagMonitor.h"
#include "utils/CameraThreadState.h"
+#include "utils/CameraServiceProxyWrapper.h"
namespace {
const char* kPermissionServiceName = "permission";
@@ -88,10 +88,11 @@
using frameworks::cameraservice::service::V2_0::implementation::HidlCameraService;
using hardware::ICamera;
using hardware::ICameraClient;
-using hardware::ICameraServiceProxy;
using hardware::ICameraServiceListener;
using hardware::camera::common::V1_0::CameraDeviceStatus;
using hardware::camera::common::V1_0::TorchModeStatus;
+using hardware::camera2::ICameraInjectionCallback;
+using hardware::camera2::ICameraInjectionSession;
using hardware::camera2::utils::CameraIdAndSessionConfiguration;
using hardware::camera2::utils::ConcurrentCameraIdCombination;
@@ -128,15 +129,16 @@
sCameraSendSystemEventsPermission("android.permission.CAMERA_SEND_SYSTEM_EVENTS");
static const String16 sCameraOpenCloseListenerPermission(
"android.permission.CAMERA_OPEN_CLOSE_LISTENER");
+static const String16
+ sCameraInjectExternalCameraPermission("android.permission.CAMERA_INJECT_EXTERNAL_CAMERA");
-// Matches with PERCEPTIBLE_APP_ADJ in ProcessList.java
-static constexpr int32_t kVendorClientScore = 200;
-// Matches with PROCESS_STATE_PERSISTENT_UI in ActivityManager.java
-static constexpr int32_t kVendorClientState = 1;
+static constexpr int32_t kVendorClientScore = resource_policy::PERCEPTIBLE_APP_ADJ;
+static constexpr int32_t kVendorClientState = ActivityManager::PROCESS_STATE_PERSISTENT_UI;
+
const String8 CameraService::kOfflineDevice("offline-");
-Mutex CameraService::sProxyMutex;
-sp<hardware::ICameraServiceProxy> CameraService::sCameraServiceProxy;
+// Set to keep track of logged service error events.
+static std::set<String8> sServiceErrorEventSet;
CameraService::CameraService() :
mEventLog(DEFAULT_EVENT_LOG_LENGTH),
@@ -150,6 +152,7 @@
void CameraService::onFirstRef()
{
+
ALOGI("CameraService process starting");
BnCameraService::onFirstRef();
@@ -170,6 +173,7 @@
mUidPolicy->registerSelf();
mSensorPrivacyPolicy = new SensorPrivacyPolicy(this);
mSensorPrivacyPolicy->registerSelf();
+ mInjectionStatusListener = new InjectionStatusListener(this);
mAppOps.setCameraAudioRestriction(mAudioRestriction);
sp<HidlCameraService> hcs = HidlCameraService::getInstance(this);
if (hcs->registerAsService() != android::OK) {
@@ -179,7 +183,7 @@
// This needs to be last call in this function, so that it's as close to
// ServiceManager::addService() as possible.
- CameraService::pingCameraServiceProxy();
+ CameraServiceProxyWrapper::pingCameraServiceProxy();
ALOGI("CameraService pinged cameraservice proxy");
}
@@ -196,6 +200,8 @@
if (res != OK) {
ALOGE("%s: Unable to initialize camera provider manager: %s (%d)",
__FUNCTION__, strerror(-res), res);
+ logServiceError(String8::format("Unable to initialize camera provider manager"),
+ ERROR_DISCONNECTED);
return res;
}
}
@@ -229,29 +235,6 @@
return OK;
}
-sp<ICameraServiceProxy> CameraService::getCameraServiceProxy() {
-#ifndef __BRILLO__
- Mutex::Autolock al(sProxyMutex);
- if (sCameraServiceProxy == nullptr) {
- sp<IServiceManager> sm = defaultServiceManager();
- // Use checkService because cameraserver normally starts before the
- // system server and the proxy service. So the long timeout that getService
- // has before giving up is inappropriate.
- sp<IBinder> binder = sm->checkService(String16("media.camera.proxy"));
- if (binder != nullptr) {
- sCameraServiceProxy = interface_cast<ICameraServiceProxy>(binder);
- }
- }
-#endif
- return sCameraServiceProxy;
-}
-
-void CameraService::pingCameraServiceProxy() {
- sp<ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
- if (proxyBinder == nullptr) return;
- proxyBinder->pingForUserUpdate();
-}
-
void CameraService::broadcastTorchModeStatus(const String8& cameraId, TorchModeStatus status,
SystemCameraKind systemCameraKind) {
Mutex::Autolock lock(mStatusListenerLock);
@@ -270,6 +253,7 @@
VendorTagDescriptor::clearGlobalVendorTagDescriptor();
mUidPolicy->unregisterSelf();
mSensorPrivacyPolicy->unregisterSelf();
+ mInjectionStatusListener->removeListener();
}
void CameraService::onNewProviderRegistered() {
@@ -618,6 +602,7 @@
}
if (!mInitialized) {
+ logServiceError(String8::format("Camera subsystem is not available"),ERROR_DISCONNECTED);
return STATUS_ERROR(ERROR_DISCONNECTED,
"Camera subsystem is not available");
}
@@ -640,6 +625,8 @@
ret = STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
"Error retrieving camera info from device %d: %s (%d)", cameraId,
strerror(-err), err);
+ logServiceError(String8::format("Error retrieving camera info from device %d",cameraId),
+ ERROR_INVALID_OPERATION);
}
return ret;
@@ -677,6 +664,7 @@
if (!mInitialized) {
ALOGE("%s: Camera HAL couldn't be initialized", __FUNCTION__);
+ logServiceError(String8::format("Camera subsystem is not available"),ERROR_DISCONNECTED);
return STATUS_ERROR(ERROR_DISCONNECTED,
"Camera subsystem is not available");;
}
@@ -691,9 +679,17 @@
status_t res = mCameraProviderManager->getCameraCharacteristics(
String8(cameraId).string(), cameraInfo);
if (res != OK) {
- return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION, "Unable to retrieve camera "
- "characteristics for device %s: %s (%d)", String8(cameraId).string(),
- strerror(-res), res);
+ if (res == NAME_NOT_FOUND) {
+ return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT, "Unable to retrieve camera "
+ "characteristics for unknown device %s: %s (%d)", String8(cameraId).string(),
+ strerror(-res), res);
+ } else {
+ logServiceError(String8::format("Unable to retrieve camera characteristics for "
+ "device %s.", String8(cameraId).string()),ERROR_INVALID_OPERATION);
+ return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION, "Unable to retrieve camera "
+ "characteristics for device %s: %s (%d)", String8(cameraId).string(),
+ strerror(-res), res);
+ }
}
SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
if (getSystemCameraKind(String8(cameraId), &deviceKind) != OK) {
@@ -773,7 +769,11 @@
return Status::ok();
}
-int CameraService::getDeviceVersion(const String8& cameraId, int* facing) {
+void CameraService::clearCachedVariables() {
+ BasicClient::BasicClient::sCameraService = nullptr;
+}
+
+int CameraService::getDeviceVersion(const String8& cameraId, int* facing, int* orientation) {
ATRACE_CALL();
int deviceVersion = 0;
@@ -790,6 +790,9 @@
res = mCameraProviderManager->getCameraInfo(cameraId.string(), &info);
if (res != OK) return -1;
*facing = info.facing;
+ if (orientation) {
+ *orientation = info.orientation;
+ }
}
return deviceVersion;
@@ -814,74 +817,46 @@
Status CameraService::makeClient(const sp<CameraService>& cameraService,
const sp<IInterface>& cameraCb, const String16& packageName,
- const std::optional<String16>& featureId, const String8& cameraId, int api1CameraId,
- int facing, int clientPid, uid_t clientUid, int servicePid, int halVersion,
- int deviceVersion, apiLevel effectiveApiLevel,
+ const std::optional<String16>& featureId, const String8& cameraId,
+ int api1CameraId, int facing, int sensorOrientation, int clientPid, uid_t clientUid,
+ int servicePid, int deviceVersion, apiLevel effectiveApiLevel,
/*out*/sp<BasicClient>* client) {
- if (halVersion < 0 || halVersion == deviceVersion) {
- // Default path: HAL version is unspecified by caller, create CameraClient
- // based on device version reported by the HAL.
- switch(deviceVersion) {
- 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, featureId,
- api1CameraId, facing, clientPid, clientUid,
- getpid());
- } else { // Camera2 API route
- ALOGW("Camera using old HAL version: %d", deviceVersion);
- return STATUS_ERROR_FMT(ERROR_DEPRECATED_HAL,
- "Camera device \"%s\" HAL version %d does not support camera2 API",
- cameraId.string(), deviceVersion);
- }
+ // Create CameraClient based on device version reported by the HAL.
+ switch(deviceVersion) {
+ case CAMERA_DEVICE_API_VERSION_1_0:
+ ALOGE("Camera using old HAL version: %d", deviceVersion);
+ return STATUS_ERROR_FMT(ERROR_DEPRECATED_HAL,
+ "Camera device \"%s\" HAL version %d no longer supported",
+ cameraId.string(), deviceVersion);
break;
- case CAMERA_DEVICE_API_VERSION_3_0:
- case CAMERA_DEVICE_API_VERSION_3_1:
- case CAMERA_DEVICE_API_VERSION_3_2:
- case CAMERA_DEVICE_API_VERSION_3_3:
- case CAMERA_DEVICE_API_VERSION_3_4:
- case CAMERA_DEVICE_API_VERSION_3_5:
- case CAMERA_DEVICE_API_VERSION_3_6:
+ case CAMERA_DEVICE_API_VERSION_3_0:
+ case CAMERA_DEVICE_API_VERSION_3_1:
+ case CAMERA_DEVICE_API_VERSION_3_2:
+ case CAMERA_DEVICE_API_VERSION_3_3:
+ case CAMERA_DEVICE_API_VERSION_3_4:
+ case CAMERA_DEVICE_API_VERSION_3_5:
+ case CAMERA_DEVICE_API_VERSION_3_6:
+ case CAMERA_DEVICE_API_VERSION_3_7:
if (effectiveApiLevel == API_1) { // Camera1 API route
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
*client = new Camera2Client(cameraService, tmp, packageName, featureId,
cameraId, api1CameraId,
- facing, clientPid, clientUid,
+ facing, sensorOrientation, 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, featureId,
- cameraId, facing, clientPid, clientUid, servicePid);
+ cameraId, facing, sensorOrientation, clientPid, clientUid, servicePid);
}
break;
- default:
+ default:
// Should not be reachable
ALOGE("Unknown camera device HAL version: %d", deviceVersion);
return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
"Camera device \"%s\" has unknown HAL version %d",
cameraId.string(), deviceVersion);
- }
- } else {
- // A particular HAL version is requested by caller. Create CameraClient
- // based on the requested HAL version.
- if (deviceVersion > CAMERA_DEVICE_API_VERSION_1_0 &&
- 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, featureId,
- api1CameraId, facing, clientPid, clientUid,
- servicePid);
- } else {
- // Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet.
- ALOGE("Invalid camera HAL version %x: HAL %x device can only be"
- " opened as HAL %x device", halVersion, deviceVersion,
- CAMERA_DEVICE_API_VERSION_1_0);
- return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT,
- "Camera device \"%s\" (HAL version %d) cannot be opened as HAL version %d",
- cameraId.string(), deviceVersion, halVersion);
- }
}
return Status::ok();
}
@@ -969,7 +944,6 @@
sp<Client> tmp = nullptr;
if (!(ret = connectHelper<ICameraClient,Client>(
sp<ICameraClient>{nullptr}, id, cameraId,
- static_cast<int>(CAMERA_HAL_API_VERSION_UNSPECIFIED),
internalPackageName, {}, uid, USE_CALLING_PID,
API_1, /*shimUpdateOnly*/ true, /*out*/ tmp)
).isOk()) {
@@ -1488,34 +1462,7 @@
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);
-
- if(!ret.isOk()) {
- logRejected(id, CameraThreadState::getCallingPid(), String8(clientPackageName),
- ret.toString8());
- return ret;
- }
-
- *device = client;
- return ret;
-}
-
-Status CameraService::connectLegacy(
- const sp<ICameraClient>& cameraClient,
- int api1CameraId, int halVersion,
- const String16& clientPackageName,
- int clientUid,
- /*out*/
- sp<ICamera>* device) {
-
- ATRACE_CALL();
- String8 id = cameraIdIntToStr(api1CameraId);
-
- Status ret = Status::ok();
- sp<Client> client = nullptr;
- ret = connectHelper<ICameraClient,Client>(cameraClient, id, api1CameraId, halVersion,
- clientPackageName, {}, clientUid, USE_CALLING_PID, API_1,
+ clientPackageName, {}, clientUid, clientPid, API_1,
/*shimUpdateOnly*/ false, /*out*/client);
if(!ret.isOk()) {
@@ -1557,8 +1504,9 @@
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;
+ // This isn't a known camera ID, so it's not a system camera
+ ALOGV("%s: Unknown camera id %s, ", __FUNCTION__, cameraId.c_str());
+ return false;
}
// (1) Cameraserver trying to connect, accept.
@@ -1607,8 +1555,7 @@
clientPackageNameAdj = String16(vendorClient.c_str());
}
ret = connectHelper<hardware::camera2::ICameraDeviceCallbacks,CameraDeviceClient>(cameraCb, id,
- /*api1CameraId*/-1,
- CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageNameAdj, clientFeatureId,
+ /*api1CameraId*/-1, clientPackageNameAdj, clientFeatureId,
clientUid, USE_CALLING_PID, API_2, /*shimUpdateOnly*/ false, /*out*/client);
if(!ret.isOk()) {
@@ -1623,7 +1570,7 @@
template<class CALLBACK, class CLIENT>
Status CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId,
- int api1CameraId, int halVersion, const String16& clientPackageName,
+ int api1CameraId, const String16& clientPackageName,
const std::optional<String16>& clientFeatureId, int clientUid, int clientPid,
apiLevel effectiveApiLevel, bool shimUpdateOnly,
/*out*/sp<CLIENT>& device) {
@@ -1633,12 +1580,16 @@
int originalClientPid = 0;
- ALOGI("CameraService::connect call (PID %d \"%s\", camera ID %s) for HAL version %s and "
+ ALOGI("CameraService::connect call (PID %d \"%s\", camera ID %s) and "
"Camera API version %d", clientPid, clientName8.string(), cameraId.string(),
- (halVersion == -1) ? "default" : std::to_string(halVersion).c_str(),
static_cast<int>(effectiveApiLevel));
+ nsecs_t openTimeNs = systemTime();
+
sp<CLIENT> client = nullptr;
+ int facing = -1;
+ int orientation = 0;
+ bool isNdk = (clientPackageName.size() == 0);
{
// Acquire mServiceLock and prevent other clients from connecting
std::unique_ptr<AutoConditionLock> lock =
@@ -1703,8 +1654,7 @@
// give flashlight a chance to close devices if necessary.
mFlashlight->prepareDeviceOpen(cameraId);
- int facing = -1;
- int deviceVersion = getDeviceVersion(cameraId, /*out*/&facing);
+ int deviceVersion = getDeviceVersion(cameraId, /*out*/&facing, /*out*/&orientation);
if (facing == -1) {
ALOGE("%s: Unable to get camera device \"%s\" facing", __FUNCTION__, cameraId.string());
return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
@@ -1713,9 +1663,9 @@
sp<BasicClient> tmp = nullptr;
if(!(ret = makeClient(this, cameraCb, clientPackageName, clientFeatureId,
- cameraId, api1CameraId, facing,
+ cameraId, api1CameraId, facing, orientation,
clientPid, clientUid, getpid(),
- halVersion, deviceVersion, effectiveApiLevel,
+ deviceVersion, effectiveApiLevel,
/*out*/&tmp)).isOk()) {
return ret;
}
@@ -1772,6 +1722,16 @@
// Set rotate-and-crop override behavior
if (mOverrideRotateAndCropMode != ANDROID_SCALER_ROTATE_AND_CROP_AUTO) {
client->setRotateAndCropOverride(mOverrideRotateAndCropMode);
+ } else if (CameraServiceProxyWrapper::isRotateAndCropOverrideNeeded(clientPackageName,
+ orientation, facing)) {
+ client->setRotateAndCropOverride(ANDROID_SCALER_ROTATE_AND_CROP_90);
+ }
+
+ // Set camera muting behavior
+ if (client->supportsCameraMute()) {
+ bool isCameraPrivacyEnabled =
+ mSensorPrivacyPolicy->isCameraPrivacyEnabled(multiuser_get_user_id(clientUid));
+ client->setCameraMute(mOverrideCameraMuteMode || isCameraPrivacyEnabled);
}
if (shimUpdateOnly) {
@@ -1783,11 +1743,18 @@
// Otherwise, add client to active clients list
finishConnectLocked(client, partial);
}
+
+ client->setImageDumpMask(mImageDumpMask);
} // lock is destroyed, allow further connect calls
// Important: release the mutex here so the client can call back into the service from its
// destructor (can be at the end of the call)
device = client;
+
+ int32_t openLatencyMs = ns2ms(systemTime() - openTimeNs);
+ CameraServiceProxyWrapper::logOpen(cameraId, facing, clientPackageName,
+ effectiveApiLevel, isNdk, openLatencyMs);
+
return ret;
}
@@ -1960,6 +1927,7 @@
errorCode = ERROR_INVALID_OPERATION;
}
ALOGE("%s: %s", __FUNCTION__, msg.string());
+ logServiceError(msg,errorCode);
return STATUS_ERROR(errorCode, msg.string());
}
@@ -2082,7 +2050,50 @@
return Status::ok();
}
- Status CameraService::getConcurrentCameraIds(
+Status CameraService::notifyDisplayConfigurationChange() {
+ ATRACE_CALL();
+ const int callingPid = CameraThreadState::getCallingPid();
+ const int selfPid = getpid();
+
+ // Permission checks
+ if (callingPid != selfPid) {
+ // Ensure we're being called by system_server, or similar process with
+ // permissions to notify the camera service about system events
+ if (!checkCallingPermission(sCameraSendSystemEventsPermission)) {
+ const int uid = CameraThreadState::getCallingUid();
+ ALOGE("Permission Denial: cannot send updates to camera service about orientation"
+ " changes from pid=%d, uid=%d", callingPid, uid);
+ return STATUS_ERROR_FMT(ERROR_PERMISSION_DENIED,
+ "No permission to send updates to camera service about orientation"
+ " changes from pid=%d, uid=%d", callingPid, uid);
+ }
+ }
+
+ Mutex::Autolock lock(mServiceLock);
+
+ // Don't do anything if rotate-and-crop override via cmd is active
+ if (mOverrideRotateAndCropMode != ANDROID_SCALER_ROTATE_AND_CROP_AUTO) return Status::ok();
+
+ const auto clients = mActiveClientManager.getAll();
+ for (auto& current : clients) {
+ if (current != nullptr) {
+ const auto basicClient = current->getValue();
+ if (basicClient.get() != nullptr) {
+ if (CameraServiceProxyWrapper::isRotateAndCropOverrideNeeded(
+ basicClient->getPackageName(), basicClient->getCameraOrientation(),
+ basicClient->getCameraFacing())) {
+ basicClient->setRotateAndCropOverride(ANDROID_SCALER_ROTATE_AND_CROP_90);
+ } else {
+ basicClient->setRotateAndCropOverride(ANDROID_SCALER_ROTATE_AND_CROP_NONE);
+ }
+ }
+ }
+ }
+
+ return Status::ok();
+}
+
+Status CameraService::getConcurrentCameraIds(
std::vector<ConcurrentCameraIdCombination>* concurrentCameraIds) {
ATRACE_CALL();
if (!concurrentCameraIds) {
@@ -2092,6 +2103,7 @@
if (!mInitialized) {
ALOGE("%s: Camera HAL couldn't be initialized", __FUNCTION__);
+ logServiceError(String8::format("Camera subsystem is not available"),ERROR_DISCONNECTED);
return STATUS_ERROR(ERROR_DISCONNECTED,
"Camera subsystem is not available");
}
@@ -2153,6 +2165,8 @@
mCameraProviderManager->isConcurrentSessionConfigurationSupported(
cameraIdsAndSessionConfigurations, isSupported);
if (res != OK) {
+ logServiceError(String8::format("Unable to query session configuration support"),
+ ERROR_INVALID_OPERATION);
return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION, "Unable to query session configuration "
"support %s (%d)", strerror(-res), res);
}
@@ -2165,10 +2179,15 @@
return addListenerHelper(listener, cameraStatuses);
}
+binder::Status CameraService::addListenerTest(const sp<hardware::ICameraServiceListener>& listener,
+ std::vector<hardware::CameraStatus>* cameraStatuses) {
+ return addListenerHelper(listener, cameraStatuses, false, true);
+}
+
Status CameraService::addListenerHelper(const sp<ICameraServiceListener>& listener,
/*out*/
std::vector<hardware::CameraStatus> *cameraStatuses,
- bool isVendorListener) {
+ bool isVendorListener, bool isProcessLocalTest) {
ATRACE_CALL();
@@ -2199,10 +2218,11 @@
sp<ServiceListener> serviceListener =
new ServiceListener(this, listener, clientUid, clientPid, isVendorListener,
openCloseCallbackAllowed);
- auto ret = serviceListener->initialize();
+ auto ret = serviceListener->initialize(isProcessLocalTest);
if (ret != NO_ERROR) {
String8 msg = String8::format("Failed to initialize service listener: %s (%d)",
strerror(-ret), ret);
+ logServiceError(msg,ERROR_ILLEGAL_ARGUMENT);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, msg.string());
}
@@ -2355,6 +2375,7 @@
case CAMERA_DEVICE_API_VERSION_3_4:
case CAMERA_DEVICE_API_VERSION_3_5:
case CAMERA_DEVICE_API_VERSION_3_6:
+ case CAMERA_DEVICE_API_VERSION_3_7:
ALOGV("%s: Camera id %s uses HAL3.2 or newer, supports api1/api2 directly",
__FUNCTION__, id.string());
*isSupported = true;
@@ -2387,6 +2408,42 @@
return Status::ok();
}
+Status CameraService::injectCamera(
+ const String16& packageName, const String16& internalCamId,
+ const String16& externalCamId,
+ const sp<ICameraInjectionCallback>& callback,
+ /*out*/
+ sp<hardware::camera2::ICameraInjectionSession>* cameraInjectionSession) {
+ ATRACE_CALL();
+
+ if (!checkCallingPermission(sCameraInjectExternalCameraPermission)) {
+ const int pid = CameraThreadState::getCallingPid();
+ const int uid = CameraThreadState::getCallingUid();
+ ALOGE("Permission Denial: can't inject camera pid=%d, uid=%d", pid, uid);
+ return STATUS_ERROR(ERROR_PERMISSION_DENIED,
+ "Permission Denial: no permission to inject camera");
+ }
+
+ ALOGV(
+ "%s: Package name = %s, Internal camera ID = %s, External camera ID = "
+ "%s",
+ __FUNCTION__, String8(packageName).string(),
+ String8(internalCamId).string(), String8(externalCamId).string());
+
+ binder::Status ret = binder::Status::ok();
+ // TODO: Implement the injection camera function.
+ // ret = internalInjectCamera(...);
+ // if(!ret.isOk()) {
+ // mInjectionStatusListener->notifyInjectionError(...);
+ // return ret;
+ // }
+
+ mInjectionStatusListener->addListener(callback);
+ *cameraInjectionSession = new CameraInjectionSession(this);
+
+ return ret;
+}
+
void CameraService::removeByClient(const BasicClient* client) {
Mutex::Autolock lock(mServiceLock);
for (auto& i : mActiveClientManager.getAll()) {
@@ -2550,7 +2607,15 @@
void CameraService::logEvent(const char* event) {
String8 curTime = getFormattedCurrentTime();
Mutex::Autolock l(mLogLock);
- mEventLog.add(String8::format("%s : %s", curTime.string(), event));
+ String8 msg = String8::format("%s : %s", curTime.string(), event);
+ // For service error events, print the msg only once.
+ if(!msg.contains("SERVICE ERROR")) {
+ mEventLog.add(msg);
+ } else if(sServiceErrorEventSet.find(msg) == sServiceErrorEventSet.end()) {
+ // Error event not added to the dumpsys log before
+ mEventLog.add(msg);
+ sServiceErrorEventSet.insert(msg);
+ }
}
void CameraService::logDisconnected(const char* cameraId, int clientPid,
@@ -2725,6 +2790,11 @@
ATRACE_CALL();
LOG1("playSound(%d)", kind);
+ if (kind < 0 || kind >= NUM_SOUNDS) {
+ ALOGE("%s: Invalid sound id requested: %d", __FUNCTION__, kind);
+ return;
+ }
+
Mutex::Autolock lock(mSoundLock);
loadSoundLocked(kind);
sp<MediaPlayer> player = mSoundPlayer[kind];
@@ -2741,13 +2811,13 @@
const String16& clientPackageName,
const std::optional<String16>& clientFeatureId,
const String8& cameraIdStr,
- int api1CameraId, int cameraFacing,
+ int api1CameraId, int cameraFacing, int sensorOrientation,
int clientPid, uid_t clientUid,
int servicePid) :
CameraService::BasicClient(cameraService,
IInterface::asBinder(cameraClient),
clientPackageName, clientFeatureId,
- cameraIdStr, cameraFacing,
+ cameraIdStr, cameraFacing, sensorOrientation,
clientPid, clientUid,
servicePid),
mCameraId(api1CameraId)
@@ -2777,22 +2847,23 @@
CameraService::BasicClient::BasicClient(const sp<CameraService>& cameraService,
const sp<IBinder>& remoteCallback,
const String16& clientPackageName, const std::optional<String16>& clientFeatureId,
- const String8& cameraIdStr, int cameraFacing,
+ const String8& cameraIdStr, int cameraFacing, int sensorOrientation,
int clientPid, uid_t clientUid,
int servicePid):
- mCameraIdStr(cameraIdStr), mCameraFacing(cameraFacing),
+ mDestructionStarted(false),
+ mCameraIdStr(cameraIdStr), mCameraFacing(cameraFacing), mOrientation(sensorOrientation),
mClientPackageName(clientPackageName), mClientFeatureId(clientFeatureId),
mClientPid(clientPid), mClientUid(clientUid),
mServicePid(servicePid),
mDisconnected(false), mUidIsTrusted(false),
mAudioRestriction(hardware::camera2::ICameraDeviceUser::AUDIO_RESTRICTION_NONE),
- mRemoteBinder(remoteCallback)
+ mRemoteBinder(remoteCallback),
+ mOpsActive(false),
+ mOpsStreaming(false)
{
if (sCameraService == nullptr) {
sCameraService = cameraService;
}
- mOpsActive = false;
- mDestructionStarted = false;
// In some cases the calling code has no access to the package it runs under.
// For example, NDK camera API.
@@ -2877,6 +2948,13 @@
return mClientPackageName;
}
+int CameraService::BasicClient::getCameraFacing() const {
+ return mCameraFacing;
+}
+
+int CameraService::BasicClient::getCameraOrientation() const {
+ return mOrientation;
+}
int CameraService::BasicClient::getClientPid() const {
return mClientPid;
@@ -2920,6 +2998,29 @@
}
}
+status_t CameraService::BasicClient::handleAppOpMode(int32_t mode) {
+ if (mode == AppOpsManager::MODE_ERRORED) {
+ ALOGI("Camera %s: Access for \"%s\" has been revoked",
+ mCameraIdStr.string(), String8(mClientPackageName).string());
+ return PERMISSION_DENIED;
+ } else if (!mUidIsTrusted && mode == AppOpsManager::MODE_IGNORED) {
+ // If the calling Uid is trusted (a native service), the AppOpsManager could
+ // return MODE_IGNORED. Do not treat such case as error.
+ bool isUidActive = sCameraService->mUidPolicy->isUidActive(mClientUid,
+ mClientPackageName);
+ bool isCameraPrivacyEnabled =
+ sCameraService->mSensorPrivacyPolicy->isCameraPrivacyEnabled(
+ multiuser_get_user_id(mClientUid));
+ if (!isUidActive || !isCameraPrivacyEnabled) {
+ ALOGI("Camera %s: Access for \"%s\" has been restricted",
+ mCameraIdStr.string(), String8(mClientPackageName).string());
+ // Return the same error as for device policy manager rejection
+ return -EACCES;
+ }
+ }
+ return OK;
+}
+
status_t CameraService::BasicClient::startCameraOps() {
ATRACE_CALL();
@@ -2930,26 +3031,16 @@
if (mAppOpsManager != nullptr) {
// Notify app ops that the camera is not available
mOpsCallback = new OpsCallback(this);
- int32_t res;
mAppOpsManager->startWatchingMode(AppOpsManager::OP_CAMERA,
mClientPackageName, mOpsCallback);
- 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",
- mCameraIdStr.string(), String8(mClientPackageName).string());
- return PERMISSION_DENIED;
- }
-
- // If the calling Uid is trusted (a native service), the AppOpsManager could
- // return MODE_IGNORED. Do not treat such case as error.
- if (!mUidIsTrusted && res == AppOpsManager::MODE_IGNORED) {
- ALOGI("Camera %s: Access for \"%s\" has been restricted",
- mCameraIdStr.string(), String8(mClientPackageName).string());
- // Return the same error as for device policy manager rejection
- return -EACCES;
+ // Just check for camera acccess here on open - delay startOp until
+ // camera frames start streaming in startCameraStreamingOps
+ int32_t mode = mAppOpsManager->checkOp(AppOpsManager::OP_CAMERA, mClientUid,
+ mClientPackageName);
+ status_t res = handleAppOpMode(mode);
+ if (res != OK) {
+ return res;
}
}
@@ -2958,14 +3049,6 @@
// Transition device availability listeners from PRESENT -> NOT_AVAILABLE
sCameraService->updateStatus(StatusInternal::NOT_AVAILABLE, mCameraIdStr);
- int apiLevel = hardware::ICameraServiceProxy::CAMERA_API_LEVEL_1;
- if (canCastToApiClient(API_2)) {
- apiLevel = hardware::ICameraServiceProxy::CAMERA_API_LEVEL_2;
- }
- // Transition device state to OPEN
- sCameraService->updateProxyDeviceState(ICameraServiceProxy::CAMERA_STATE_OPEN,
- mCameraIdStr, mCameraFacing, mClientPackageName, apiLevel);
-
sCameraService->mUidPolicy->registerMonitorUid(mClientUid);
// Notify listeners of camera open/close status
@@ -2974,17 +3057,69 @@
return OK;
}
+status_t CameraService::BasicClient::startCameraStreamingOps() {
+ ATRACE_CALL();
+
+ if (!mOpsActive) {
+ ALOGE("%s: Calling streaming start when not yet active", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ if (mOpsStreaming) {
+ ALOGV("%s: Streaming already active!", __FUNCTION__);
+ return OK;
+ }
+
+ ALOGV("%s: Start camera streaming ops, package name = %s, client UID = %d",
+ __FUNCTION__, String8(mClientPackageName).string(), mClientUid);
+
+ if (mAppOpsManager != nullptr) {
+ int32_t mode = mAppOpsManager->startOpNoThrow(AppOpsManager::OP_CAMERA, mClientUid,
+ mClientPackageName, /*startIfModeDefault*/ false, mClientFeatureId,
+ String16("start camera ") + String16(mCameraIdStr));
+ status_t res = handleAppOpMode(mode);
+ if (res != OK) {
+ return res;
+ }
+ }
+
+ mOpsStreaming = true;
+
+ return OK;
+}
+
+status_t CameraService::BasicClient::finishCameraStreamingOps() {
+ ATRACE_CALL();
+
+ if (!mOpsActive) {
+ ALOGE("%s: Calling streaming start when not yet active", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ if (!mOpsStreaming) {
+ ALOGV("%s: Streaming not active!", __FUNCTION__);
+ return OK;
+ }
+
+ if (mAppOpsManager != nullptr) {
+ mAppOpsManager->finishOp(AppOpsManager::OP_CAMERA, mClientUid,
+ mClientPackageName, mClientFeatureId);
+ mOpsStreaming = false;
+ }
+
+ return OK;
+}
+
status_t CameraService::BasicClient::finishCameraOps() {
ATRACE_CALL();
+ if (mOpsStreaming) {
+ // Make sure we've notified everyone about camera stopping
+ finishCameraStreamingOps();
+ }
+
// Check if startCameraOps succeeded, and if so, finish the camera op
if (mOpsActive) {
- // Notify app ops that the camera is available again
- if (mAppOpsManager != nullptr) {
- mAppOpsManager->finishOp(AppOpsManager::OP_CAMERA, mClientUid,
- mClientPackageName, mClientFeatureId);
- mOpsActive = false;
- }
+ mOpsActive = false;
+
// This function is called when a client disconnects. This should
// release the camera, but actually only if it was in a proper
// functional state, i.e. with status NOT_AVAILABLE
@@ -2994,14 +3129,6 @@
// Transition to PRESENT if the camera is not in either of the rejected states
sCameraService->updateStatus(StatusInternal::PRESENT,
mCameraIdStr, rejected);
-
- int apiLevel = hardware::ICameraServiceProxy::CAMERA_API_LEVEL_1;
- if (canCastToApiClient(API_2)) {
- apiLevel = hardware::ICameraServiceProxy::CAMERA_API_LEVEL_2;
- }
- // Transition device state to CLOSED
- sCameraService->updateProxyDeviceState(ICameraServiceProxy::CAMERA_STATE_CLOSED,
- mCameraIdStr, mCameraFacing, mClientPackageName, apiLevel);
}
// Always stop watching, even if no camera op is active
if (mOpsCallback != nullptr && mAppOpsManager != nullptr) {
@@ -3037,10 +3164,28 @@
res == AppOpsManager::MODE_ERRORED ? "ERRORED" :
"UNKNOWN");
- if (res != AppOpsManager::MODE_ALLOWED) {
+ if (res == AppOpsManager::MODE_ERRORED) {
ALOGI("Camera %s: Access for \"%s\" revoked", mCameraIdStr.string(),
String8(mClientPackageName).string());
block();
+ } else if (res == AppOpsManager::MODE_IGNORED) {
+ bool isUidActive = sCameraService->mUidPolicy->isUidActive(mClientUid, mClientPackageName);
+ bool isCameraPrivacyEnabled =
+ sCameraService->mSensorPrivacyPolicy->isCameraPrivacyEnabled(
+ multiuser_get_user_id(mClientUid));
+ ALOGI("Camera %s: Access for \"%s\" has been restricted, isUidTrusted %d, isUidActive %d",
+ mCameraIdStr.string(), String8(mClientPackageName).string(),
+ mUidIsTrusted, isUidActive);
+ // If the calling Uid is trusted (a native service), or the client Uid is active (WAR for
+ // b/175320666), the AppOpsManager could return MODE_IGNORED. Do not treat such cases as
+ // error.
+ if (!mUidIsTrusted && isUidActive && isCameraPrivacyEnabled) {
+ setCameraMute(true);
+ } else if (!mUidIsTrusted && !isUidActive) {
+ block();
+ }
+ } else if (res == AppOpsManager::MODE_ALLOWED) {
+ setCameraMute(sCameraService->mOverrideCameraMuteMode);
}
}
@@ -3313,6 +3458,7 @@
if (mRegistered) {
return;
}
+ hasCameraPrivacyFeature(); // Called so the result is cached
mSpm.addSensorPrivacyListener(this);
mSensorPrivacyEnabled = mSpm.isSensorPrivacyEnabled();
status_t res = mSpm.linkToDeath(this);
@@ -3335,6 +3481,14 @@
return mSensorPrivacyEnabled;
}
+bool CameraService::SensorPrivacyPolicy::isCameraPrivacyEnabled(userid_t userId) {
+ if (!hasCameraPrivacyFeature()) {
+ return false;
+ }
+ return mSpm.isIndividualSensorPrivacyEnabled(userId,
+ SensorPrivacyManager::INDIVIDUAL_SENSOR_CAMERA);
+}
+
binder::Status CameraService::SensorPrivacyPolicy::onSensorPrivacyChanged(bool enabled) {
{
Mutex::Autolock _l(mSensorPrivacyLock);
@@ -3356,6 +3510,10 @@
mRegistered = false;
}
+bool CameraService::SensorPrivacyPolicy::hasCameraPrivacyFeature() {
+ return mSpm.supportsSensorToggle(SensorPrivacyManager::INDIVIDUAL_SENSOR_CAMERA);
+}
+
// ----------------------------------------------------------------------------
// CameraState
// ----------------------------------------------------------------------------
@@ -3522,6 +3680,66 @@
}
// ----------------------------------------------------------------------------
+// InjectionStatusListener
+// ----------------------------------------------------------------------------
+
+void CameraService::InjectionStatusListener::addListener(
+ const sp<ICameraInjectionCallback>& callback) {
+ Mutex::Autolock lock(mListenerLock);
+ if (mCameraInjectionCallback) return;
+ status_t res = IInterface::asBinder(callback)->linkToDeath(this);
+ if (res == OK) {
+ mCameraInjectionCallback = callback;
+ }
+}
+
+void CameraService::InjectionStatusListener::removeListener() {
+ Mutex::Autolock lock(mListenerLock);
+ if (mCameraInjectionCallback == nullptr) {
+ ALOGW("InjectionStatusListener: mCameraInjectionCallback == nullptr");
+ return;
+ }
+ IInterface::asBinder(mCameraInjectionCallback)->unlinkToDeath(this);
+ mCameraInjectionCallback = nullptr;
+}
+
+void CameraService::InjectionStatusListener::notifyInjectionError(
+ int errorCode) {
+ Mutex::Autolock lock(mListenerLock);
+ if (mCameraInjectionCallback == nullptr) {
+ ALOGW("InjectionStatusListener: mCameraInjectionCallback == nullptr");
+ return;
+ }
+ mCameraInjectionCallback->onInjectionError(errorCode);
+}
+
+void CameraService::InjectionStatusListener::binderDied(
+ const wp<IBinder>& /*who*/) {
+ Mutex::Autolock lock(mListenerLock);
+ ALOGV("InjectionStatusListener: ICameraInjectionCallback has died");
+ auto parent = mParent.promote();
+ if (parent != nullptr) {
+ parent->stopInjectionImpl();
+ }
+}
+
+// ----------------------------------------------------------------------------
+// CameraInjectionSession
+// ----------------------------------------------------------------------------
+
+binder::Status CameraService::CameraInjectionSession::stopInjection() {
+ Mutex::Autolock lock(mInjectionSessionLock);
+ auto parent = mParent.promote();
+ if (parent == nullptr) {
+ ALOGE("CameraInjectionSession: Parent is gone");
+ return STATUS_ERROR(ICameraInjectionCallback::ERROR_INJECTION_SERVICE,
+ "Camera service encountered error");
+ }
+ parent->stopInjectionImpl();
+ return binder::Status::ok();
+}
+
+// ----------------------------------------------------------------------------
static const int kDumpLockRetries = 50;
static const int kDumpLockSleep = 60000;
@@ -3881,14 +4099,6 @@
onStatusUpdatedLocked(cameraId, status);
}
-void CameraService::updateProxyDeviceState(int newState,
- const String8& cameraId, int facing, const String16& clientName, int apiLevel) {
- sp<ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
- if (proxyBinder == nullptr) return;
- String16 id(cameraId);
- proxyBinder->notifyCameraState(id, newState, facing, clientName, apiLevel);
-}
-
status_t CameraService::getTorchStatusLocked(
const String8& cameraId,
TorchModeStatus *status) const {
@@ -4001,6 +4211,12 @@
return handleSetRotateAndCrop(args);
} else if (args.size() >= 1 && args[0] == String16("get-rotate-and-crop")) {
return handleGetRotateAndCrop(out);
+ } else if (args.size() >= 2 && args[0] == String16("set-image-dump-mask")) {
+ return handleSetImageDumpMask(args);
+ } else if (args.size() >= 1 && args[0] == String16("get-image-dump-mask")) {
+ return handleGetImageDumpMask(out);
+ } else if (args.size() >= 2 && args[0] == String16("set-camera-mute")) {
+ return handleSetCameraMute(args);
} else if (args.size() == 1 && args[0] == String16("help")) {
printHelp(out);
return NO_ERROR;
@@ -4100,6 +4316,53 @@
return dprintf(out, "rotateAndCrop override: %d\n", mOverrideRotateAndCropMode);
}
+status_t CameraService::handleSetImageDumpMask(const Vector<String16>& args) {
+ char *endPtr;
+ errno = 0;
+ String8 maskString8 = String8(args[1]);
+ long maskValue = strtol(maskString8.c_str(), &endPtr, 10);
+
+ if (errno != 0) return BAD_VALUE;
+ if (endPtr != maskString8.c_str() + maskString8.size()) return BAD_VALUE;
+ if (maskValue < 0 || maskValue > 1) return BAD_VALUE;
+
+ Mutex::Autolock lock(mServiceLock);
+
+ mImageDumpMask = maskValue;
+
+ return OK;
+}
+
+status_t CameraService::handleGetImageDumpMask(int out) {
+ Mutex::Autolock lock(mServiceLock);
+
+ return dprintf(out, "Image dump mask: %d\n", mImageDumpMask);
+}
+
+status_t CameraService::handleSetCameraMute(const Vector<String16>& args) {
+ int muteValue = strtol(String8(args[1]), nullptr, 10);
+ if (errno != 0) return BAD_VALUE;
+
+ if (muteValue < 0 || muteValue > 1) return BAD_VALUE;
+ Mutex::Autolock lock(mServiceLock);
+
+ mOverrideCameraMuteMode = (muteValue == 1);
+
+ const auto clients = mActiveClientManager.getAll();
+ for (auto& current : clients) {
+ if (current != nullptr) {
+ const auto basicClient = current->getValue();
+ if (basicClient.get() != nullptr) {
+ if (basicClient->supportsCameraMute()) {
+ basicClient->setCameraMute(mOverrideCameraMuteMode);
+ }
+ }
+ }
+ }
+
+ return OK;
+}
+
status_t CameraService::printHelp(int out) {
return dprintf(out, "Camera service commands:\n"
" get-uid-state <PACKAGE> [--user USER_ID] gets the uid state\n"
@@ -4108,6 +4371,10 @@
" set-rotate-and-crop <ROTATION> overrides the rotate-and-crop value for AUTO backcompat\n"
" Valid values 0=0 deg, 1=90 deg, 2=180 deg, 3=270 deg, 4=No override\n"
" get-rotate-and-crop returns the current override rotate-and-crop value\n"
+ " set-image-dump-mask <MASK> specifies the formats to be saved to disk\n"
+ " Valid values 0=OFF, 1=ON for JPEG\n"
+ " get-image-dump-mask returns the current image-dump-mask value\n"
+ " set-camera-mute <0/1> enable or disable camera muting\n"
" help print this message\n");
}
@@ -4132,4 +4399,10 @@
return mode;
}
+void CameraService::stopInjectionImpl() {
+ mInjectionStatusListener->removeListener();
+
+ // TODO: Implement the stop injection function.
+}
+
}; // namespace android
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 2853b0c..10e1748 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -20,7 +20,8 @@
#include <android/hardware/BnCameraService.h>
#include <android/hardware/BnSensorPrivacyListener.h>
#include <android/hardware/ICameraServiceListener.h>
-#include <android/hardware/ICameraServiceProxy.h>
+#include <android/hardware/camera2/BnCameraInjectionSession.h>
+#include <android/hardware/camera2/ICameraInjectionCallback.h>
#include <cutils/multiuser.h>
#include <utils/Vector.h>
@@ -71,7 +72,6 @@
public virtual CameraProviderManager::StatusListener
{
friend class BinderService<CameraService>;
- friend class CameraClient;
friend class CameraOfflineSessionClient;
public:
class Client;
@@ -135,12 +135,6 @@
/*out*/
sp<hardware::ICamera>* device);
- virtual binder::Status connectLegacy(const sp<hardware::ICameraClient>& cameraClient,
- int32_t cameraId, int32_t halVersion,
- const String16& clientPackageName, int32_t clientUid,
- /*out*/
- sp<hardware::ICamera>* device);
-
virtual binder::Status connectDevice(
const sp<hardware::camera2::ICameraDeviceCallbacks>& cameraCb, const String16& cameraId,
const String16& clientPackageName, const std::optional<String16>& clientFeatureId,
@@ -175,6 +169,8 @@
virtual binder::Status notifyDeviceStateChange(int64_t newState);
+ virtual binder::Status notifyDisplayConfigurationChange();
+
// OK = supports api of that version, -EOPNOTSUPP = does not support
virtual binder::Status supportsCameraApi(
const String16& cameraId, int32_t apiVersion,
@@ -186,6 +182,13 @@
/*out*/
bool *isSupported);
+ virtual binder::Status injectCamera(
+ const String16& packageName, const String16& internalCamId,
+ const String16& externalCamId,
+ const sp<hardware::camera2::ICameraInjectionCallback>& callback,
+ /*out*/
+ sp<hardware::camera2::ICameraInjectionSession>* cameraInjectionSession);
+
// Extra permissions checks
virtual status_t onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags);
@@ -196,7 +199,8 @@
binder::Status addListenerHelper(const sp<hardware::ICameraServiceListener>& listener,
/*out*/
- std::vector<hardware::CameraStatus>* cameraStatuses, bool isVendor = false);
+ std::vector<hardware::CameraStatus>* cameraStatuses, bool isVendor = false,
+ bool isProcessLocalTest = false);
// Monitored UIDs availability notification
void notifyMonitoredUids();
@@ -218,20 +222,24 @@
void loadSoundLocked(sound_kind kind);
void decreaseSoundRef();
void increaseSoundRef();
- /**
- * Update the state of a given camera device (open/close/active/idle) with
- * the camera proxy service in the system service
- */
- static void updateProxyDeviceState(
- int newState,
- const String8& cameraId,
- int facing,
- const String16& clientName,
- int apiLevel);
/////////////////////////////////////////////////////////////////////
// CameraDeviceFactory functionality
- int getDeviceVersion(const String8& cameraId, int* facing = NULL);
+ int getDeviceVersion(const String8& cameraId, int* facing = nullptr,
+ int* orientation = nullptr);
+
+ /////////////////////////////////////////////////////////////////////
+ // Methods to be used in CameraService class tests only
+ //
+ // CameraService class test method only - clear static variables in the
+ // cameraserver process, which otherwise might affect multiple test runs.
+ void clearCachedVariables();
+
+ // Add test listener, linkToDeath won't be called since this is for process
+ // local testing.
+ binder::Status addListenerTest(const sp<hardware::ICameraServiceListener>& listener,
+ /*out*/
+ std::vector<hardware::CameraStatus>* cameraStatuses);
/////////////////////////////////////////////////////////////////////
// Shared utilities
@@ -241,6 +249,7 @@
// CameraClient functionality
class BasicClient : public virtual RefBase {
+ friend class CameraService;
public:
virtual status_t initialize(sp<CameraProviderManager> manager,
const String8& monitorTags) = 0;
@@ -263,6 +272,12 @@
// Return the package name for this client
virtual String16 getPackageName() const;
+ // Return the camera facing for this client
+ virtual int getCameraFacing() const;
+
+ // Return the camera orientation for this client
+ virtual int getCameraOrientation() const;
+
// Notify client about a fatal error
virtual void notifyError(int32_t errorCode,
const CaptureResultExtras& resultExtras) = 0;
@@ -296,6 +311,12 @@
// Override rotate-and-crop AUTO behavior
virtual status_t setRotateAndCropOverride(uint8_t rotateAndCrop) = 0;
+ // Whether the client supports camera muting (black only output)
+ virtual bool supportsCameraMute() = 0;
+
+ // Set/reset camera mute
+ virtual status_t setCameraMute(bool enabled) = 0;
+
protected:
BasicClient(const sp<CameraService>& cameraService,
const sp<IBinder>& remoteCallback,
@@ -303,6 +324,7 @@
const std::optional<String16>& clientFeatureId,
const String8& cameraIdStr,
int cameraFacing,
+ int sensorOrientation,
int clientPid,
uid_t clientUid,
int servicePid);
@@ -319,6 +341,7 @@
static sp<CameraService> sCameraService;
const String8 mCameraIdStr;
const int mCameraFacing;
+ const int mOrientation;
String16 mClientPackageName;
std::optional<String16> mClientFeatureId;
pid_t mClientPid;
@@ -333,9 +356,18 @@
// - The app-side Binder interface to receive callbacks from us
sp<IBinder> mRemoteBinder; // immutable after constructor
- // permissions management
+ // Permissions management methods for camera lifecycle
+
+ // Notify rest of system/apps about camera opening, and check appops
virtual status_t startCameraOps();
+ // Notify rest of system/apps about camera starting to stream data, and confirm appops
+ virtual status_t startCameraStreamingOps();
+ // Notify rest of system/apps about camera stopping streaming data
+ virtual status_t finishCameraStreamingOps();
+ // Notify rest of system/apps about camera closing
virtual status_t finishCameraOps();
+ // Handle errors for start/checkOps
+ virtual status_t handleAppOpMode(int32_t mode);
std::unique_ptr<AppOpsManager> mAppOpsManager = nullptr;
@@ -350,9 +382,12 @@
}; // class OpsCallback
sp<OpsCallback> mOpsCallback;
- // Track whether startCameraOps was called successfully, to avoid
- // finishing what we didn't start.
+ // Track whether checkOps was called successfully, to avoid
+ // finishing what we didn't start, on camera open.
bool mOpsActive;
+ // Track whether startOps was called successfully on start of
+ // camera streaming.
+ bool mOpsStreaming;
// IAppOpsCallback interface, indirected through opListener
virtual void opChanged(int32_t op, const String16& packageName);
@@ -396,6 +431,7 @@
const String8& cameraIdStr,
int api1CameraId,
int cameraFacing,
+ int sensorOrientation,
int clientPid,
uid_t clientUid,
int servicePid);
@@ -416,6 +452,8 @@
// Check what API level is used for this client. This is used to determine which
// superclass this can be cast to.
virtual bool canCastToApiClient(apiLevel level) const;
+
+ void setImageDumpMask(int /*mask*/) { }
protected:
// Initialized in constructor
@@ -653,6 +691,7 @@
void unregisterSelf();
bool isSensorPrivacyEnabled();
+ bool isCameraPrivacyEnabled(userid_t userId);
binder::Status onSensorPrivacyChanged(bool enabled);
@@ -665,6 +704,8 @@
Mutex mSensorPrivacyLock;
bool mSensorPrivacyEnabled;
bool mRegistered;
+
+ bool hasCameraPrivacyFeature();
};
sp<UidPolicy> mUidPolicy;
@@ -729,7 +770,7 @@
// 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 api1CameraId, const String16& clientPackageName,
const std::optional<String16>& clientFeatureId, int clientUid, int clientPid,
apiLevel effectiveApiLevel, bool shimUpdateOnly, /*out*/sp<CLIENT>& device);
@@ -920,7 +961,10 @@
mIsVendorListener(isVendorClient),
mOpenCloseCallbackAllowed(openCloseCallbackAllowed) { }
- status_t initialize() {
+ status_t initialize(bool isProcessLocalTest) {
+ if (isProcessLocalTest) {
+ return OK;
+ }
return IInterface::asBinder(mListener)->linkToDeath(this);
}
@@ -1055,6 +1099,15 @@
// Get the rotate-and-crop AUTO override behavior
status_t handleGetRotateAndCrop(int out);
+ // Set the mask for image dump to disk
+ status_t handleSetImageDumpMask(const Vector<String16>& args);
+
+ // Get the mask for image dump to disk
+ status_t handleGetImageDumpMask(int out);
+
+ // Set the camera mute state
+ status_t handleSetCameraMute(const Vector<String16>& args);
+
// Prints the shell command help
status_t printHelp(int out);
@@ -1066,7 +1119,7 @@
static binder::Status makeClient(const sp<CameraService>& cameraService,
const sp<IInterface>& cameraCb, const String16& packageName,
const std::optional<String16>& featureId, const String8& cameraId, int api1CameraId,
- int facing, int clientPid, uid_t clientUid, int servicePid, int halVersion,
+ int facing, int sensorOrientation, int clientPid, uid_t clientUid, int servicePid,
int deviceVersion, apiLevel effectiveApiLevel,
/*out*/sp<BasicClient>* client);
@@ -1077,13 +1130,6 @@
static StatusInternal mapToInternal(hardware::camera::common::V1_0::CameraDeviceStatus status);
static int32_t mapToInterface(StatusInternal status);
- // Guard mCameraServiceProxy
- static Mutex sProxyMutex;
- // Cached interface to the camera service proxy in system service
- static sp<hardware::ICameraServiceProxy> sCameraServiceProxy;
-
- static sp<hardware::ICameraServiceProxy> getCameraServiceProxy();
- static void pingCameraServiceProxy();
void broadcastTorchModeStatus(const String8& cameraId,
hardware::camera::common::V1_0::TorchModeStatus status,
@@ -1102,8 +1148,54 @@
// Aggreated audio restriction mode for all camera clients
int32_t mAudioRestriction;
- // Current override rotate-and-crop mode
+ // Current override cmd rotate-and-crop mode; AUTO means no override
uint8_t mOverrideRotateAndCropMode = ANDROID_SCALER_ROTATE_AND_CROP_AUTO;
+
+ // Current image dump mask
+ uint8_t mImageDumpMask = 0;
+
+ // Current camera mute mode
+ bool mOverrideCameraMuteMode = false;
+
+ /**
+ * A listener class that implements the IBinder::DeathRecipient interface
+ * for use to call back the error state injected by the external camera, and
+ * camera service can kill the injection when binder signals process death.
+ */
+ class InjectionStatusListener : public virtual IBinder::DeathRecipient {
+ public:
+ InjectionStatusListener(sp<CameraService> parent) : mParent(parent) {}
+
+ void addListener(const sp<hardware::camera2::ICameraInjectionCallback>& callback);
+ void removeListener();
+ void notifyInjectionError(int errorCode);
+
+ // IBinder::DeathRecipient implementation
+ virtual void binderDied(const wp<IBinder>& who);
+
+ private:
+ Mutex mListenerLock;
+ wp<CameraService> mParent;
+ sp<hardware::camera2::ICameraInjectionCallback> mCameraInjectionCallback;
+ };
+
+ sp<InjectionStatusListener> mInjectionStatusListener;
+
+ /**
+ * A class that implements the hardware::camera2::BnCameraInjectionSession interface
+ */
+ class CameraInjectionSession : public hardware::camera2::BnCameraInjectionSession {
+ public:
+ CameraInjectionSession(sp<CameraService> parent) : mParent(parent) {}
+ virtual ~CameraInjectionSession() {}
+ binder::Status stopInjection() override;
+
+ private:
+ Mutex mInjectionSessionLock;
+ wp<CameraService> mParent;
+ };
+
+ void stopInjectionImpl();
};
} // namespace android
diff --git a/services/camera/libcameraservice/TEST_MAPPING b/services/camera/libcameraservice/TEST_MAPPING
index 6fdac68..ca6cc58 100644
--- a/services/camera/libcameraservice/TEST_MAPPING
+++ b/services/camera/libcameraservice/TEST_MAPPING
@@ -1,7 +1,12 @@
{
"presubmit": [
{
- "name": "cameraservice_test"
+ "name": "cameraservice_test"
+ }
+ ],
+ "imports": [
+ {
+ "path": "frameworks/av/camera"
}
]
}
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 09e2c3f..72b3c40 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -34,6 +34,7 @@
#include "api1/client2/CallbackProcessor.h"
#include "api1/client2/ZslProcessor.h"
#include "utils/CameraThreadState.h"
+#include "utils/CameraServiceProxyWrapper.h"
#define ALOG1(...) ALOGD_IF(gLogLevel >= 1, __VA_ARGS__);
#define ALOG2(...) ALOGD_IF(gLogLevel >= 2, __VA_ARGS__);
@@ -54,11 +55,12 @@
const String8& cameraDeviceId,
int api1CameraId,
int cameraFacing,
+ int sensorOrientation,
int clientPid,
uid_t clientUid,
int servicePid):
Camera2ClientBase(cameraService, cameraClient, clientPackageName, clientFeatureId,
- cameraDeviceId, api1CameraId, cameraFacing,
+ cameraDeviceId, api1CameraId, cameraFacing, sensorOrientation,
clientPid, clientUid, servicePid),
mParameters(api1CameraId, cameraFacing)
{
@@ -75,7 +77,8 @@
bool Camera2Client::isZslEnabledInStillTemplate() {
bool zslEnabled = false;
CameraMetadata stillTemplate;
- status_t res = mDevice->createDefaultRequest(CAMERA2_TEMPLATE_STILL_CAPTURE, &stillTemplate);
+ status_t res = mDevice->createDefaultRequest(
+ camera_request_template_t::CAMERA_TEMPLATE_STILL_CAPTURE, &stillTemplate);
if (res == OK) {
camera_metadata_entry_t enableZsl = stillTemplate.find(ANDROID_CONTROL_ENABLE_ZSL);
if (enableZsl.count == 1) {
@@ -396,6 +399,7 @@
binder::Status Camera2Client::disconnect() {
ATRACE_CALL();
+ nsecs_t startTime = systemTime();
Mutex::Autolock icl(mBinderSerializationLock);
binder::Status res = binder::Status::ok();
@@ -457,6 +461,9 @@
CameraService::Client::disconnect();
+ int32_t closeLatencyMs = ns2ms(systemTime() - startTime);
+ CameraServiceProxyWrapper::logClose(mCameraIdStr, closeLatencyMs);
+
return res;
}
@@ -929,6 +936,7 @@
return res;
}
+ mCallbackProcessor->unpauseCallback();
params.state = Parameters::PREVIEW;
return OK;
}
@@ -963,6 +971,7 @@
FALLTHROUGH_INTENDED;
case Parameters::RECORD:
case Parameters::PREVIEW:
+ mCallbackProcessor->pauseCallback();
syncWithDevice();
// Due to flush a camera device sync is not a sufficient
// guarantee that the current client parameters are
@@ -2290,6 +2299,14 @@
static_cast<camera_metadata_enum_android_scaler_rotate_and_crop_t>(rotateAndCrop));
}
+bool Camera2Client::supportsCameraMute() {
+ return mDevice->supportsCameraMute();
+}
+
+status_t Camera2Client::setCameraMute(bool enabled) {
+ return mDevice->setCameraMute(enabled);
+}
+
status_t Camera2Client::waitUntilCurrentRequestIdLocked() {
int32_t activeRequestId = mStreamingProcessor->getActiveRequestId();
if (activeRequestId != 0) {
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index f8da0b6..d16b242 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -87,6 +87,9 @@
virtual int32_t getGlobalAudioRestriction();
virtual status_t setRotateAndCropOverride(uint8_t rotateAndCrop);
+ virtual bool supportsCameraMute();
+ virtual status_t setCameraMute(bool enabled);
+
/**
* Interface used by CameraService
*/
@@ -98,6 +101,7 @@
const String8& cameraDeviceId,
int api1CameraId,
int cameraFacing,
+ int sensorOrientation,
int clientPid,
uid_t clientUid,
int servicePid);
diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp
deleted file mode 100644
index b860ceb..0000000
--- a/services/camera/libcameraservice/api1/CameraClient.cpp
+++ /dev/null
@@ -1,1208 +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_TAG "CameraClient"
-//#define LOG_NDEBUG 0
-
-#include <cutils/atomic.h>
-#include <cutils/properties.h>
-#include <gui/Surface.h>
-#include <media/hardware/HardwareAPI.h>
-
-#include "api1/CameraClient.h"
-#include "device1/CameraHardwareInterface.h"
-#include "CameraService.h"
-#include "utils/CameraThreadState.h"
-
-namespace android {
-
-#define LOG1(...) ALOGD_IF(gLogLevel >= 1, __VA_ARGS__);
-#define LOG2(...) ALOGD_IF(gLogLevel >= 2, __VA_ARGS__);
-
-CameraClient::CameraClient(const sp<CameraService>& cameraService,
- const sp<hardware::ICameraClient>& cameraClient,
- const String16& clientPackageName, const std::optional<String16>& clientFeatureId,
- int cameraId, int cameraFacing,
- int clientPid, int clientUid,
- int servicePid):
- Client(cameraService, cameraClient, clientPackageName, clientFeatureId,
- String8::format("%d", cameraId), cameraId, cameraFacing, clientPid,
- clientUid, servicePid)
-{
- int callingPid = CameraThreadState::getCallingPid();
- LOG1("CameraClient::CameraClient E (pid %d, id %d)", callingPid, cameraId);
-
- mHardware = NULL;
- mMsgEnabled = 0;
- mSurface = 0;
- mPreviewWindow = 0;
- mDestructionStarted = false;
-
- // Callback is disabled by default
- mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP;
- mOrientation = getOrientation(0, mCameraFacing == CAMERA_FACING_FRONT);
- mPlayShutterSound = true;
- LOG1("CameraClient::CameraClient X (pid %d, id %d)", callingPid, cameraId);
-}
-
-status_t CameraClient::initialize(sp<CameraProviderManager> manager,
- const String8& /*monitorTags*/) {
- int callingPid = CameraThreadState::getCallingPid();
- status_t res;
-
- LOG1("CameraClient::initialize E (pid %d, id %d)", callingPid, mCameraId);
-
- // Verify ops permissions
- res = startCameraOps();
- if (res != OK) {
- return res;
- }
-
- char camera_device_name[10];
- snprintf(camera_device_name, sizeof(camera_device_name), "%d", mCameraId);
-
- mHardware = new CameraHardwareInterface(camera_device_name);
- res = mHardware->initialize(manager);
- if (res != OK) {
- ALOGE("%s: Camera %d: unable to initialize device: %s (%d)",
- __FUNCTION__, mCameraId, strerror(-res), res);
- mHardware.clear();
- return res;
- }
-
- mHardware->setCallbacks(notifyCallback,
- dataCallback,
- dataCallbackTimestamp,
- handleCallbackTimestampBatch,
- (void *)(uintptr_t)mCameraId);
-
- // Enable zoom, error, focus, and metadata messages by default
- enableMsgType(CAMERA_MSG_ERROR | CAMERA_MSG_ZOOM | CAMERA_MSG_FOCUS |
- CAMERA_MSG_PREVIEW_METADATA | CAMERA_MSG_FOCUS_MOVE);
-
- LOG1("CameraClient::initialize X (pid %d, id %d)", callingPid, mCameraId);
- return OK;
-}
-
-
-// tear down the client
-CameraClient::~CameraClient() {
- mDestructionStarted = true;
- int callingPid = CameraThreadState::getCallingPid();
- LOG1("CameraClient::~CameraClient E (pid %d, this %p)", callingPid, this);
-
- disconnect();
- LOG1("CameraClient::~CameraClient X (pid %d, this %p)", callingPid, this);
-}
-
-status_t CameraClient::dump(int fd, const Vector<String16>& args) {
- return BasicClient::dump(fd, args);
-}
-
-status_t CameraClient::dumpClient(int fd, const Vector<String16>& args) {
- const size_t SIZE = 256;
- char buffer[SIZE];
-
- size_t len = snprintf(buffer, SIZE, "Client[%d] (%p) with UID %d\n",
- mCameraId,
- (getRemoteCallback() != NULL ?
- IInterface::asBinder(getRemoteCallback()).get() : NULL),
- mClientUid);
- len = (len > SIZE - 1) ? SIZE - 1 : len;
- write(fd, buffer, len);
-
- len = snprintf(buffer, SIZE, "Latest set parameters:\n");
- len = (len > SIZE - 1) ? SIZE - 1 : len;
- write(fd, buffer, len);
-
- mLatestSetParameters.dump(fd, args);
-
- const char *enddump = "\n\n";
- write(fd, enddump, strlen(enddump));
-
- sp<CameraHardwareInterface> hardware = mHardware;
- if (hardware != nullptr) {
- return hardware->dump(fd, args);
- }
- ALOGI("%s: camera device closed already, skip dumping", __FUNCTION__);
- return OK;
-}
-
-// ----------------------------------------------------------------------------
-
-status_t CameraClient::checkPid() const {
- int callingPid = CameraThreadState::getCallingPid();
- if (callingPid == mClientPid) return NO_ERROR;
-
- ALOGW("attempt to use a locked camera from a different process"
- " (old pid %d, new pid %d)", mClientPid, callingPid);
- return EBUSY;
-}
-
-status_t CameraClient::checkPidAndHardware() const {
- if (mHardware == 0) {
- ALOGE("attempt to use a camera after disconnect() (pid %d)",
- CameraThreadState::getCallingPid());
- return INVALID_OPERATION;
- }
- status_t result = checkPid();
- if (result != NO_ERROR) return result;
- return NO_ERROR;
-}
-
-status_t CameraClient::lock() {
- int callingPid = CameraThreadState::getCallingPid();
- LOG1("lock (pid %d)", callingPid);
- Mutex::Autolock lock(mLock);
-
- // lock camera to this client if the the camera is unlocked
- if (mClientPid == 0) {
- mClientPid = callingPid;
- return NO_ERROR;
- }
-
- // returns NO_ERROR if the client already owns the camera, EBUSY otherwise
- return checkPid();
-}
-
-status_t CameraClient::unlock() {
- int callingPid = CameraThreadState::getCallingPid();
- LOG1("unlock (pid %d)", callingPid);
- Mutex::Autolock lock(mLock);
-
- // allow anyone to use camera (after they lock the camera)
- status_t result = checkPid();
- if (result == NO_ERROR) {
- if (mHardware->recordingEnabled()) {
- ALOGE("Not allowed to unlock camera during recording.");
- return INVALID_OPERATION;
- }
- mClientPid = 0;
- LOG1("clear mRemoteCallback (pid %d)", callingPid);
- // we need to remove the reference to ICameraClient so that when the app
- // goes away, the reference count goes to 0.
- mRemoteCallback.clear();
- }
- return result;
-}
-
-// connect a new client to the camera
-status_t CameraClient::connect(const sp<hardware::ICameraClient>& client) {
- int callingPid = CameraThreadState::getCallingPid();
- LOG1("connect E (pid %d)", callingPid);
- Mutex::Autolock lock(mLock);
-
- if (mClientPid != 0 && checkPid() != NO_ERROR) {
- ALOGW("Tried to connect to a locked camera (old pid %d, new pid %d)",
- mClientPid, callingPid);
- return EBUSY;
- }
-
- if (mRemoteCallback != 0 &&
- (IInterface::asBinder(client) == IInterface::asBinder(mRemoteCallback))) {
- LOG1("Connect to the same client");
- return NO_ERROR;
- }
-
- mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP;
- mClientPid = callingPid;
- mRemoteCallback = client;
-
- LOG1("connect X (pid %d)", callingPid);
- return NO_ERROR;
-}
-
-static void disconnectWindow(const sp<ANativeWindow>& window) {
- if (window != 0) {
- status_t result = native_window_api_disconnect(window.get(),
- NATIVE_WINDOW_API_CAMERA);
- if (result != NO_ERROR) {
- ALOGW("native_window_api_disconnect failed: %s (%d)", strerror(-result),
- result);
- }
- }
-}
-
-binder::Status CameraClient::disconnect() {
- int callingPid = CameraThreadState::getCallingPid();
- LOG1("disconnect E (pid %d)", callingPid);
- Mutex::Autolock lock(mLock);
-
- binder::Status res = binder::Status::ok();
- // Allow both client and the cameraserver to disconnect at all times
- if (callingPid != mClientPid && callingPid != mServicePid) {
- ALOGW("different client - don't disconnect");
- return res;
- }
-
- // Make sure disconnect() is done once and once only, whether it is called
- // from the user directly, or called by the destructor.
- if (mHardware == 0) return res;
-
- LOG1("hardware teardown");
- // Before destroying mHardware, we must make sure it's in the
- // idle state.
- // Turn off all messages.
- disableMsgType(CAMERA_MSG_ALL_MSGS);
- mHardware->stopPreview();
- sCameraService->updateProxyDeviceState(
- hardware::ICameraServiceProxy::CAMERA_STATE_IDLE,
- mCameraIdStr, mCameraFacing, mClientPackageName,
- hardware::ICameraServiceProxy::CAMERA_API_LEVEL_1);
- mHardware->cancelPicture();
- // Release the hardware resources.
- mHardware->release();
-
- // Release the held ANativeWindow resources.
- if (mPreviewWindow != 0) {
- disconnectWindow(mPreviewWindow);
- mPreviewWindow = 0;
- mHardware->setPreviewWindow(mPreviewWindow);
- }
- mHardware.clear();
-
- CameraService::Client::disconnect();
-
- LOG1("disconnect X (pid %d)", callingPid);
-
- return res;
-}
-
-// ----------------------------------------------------------------------------
-
-status_t CameraClient::setPreviewWindow(const sp<IBinder>& binder,
- const sp<ANativeWindow>& window) {
- Mutex::Autolock lock(mLock);
- status_t result = checkPidAndHardware();
- if (result != NO_ERROR) return result;
-
- // return if no change in surface.
- if (binder == mSurface) {
- return NO_ERROR;
- }
-
- if (window != 0) {
- result = native_window_api_connect(window.get(), NATIVE_WINDOW_API_CAMERA);
- if (result != NO_ERROR) {
- ALOGE("native_window_api_connect failed: %s (%d)", strerror(-result),
- result);
- return result;
- }
- }
-
- // If preview has been already started, register preview buffers now.
- if (mHardware->previewEnabled()) {
- if (window != 0) {
- mHardware->setPreviewScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
- mHardware->setPreviewTransform(mOrientation);
- result = mHardware->setPreviewWindow(window);
- }
- }
-
- if (result == NO_ERROR) {
- // Everything has succeeded. Disconnect the old window and remember the
- // new window.
- disconnectWindow(mPreviewWindow);
- mSurface = binder;
- mPreviewWindow = window;
- } else {
- // Something went wrong after we connected to the new window, so
- // disconnect here.
- disconnectWindow(window);
- }
-
- return result;
-}
-
-// set the buffer consumer that the preview will use
-status_t CameraClient::setPreviewTarget(
- const sp<IGraphicBufferProducer>& bufferProducer) {
- LOG1("setPreviewTarget(%p) (pid %d)", bufferProducer.get(),
- CameraThreadState::getCallingPid());
-
- sp<IBinder> binder;
- sp<ANativeWindow> window;
- if (bufferProducer != 0) {
- binder = IInterface::asBinder(bufferProducer);
- // Using controlledByApp flag to ensure that the buffer queue remains in
- // async mode for the old camera API, where many applications depend
- // on that behavior.
- window = new Surface(bufferProducer, /*controlledByApp*/ true);
- }
- return setPreviewWindow(binder, window);
-}
-
-// set the preview callback flag to affect how the received frames from
-// preview are handled.
-void CameraClient::setPreviewCallbackFlag(int callback_flag) {
- LOG1("setPreviewCallbackFlag(%d) (pid %d)", callback_flag, CameraThreadState::getCallingPid());
- Mutex::Autolock lock(mLock);
- if (checkPidAndHardware() != NO_ERROR) return;
-
- mPreviewCallbackFlag = callback_flag;
- if (mPreviewCallbackFlag & CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK) {
- enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
- } else {
- disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
- }
-}
-
-status_t CameraClient::setPreviewCallbackTarget(
- const sp<IGraphicBufferProducer>& callbackProducer) {
- (void)callbackProducer;
- ALOGE("%s: Unimplemented!", __FUNCTION__);
- return INVALID_OPERATION;
-}
-
-// start preview mode
-status_t CameraClient::startPreview() {
- LOG1("startPreview (pid %d)", CameraThreadState::getCallingPid());
- return startCameraMode(CAMERA_PREVIEW_MODE);
-}
-
-// start recording mode
-status_t CameraClient::startRecording() {
- LOG1("startRecording (pid %d)", CameraThreadState::getCallingPid());
- return startCameraMode(CAMERA_RECORDING_MODE);
-}
-
-// start preview or recording
-status_t CameraClient::startCameraMode(camera_mode mode) {
- LOG1("startCameraMode(%d)", mode);
- Mutex::Autolock lock(mLock);
- status_t result = checkPidAndHardware();
- if (result != NO_ERROR) return result;
-
- switch(mode) {
- case CAMERA_PREVIEW_MODE:
- if (mSurface == 0 && mPreviewWindow == 0) {
- LOG1("mSurface is not set yet.");
- // still able to start preview in this case.
- }
- return startPreviewMode();
- case CAMERA_RECORDING_MODE:
- if (mSurface == 0 && mPreviewWindow == 0) {
- ALOGE("mSurface or mPreviewWindow must be set before startRecordingMode.");
- return INVALID_OPERATION;
- }
- return startRecordingMode();
- default:
- return UNKNOWN_ERROR;
- }
-}
-
-status_t CameraClient::startPreviewMode() {
- LOG1("startPreviewMode");
- status_t result = NO_ERROR;
-
- // if preview has been enabled, nothing needs to be done
- if (mHardware->previewEnabled()) {
- return NO_ERROR;
- }
-
- if (mPreviewWindow != 0) {
- mHardware->setPreviewScalingMode(
- NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
- mHardware->setPreviewTransform(mOrientation);
- }
- mHardware->setPreviewWindow(mPreviewWindow);
- result = mHardware->startPreview();
- if (result == NO_ERROR) {
- sCameraService->updateProxyDeviceState(
- hardware::ICameraServiceProxy::CAMERA_STATE_ACTIVE,
- mCameraIdStr, mCameraFacing, mClientPackageName,
- hardware::ICameraServiceProxy::CAMERA_API_LEVEL_1);
- }
- return result;
-}
-
-status_t CameraClient::startRecordingMode() {
- LOG1("startRecordingMode");
- status_t result = NO_ERROR;
-
- // if recording has been enabled, nothing needs to be done
- if (mHardware->recordingEnabled()) {
- return NO_ERROR;
- }
-
- // if preview has not been started, start preview first
- if (!mHardware->previewEnabled()) {
- result = startPreviewMode();
- if (result != NO_ERROR) {
- return result;
- }
- }
-
- // start recording mode
- enableMsgType(CAMERA_MSG_VIDEO_FRAME);
- sCameraService->playSound(CameraService::SOUND_RECORDING_START);
- result = mHardware->startRecording();
- if (result != NO_ERROR) {
- ALOGE("mHardware->startRecording() failed with status %d", result);
- }
- return result;
-}
-
-// stop preview mode
-void CameraClient::stopPreview() {
- LOG1("stopPreview (pid %d)", CameraThreadState::getCallingPid());
- Mutex::Autolock lock(mLock);
- if (checkPidAndHardware() != NO_ERROR) return;
-
-
- disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
- mHardware->stopPreview();
- sCameraService->updateProxyDeviceState(
- hardware::ICameraServiceProxy::CAMERA_STATE_IDLE,
- mCameraIdStr, mCameraFacing, mClientPackageName,
- hardware::ICameraServiceProxy::CAMERA_API_LEVEL_1);
- mPreviewBuffer.clear();
-}
-
-// stop recording mode
-void CameraClient::stopRecording() {
- LOG1("stopRecording (pid %d)", CameraThreadState::getCallingPid());
- {
- Mutex::Autolock lock(mLock);
- if (checkPidAndHardware() != NO_ERROR) return;
-
- disableMsgType(CAMERA_MSG_VIDEO_FRAME);
- mHardware->stopRecording();
- sCameraService->playSound(CameraService::SOUND_RECORDING_STOP);
-
- mPreviewBuffer.clear();
- }
-
- {
- Mutex::Autolock l(mAvailableCallbackBuffersLock);
- if (!mAvailableCallbackBuffers.empty()) {
- mAvailableCallbackBuffers.clear();
- }
- }
-}
-
-// release a recording frame
-void CameraClient::releaseRecordingFrame(const sp<IMemory>& mem) {
- Mutex::Autolock lock(mLock);
- if (checkPidAndHardware() != NO_ERROR) return;
- if (mem == nullptr) {
- android_errorWriteWithInfoLog(CameraService::SN_EVENT_LOG_ID, "26164272",
- CameraThreadState::getCallingUid(), nullptr, 0);
- return;
- }
-
- mHardware->releaseRecordingFrame(mem);
-}
-
-void CameraClient::releaseRecordingFrameHandle(native_handle_t *handle) {
- if (handle == nullptr) return;
- Mutex::Autolock lock(mLock);
- sp<IMemory> dataPtr;
- {
- Mutex::Autolock l(mAvailableCallbackBuffersLock);
- if (!mAvailableCallbackBuffers.empty()) {
- dataPtr = mAvailableCallbackBuffers.back();
- mAvailableCallbackBuffers.pop_back();
- }
- }
-
- if (dataPtr == nullptr) {
- ALOGE("%s: %d: No callback buffer available. Dropping a native handle.", __FUNCTION__,
- __LINE__);
- native_handle_close(handle);
- native_handle_delete(handle);
- return;
- } else if (dataPtr->size() != sizeof(VideoNativeHandleMetadata)) {
- ALOGE("%s: %d: Callback buffer size doesn't match VideoNativeHandleMetadata", __FUNCTION__,
- __LINE__);
- native_handle_close(handle);
- native_handle_delete(handle);
- return;
- }
-
- if (mHardware != nullptr) {
- VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(dataPtr->unsecurePointer());
- metadata->eType = kMetadataBufferTypeNativeHandleSource;
- metadata->pHandle = handle;
- mHardware->releaseRecordingFrame(dataPtr);
- }
-}
-
-void CameraClient::releaseRecordingFrameHandleBatch(const std::vector<native_handle_t*>& handles) {
- Mutex::Autolock lock(mLock);
- bool disconnected = (mHardware == nullptr);
- size_t n = handles.size();
- std::vector<sp<IMemory>> frames;
- if (!disconnected) {
- frames.reserve(n);
- }
- bool error = false;
- for (auto& handle : handles) {
- sp<IMemory> dataPtr;
- {
- Mutex::Autolock l(mAvailableCallbackBuffersLock);
- if (!mAvailableCallbackBuffers.empty()) {
- dataPtr = mAvailableCallbackBuffers.back();
- mAvailableCallbackBuffers.pop_back();
- }
- }
-
- if (dataPtr == nullptr) {
- ALOGE("%s: %d: No callback buffer available. Dropping frames.", __FUNCTION__,
- __LINE__);
- error = true;
- break;
- } else if (dataPtr->size() != sizeof(VideoNativeHandleMetadata)) {
- ALOGE("%s: %d: Callback buffer must be VideoNativeHandleMetadata", __FUNCTION__,
- __LINE__);
- error = true;
- break;
- }
-
- if (!disconnected) {
- VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(dataPtr->unsecurePointer());
- metadata->eType = kMetadataBufferTypeNativeHandleSource;
- metadata->pHandle = handle;
- frames.push_back(dataPtr);
- }
- }
-
- if (error) {
- for (auto& handle : handles) {
- native_handle_close(handle);
- native_handle_delete(handle);
- }
- } else if (!disconnected) {
- mHardware->releaseRecordingFrameBatch(frames);
- }
- return;
-}
-
-status_t CameraClient::setVideoBufferMode(int32_t videoBufferMode) {
- LOG1("setVideoBufferMode: %d", videoBufferMode);
- bool enableMetadataInBuffers = false;
-
- if (videoBufferMode == VIDEO_BUFFER_MODE_DATA_CALLBACK_METADATA) {
- enableMetadataInBuffers = true;
- } else if (videoBufferMode != VIDEO_BUFFER_MODE_DATA_CALLBACK_YUV) {
- ALOGE("%s: %d: videoBufferMode %d is not supported.", __FUNCTION__, __LINE__,
- videoBufferMode);
- return BAD_VALUE;
- }
-
- Mutex::Autolock lock(mLock);
- if (checkPidAndHardware() != NO_ERROR) {
- return UNKNOWN_ERROR;
- }
-
- return mHardware->storeMetaDataInBuffers(enableMetadataInBuffers);
-}
-
-bool CameraClient::previewEnabled() {
- LOG1("previewEnabled (pid %d)", CameraThreadState::getCallingPid());
-
- Mutex::Autolock lock(mLock);
- if (checkPidAndHardware() != NO_ERROR) return false;
- return mHardware->previewEnabled();
-}
-
-bool CameraClient::recordingEnabled() {
- LOG1("recordingEnabled (pid %d)", CameraThreadState::getCallingPid());
-
- Mutex::Autolock lock(mLock);
- if (checkPidAndHardware() != NO_ERROR) return false;
- return mHardware->recordingEnabled();
-}
-
-status_t CameraClient::autoFocus() {
- LOG1("autoFocus (pid %d)", CameraThreadState::getCallingPid());
-
- Mutex::Autolock lock(mLock);
- status_t result = checkPidAndHardware();
- if (result != NO_ERROR) return result;
-
- return mHardware->autoFocus();
-}
-
-status_t CameraClient::cancelAutoFocus() {
- LOG1("cancelAutoFocus (pid %d)", CameraThreadState::getCallingPid());
-
- Mutex::Autolock lock(mLock);
- status_t result = checkPidAndHardware();
- if (result != NO_ERROR) return result;
-
- return mHardware->cancelAutoFocus();
-}
-
-// take a picture - image is returned in callback
-status_t CameraClient::takePicture(int msgType) {
- LOG1("takePicture (pid %d): 0x%x", CameraThreadState::getCallingPid(), msgType);
-
- Mutex::Autolock lock(mLock);
- status_t result = checkPidAndHardware();
- if (result != NO_ERROR) return result;
-
- if ((msgType & CAMERA_MSG_RAW_IMAGE) &&
- (msgType & CAMERA_MSG_RAW_IMAGE_NOTIFY)) {
- ALOGE("CAMERA_MSG_RAW_IMAGE and CAMERA_MSG_RAW_IMAGE_NOTIFY"
- " cannot be both enabled");
- return BAD_VALUE;
- }
-
- // We only accept picture related message types
- // and ignore other types of messages for takePicture().
- int picMsgType = msgType
- & (CAMERA_MSG_SHUTTER |
- CAMERA_MSG_POSTVIEW_FRAME |
- CAMERA_MSG_RAW_IMAGE |
- CAMERA_MSG_RAW_IMAGE_NOTIFY |
- CAMERA_MSG_COMPRESSED_IMAGE);
-
- enableMsgType(picMsgType);
-
- return mHardware->takePicture();
-}
-
-// set preview/capture parameters - key/value pairs
-status_t CameraClient::setParameters(const String8& params) {
- LOG1("setParameters (pid %d) (%s)", CameraThreadState::getCallingPid(), params.string());
-
- Mutex::Autolock lock(mLock);
- status_t result = checkPidAndHardware();
- if (result != NO_ERROR) return result;
-
- mLatestSetParameters = CameraParameters(params);
- CameraParameters p(params);
- return mHardware->setParameters(p);
-}
-
-// get preview/capture parameters - key/value pairs
-String8 CameraClient::getParameters() const {
- Mutex::Autolock lock(mLock);
- // The camera service can unconditionally get the parameters at all times
- if (CameraThreadState::getCallingPid() != mServicePid && checkPidAndHardware() != NO_ERROR) {
- return String8();
- }
-
- String8 params(mHardware->getParameters().flatten());
- LOG1("getParameters (pid %d) (%s)", CameraThreadState::getCallingPid(), params.string());
- return params;
-}
-
-// enable shutter sound
-status_t CameraClient::enableShutterSound(bool enable) {
- LOG1("enableShutterSound (pid %d)", CameraThreadState::getCallingPid());
-
- status_t result = checkPidAndHardware();
- if (result != NO_ERROR) return result;
-
- if (enable) {
- mPlayShutterSound = true;
- return OK;
- }
-
- mPlayShutterSound = false;
- return OK;
-}
-
-status_t CameraClient::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) {
- LOG1("sendCommand (pid %d)", CameraThreadState::getCallingPid());
- int orientation;
- Mutex::Autolock lock(mLock);
- status_t result = checkPidAndHardware();
- if (result != NO_ERROR) return result;
-
- if (cmd == CAMERA_CMD_SET_DISPLAY_ORIENTATION) {
- // Mirror the preview if the camera is front-facing.
- orientation = getOrientation(arg1, mCameraFacing == CAMERA_FACING_FRONT);
- if (orientation == -1) return BAD_VALUE;
-
- if (mOrientation != orientation) {
- mOrientation = orientation;
- if (mPreviewWindow != 0) {
- mHardware->setPreviewTransform(mOrientation);
- }
- }
- return OK;
- } else if (cmd == CAMERA_CMD_ENABLE_SHUTTER_SOUND) {
- switch (arg1) {
- case 0:
- return enableShutterSound(false);
- case 1:
- return enableShutterSound(true);
- default:
- return BAD_VALUE;
- }
- return OK;
- } else if (cmd == CAMERA_CMD_PLAY_RECORDING_SOUND) {
- sCameraService->playSound(CameraService::SOUND_RECORDING_START);
- } else if (cmd == CAMERA_CMD_SET_VIDEO_BUFFER_COUNT) {
- // Silently ignore this command
- return INVALID_OPERATION;
- } else if (cmd == CAMERA_CMD_PING) {
- // If mHardware is 0, checkPidAndHardware will return error.
- return OK;
- }
-
- return mHardware->sendCommand(cmd, arg1, arg2);
-}
-
-// ----------------------------------------------------------------------------
-
-void CameraClient::enableMsgType(int32_t msgType) {
- android_atomic_or(msgType, &mMsgEnabled);
- mHardware->enableMsgType(msgType);
-}
-
-void CameraClient::disableMsgType(int32_t msgType) {
- android_atomic_and(~msgType, &mMsgEnabled);
- mHardware->disableMsgType(msgType);
-}
-
-#define CHECK_MESSAGE_INTERVAL 10 // 10ms
-bool CameraClient::lockIfMessageWanted(int32_t msgType) {
- int sleepCount = 0;
- while (mMsgEnabled & msgType) {
- if (mLock.tryLock() == NO_ERROR) {
- if (sleepCount > 0) {
- LOG1("lockIfMessageWanted(%d): waited for %d ms",
- msgType, sleepCount * CHECK_MESSAGE_INTERVAL);
- }
-
- // If messages are no longer enabled after acquiring lock, release and drop message
- if ((mMsgEnabled & msgType) == 0) {
- mLock.unlock();
- break;
- }
-
- return true;
- }
- if (sleepCount++ == 0) {
- LOG1("lockIfMessageWanted(%d): enter sleep", msgType);
- }
- usleep(CHECK_MESSAGE_INTERVAL * 1000);
- }
- ALOGW("lockIfMessageWanted(%d): dropped unwanted message", msgType);
- return false;
-}
-
-sp<CameraClient> CameraClient::getClientFromCookie(void* user) {
- String8 cameraId = String8::format("%d", (int)(intptr_t) user);
- auto clientDescriptor = sCameraService->mActiveClientManager.get(cameraId);
- if (clientDescriptor != nullptr) {
- return sp<CameraClient>{
- static_cast<CameraClient*>(clientDescriptor->getValue().get())};
- }
- return sp<CameraClient>{nullptr};
-}
-
-// Callback messages can be dispatched to internal handlers or pass to our
-// client's callback functions, depending on the message type.
-//
-// notifyCallback:
-// CAMERA_MSG_SHUTTER handleShutter
-// (others) c->notifyCallback
-// dataCallback:
-// CAMERA_MSG_PREVIEW_FRAME handlePreviewData
-// CAMERA_MSG_POSTVIEW_FRAME handlePostview
-// CAMERA_MSG_RAW_IMAGE handleRawPicture
-// CAMERA_MSG_COMPRESSED_IMAGE handleCompressedPicture
-// (others) c->dataCallback
-// dataCallbackTimestamp
-// (others) c->dataCallbackTimestamp
-
-void CameraClient::notifyCallback(int32_t msgType, int32_t ext1,
- int32_t ext2, void* user) {
- LOG2("notifyCallback(%d)", msgType);
-
- sp<CameraClient> client = getClientFromCookie(user);
- if (client.get() == nullptr) return;
-
- if (!client->lockIfMessageWanted(msgType)) return;
-
- switch (msgType) {
- case CAMERA_MSG_SHUTTER:
- // ext1 is the dimension of the yuv picture.
- client->handleShutter();
- break;
- default:
- client->handleGenericNotify(msgType, ext1, ext2);
- break;
- }
-}
-
-void CameraClient::dataCallback(int32_t msgType,
- const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata, void* user) {
- LOG2("dataCallback(%d)", msgType);
-
- sp<CameraClient> client = getClientFromCookie(user);
- if (client.get() == nullptr) return;
-
- if (!client->lockIfMessageWanted(msgType)) return;
- if (dataPtr == 0 && metadata == NULL) {
- ALOGE("Null data returned in data callback");
- client->handleGenericNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
- return;
- }
-
- switch (msgType & ~CAMERA_MSG_PREVIEW_METADATA) {
- case CAMERA_MSG_PREVIEW_FRAME:
- client->handlePreviewData(msgType, dataPtr, metadata);
- break;
- case CAMERA_MSG_POSTVIEW_FRAME:
- client->handlePostview(dataPtr);
- break;
- case CAMERA_MSG_RAW_IMAGE:
- client->handleRawPicture(dataPtr);
- break;
- case CAMERA_MSG_COMPRESSED_IMAGE:
- client->handleCompressedPicture(dataPtr);
- break;
- default:
- client->handleGenericData(msgType, dataPtr, metadata);
- break;
- }
-}
-
-void CameraClient::dataCallbackTimestamp(nsecs_t timestamp,
- int32_t msgType, const sp<IMemory>& dataPtr, void* user) {
- LOG2("dataCallbackTimestamp(%d)", msgType);
-
- sp<CameraClient> client = getClientFromCookie(user);
- if (client.get() == nullptr) return;
-
- if (!client->lockIfMessageWanted(msgType)) return;
-
- if (dataPtr == 0) {
- ALOGE("Null data returned in data with timestamp callback");
- client->handleGenericNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
- return;
- }
-
- client->handleGenericDataTimestamp(timestamp, msgType, dataPtr);
-}
-
-void CameraClient::handleCallbackTimestampBatch(
- int32_t msgType, const std::vector<HandleTimestampMessage>& msgs, void* user) {
- LOG2("dataCallbackTimestampBatch");
- sp<CameraClient> client = getClientFromCookie(user);
- if (client.get() == nullptr) return;
- if (!client->lockIfMessageWanted(msgType)) return;
-
- sp<hardware::ICameraClient> c = client->mRemoteCallback;
- client->mLock.unlock();
- if (c != 0 && msgs.size() > 0) {
- size_t n = msgs.size();
- std::vector<nsecs_t> timestamps;
- std::vector<native_handle_t*> handles;
- timestamps.reserve(n);
- handles.reserve(n);
- for (auto& msg : msgs) {
- native_handle_t* handle = nullptr;
- if (msg.dataPtr->size() != sizeof(VideoNativeHandleMetadata)) {
- 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->unsecurePointer());
- if (metadata->eType == kMetadataBufferTypeNativeHandleSource) {
- handle = metadata->pHandle;
- }
-
- if (handle == nullptr) {
- ALOGE("%s: VideoNativeHandleMetadata type mismatch or null handle passed!",
- __FUNCTION__);
- return;
- }
- {
- Mutex::Autolock l(client->mAvailableCallbackBuffersLock);
- client->mAvailableCallbackBuffers.push_back(msg.dataPtr);
- }
- timestamps.push_back(msg.timestamp);
- handles.push_back(handle);
- }
- c->recordingFrameHandleCallbackTimestampBatch(timestamps, handles);
- }
-}
-
-// snapshot taken callback
-void CameraClient::handleShutter(void) {
- if (mPlayShutterSound) {
- sCameraService->playSound(CameraService::SOUND_SHUTTER);
- }
-
- sp<hardware::ICameraClient> c = mRemoteCallback;
- if (c != 0) {
- mLock.unlock();
- c->notifyCallback(CAMERA_MSG_SHUTTER, 0, 0);
- if (!lockIfMessageWanted(CAMERA_MSG_SHUTTER)) return;
- }
- disableMsgType(CAMERA_MSG_SHUTTER);
-
- // Shutters only happen in response to takePicture, so mark device as
- // idle now, until preview is restarted
- sCameraService->updateProxyDeviceState(
- hardware::ICameraServiceProxy::CAMERA_STATE_IDLE,
- mCameraIdStr, mCameraFacing, mClientPackageName,
- hardware::ICameraServiceProxy::CAMERA_API_LEVEL_1);
-
- mLock.unlock();
-}
-
-// preview callback - frame buffer update
-void CameraClient::handlePreviewData(int32_t msgType,
- const sp<IMemory>& mem,
- camera_frame_metadata_t *metadata) {
- ssize_t offset;
- size_t size;
- sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
-
- // local copy of the callback flags
- int flags = mPreviewCallbackFlag;
-
- // is callback enabled?
- if (!(flags & CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK)) {
- // If the enable bit is off, the copy-out and one-shot bits are ignored
- LOG2("frame callback is disabled");
- mLock.unlock();
- return;
- }
-
- // hold a strong pointer to the client
- sp<hardware::ICameraClient> c = mRemoteCallback;
-
- // clear callback flags if no client or one-shot mode
- if (c == 0 || (mPreviewCallbackFlag & CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK)) {
- LOG2("Disable preview callback");
- mPreviewCallbackFlag &= ~(CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK |
- CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK |
- CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK);
- disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
- }
-
- if (c != 0) {
- // Is the received frame copied out or not?
- if (flags & CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK) {
- LOG2("frame is copied");
- copyFrameAndPostCopiedFrame(msgType, c, heap, offset, size, metadata);
- } else {
- LOG2("frame is forwarded");
- mLock.unlock();
- c->dataCallback(msgType, mem, metadata);
- }
- } else {
- mLock.unlock();
- }
-}
-
-// picture callback - postview image ready
-void CameraClient::handlePostview(const sp<IMemory>& mem) {
- disableMsgType(CAMERA_MSG_POSTVIEW_FRAME);
-
- sp<hardware::ICameraClient> c = mRemoteCallback;
- mLock.unlock();
- if (c != 0) {
- c->dataCallback(CAMERA_MSG_POSTVIEW_FRAME, mem, NULL);
- }
-}
-
-// picture callback - raw image ready
-void CameraClient::handleRawPicture(const sp<IMemory>& mem) {
- disableMsgType(CAMERA_MSG_RAW_IMAGE);
-
- ssize_t offset;
- size_t size;
- sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
-
- sp<hardware::ICameraClient> c = mRemoteCallback;
- mLock.unlock();
- if (c != 0) {
- c->dataCallback(CAMERA_MSG_RAW_IMAGE, mem, NULL);
- }
-}
-
-// picture callback - compressed picture ready
-void CameraClient::handleCompressedPicture(const sp<IMemory>& mem) {
- disableMsgType(CAMERA_MSG_COMPRESSED_IMAGE);
-
- sp<hardware::ICameraClient> c = mRemoteCallback;
- mLock.unlock();
- if (c != 0) {
- c->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE, mem, NULL);
- }
-}
-
-
-void CameraClient::handleGenericNotify(int32_t msgType,
- int32_t ext1, int32_t ext2) {
- sp<hardware::ICameraClient> c = mRemoteCallback;
- mLock.unlock();
- if (c != 0) {
- c->notifyCallback(msgType, ext1, ext2);
- }
-}
-
-void CameraClient::handleGenericData(int32_t msgType,
- const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata) {
- sp<hardware::ICameraClient> c = mRemoteCallback;
- mLock.unlock();
- if (c != 0) {
- c->dataCallback(msgType, dataPtr, metadata);
- }
-}
-
-void CameraClient::handleGenericDataTimestamp(nsecs_t timestamp,
- int32_t msgType, const sp<IMemory>& dataPtr) {
- sp<hardware::ICameraClient> c = mRemoteCallback;
- mLock.unlock();
- if (c != 0 && dataPtr != nullptr) {
- native_handle_t* handle = nullptr;
-
- // 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->unsecurePointer());
- if (metadata->eType == kMetadataBufferTypeNativeHandleSource) {
- handle = metadata->pHandle;
- }
- }
-
- // If dataPtr contains a native handle, send it via recordingFrameHandleCallbackTimestamp.
- if (handle != nullptr) {
- {
- Mutex::Autolock l(mAvailableCallbackBuffersLock);
- mAvailableCallbackBuffers.push_back(dataPtr);
- }
- c->recordingFrameHandleCallbackTimestamp(timestamp, handle);
- } else {
- c->dataCallbackTimestamp(timestamp, msgType, dataPtr);
- }
- }
-}
-
-void CameraClient::copyFrameAndPostCopiedFrame(
- int32_t msgType, const sp<hardware::ICameraClient>& client,
- const sp<IMemoryHeap>& heap, size_t offset, size_t size,
- camera_frame_metadata_t *metadata) {
- LOG2("copyFrameAndPostCopiedFrame");
- // It is necessary to copy out of pmem before sending this to
- // the callback. For efficiency, reuse the same MemoryHeapBase
- // provided it's big enough. Don't allocate the memory or
- // perform the copy if there's no callback.
- // hold the preview lock while we grab a reference to the preview buffer
- sp<MemoryHeapBase> previewBuffer;
-
- if (mPreviewBuffer == 0) {
- mPreviewBuffer = new MemoryHeapBase(size, 0, NULL);
- } else if (size > mPreviewBuffer->virtualSize()) {
- mPreviewBuffer.clear();
- mPreviewBuffer = new MemoryHeapBase(size, 0, NULL);
- }
- if (mPreviewBuffer == 0) {
- ALOGE("failed to allocate space for preview buffer");
- mLock.unlock();
- return;
- }
- previewBuffer = mPreviewBuffer;
-
- void* previewBufferBase = previewBuffer->base();
- void* heapBase = heap->base();
-
- if (heapBase == MAP_FAILED) {
- ALOGE("%s: Failed to mmap heap for preview frame.", __FUNCTION__);
- mLock.unlock();
- return;
- } else if (previewBufferBase == MAP_FAILED) {
- ALOGE("%s: Failed to mmap preview buffer for preview frame.", __FUNCTION__);
- mLock.unlock();
- return;
- }
-
- memcpy(previewBufferBase, (uint8_t *) heapBase + offset, size);
-
- sp<MemoryBase> frame = new MemoryBase(previewBuffer, 0, size);
- if (frame == 0) {
- ALOGE("failed to allocate space for frame callback");
- mLock.unlock();
- return;
- }
-
- mLock.unlock();
- client->dataCallback(msgType, frame, metadata);
-}
-
-int CameraClient::getOrientation(int degrees, bool mirror) {
- if (!mirror) {
- if (degrees == 0) return 0;
- else if (degrees == 90) return HAL_TRANSFORM_ROT_90;
- else if (degrees == 180) return HAL_TRANSFORM_ROT_180;
- else if (degrees == 270) return HAL_TRANSFORM_ROT_270;
- } else { // Do mirror (horizontal flip)
- if (degrees == 0) { // FLIP_H and ROT_0
- return HAL_TRANSFORM_FLIP_H;
- } else if (degrees == 90) { // FLIP_H and ROT_90
- return HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_ROT_90;
- } else if (degrees == 180) { // FLIP_H and ROT_180
- return HAL_TRANSFORM_FLIP_V;
- } else if (degrees == 270) { // FLIP_H and ROT_270
- return HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90;
- }
- }
- ALOGE("Invalid setDisplayOrientation degrees=%d", degrees);
- return -1;
-}
-
-status_t CameraClient::setVideoTarget(const sp<IGraphicBufferProducer>& bufferProducer) {
- (void)bufferProducer;
- ALOGE("%s: %d: CameraClient doesn't support setting a video target.", __FUNCTION__, __LINE__);
- 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();
-}
-
-// API1->Device1 does not support this feature
-status_t CameraClient::setRotateAndCropOverride(uint8_t /*rotateAndCrop*/) {
- return OK;
-}
-
-}; // namespace android
diff --git a/services/camera/libcameraservice/api1/CameraClient.h b/services/camera/libcameraservice/api1/CameraClient.h
deleted file mode 100644
index aacb00e..0000000
--- a/services/camera/libcameraservice/api1/CameraClient.h
+++ /dev/null
@@ -1,190 +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 ANDROID_SERVERS_CAMERA_CAMERACLIENT_H
-#define ANDROID_SERVERS_CAMERA_CAMERACLIENT_H
-
-#include "CameraService.h"
-
-namespace android {
-
-class MemoryHeapBase;
-class CameraHardwareInterface;
-
-/**
- * Interface between android.hardware.Camera API and Camera HAL device for version
- * CAMERA_DEVICE_API_VERSION_1_0.
- */
-
-class CameraClient : public CameraService::Client
-{
-public:
- // ICamera interface (see ICamera for details)
- virtual binder::Status disconnect();
- virtual status_t connect(const sp<hardware::ICameraClient>& client);
- virtual status_t lock();
- virtual status_t unlock();
- virtual status_t setPreviewTarget(const sp<IGraphicBufferProducer>& bufferProducer);
- virtual void setPreviewCallbackFlag(int flag);
- virtual status_t setPreviewCallbackTarget(
- const sp<IGraphicBufferProducer>& callbackProducer);
- virtual status_t startPreview();
- virtual void stopPreview();
- virtual bool previewEnabled();
- virtual status_t setVideoBufferMode(int32_t videoBufferMode);
- virtual status_t startRecording();
- virtual void stopRecording();
- virtual bool recordingEnabled();
- virtual void releaseRecordingFrame(const sp<IMemory>& mem);
- virtual void releaseRecordingFrameHandle(native_handle_t *handle);
- virtual void releaseRecordingFrameHandleBatch(
- const std::vector<native_handle_t*>& handles);
- virtual status_t autoFocus();
- virtual status_t cancelAutoFocus();
- virtual status_t takePicture(int msgType);
- virtual status_t setParameters(const String8& params);
- 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();
-
- virtual status_t setRotateAndCropOverride(uint8_t override);
-
- // Interface used by CameraService
- CameraClient(const sp<CameraService>& cameraService,
- const sp<hardware::ICameraClient>& cameraClient,
- const String16& clientPackageName,
- const std::optional<String16>& clientFeatureId,
- int cameraId,
- int cameraFacing,
- int clientPid,
- int clientUid,
- int servicePid);
- ~CameraClient();
-
- virtual status_t initialize(sp<CameraProviderManager> manager,
- const String8& monitorTags) override;
-
- virtual status_t dump(int fd, const Vector<String16>& args);
-
- virtual status_t dumpClient(int fd, const Vector<String16>& args);
-
-private:
-
- // check whether the calling process matches mClientPid.
- status_t checkPid() const;
- status_t checkPidAndHardware() const; // also check mHardware != 0
-
- // these are internal functions used to set up preview buffers
- status_t registerPreviewBuffers();
-
- // camera operation mode
- enum camera_mode {
- CAMERA_PREVIEW_MODE = 0, // frame automatically released
- CAMERA_RECORDING_MODE = 1, // frame has to be explicitly released by releaseRecordingFrame()
- };
- // these are internal functions used for preview/recording
- status_t startCameraMode(camera_mode mode);
- status_t startPreviewMode();
- status_t startRecordingMode();
-
- // internal function used by sendCommand to enable/disable shutter sound.
- status_t enableShutterSound(bool enable);
-
- static sp<CameraClient> getClientFromCookie(void* user);
-
- // these are static callback functions
- static void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user);
- static void dataCallback(int32_t msgType, const sp<IMemory>& dataPtr,
- camera_frame_metadata_t *metadata, void* user);
- static void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr, void* user);
- static void handleCallbackTimestampBatch(
- int32_t msgType, const std::vector<HandleTimestampMessage>&, void* user);
- // handlers for messages
- void handleShutter(void);
- void handlePreviewData(int32_t msgType, const sp<IMemory>& mem,
- camera_frame_metadata_t *metadata);
- void handlePostview(const sp<IMemory>& mem);
- void handleRawPicture(const sp<IMemory>& mem);
- void handleCompressedPicture(const sp<IMemory>& mem);
- void handleGenericNotify(int32_t msgType, int32_t ext1, int32_t ext2);
- void handleGenericData(int32_t msgType, const sp<IMemory>& dataPtr,
- camera_frame_metadata_t *metadata);
- void handleGenericDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr);
-
- void copyFrameAndPostCopiedFrame(
- int32_t msgType,
- const sp<hardware::ICameraClient>& client,
- const sp<IMemoryHeap>& heap,
- size_t offset, size_t size,
- camera_frame_metadata_t *metadata);
-
- int getOrientation(int orientation, bool mirror);
-
- status_t setPreviewWindow(
- const sp<IBinder>& binder,
- const sp<ANativeWindow>& window);
-
-
- // these are initialized in the constructor.
- sp<CameraHardwareInterface> mHardware; // cleared after disconnect()
- int mPreviewCallbackFlag;
- int mOrientation; // Current display orientation
- bool mPlayShutterSound;
- bool mLegacyMode; // camera2 api legacy mode?
-
- // Ensures atomicity among the public methods
- mutable Mutex mLock;
- // This is a binder of Surface or Surface.
- sp<IBinder> mSurface;
- sp<ANativeWindow> mPreviewWindow;
-
- // If the user want us to return a copy of the preview frame (instead
- // of the original one), we allocate mPreviewBuffer and reuse it if possible.
- sp<MemoryHeapBase> mPreviewBuffer;
-
- // Debugging information
- CameraParameters mLatestSetParameters;
-
- // mAvailableCallbackBuffers stores sp<IMemory> that HAL uses to send VideoNativeHandleMetadata.
- // It will be used to send VideoNativeHandleMetadata back to HAL when camera receives the
- // native handle from releaseRecordingFrameHandle.
- Mutex mAvailableCallbackBuffersLock;
- std::vector<sp<IMemory>> mAvailableCallbackBuffers;
-
- // We need to avoid the deadlock when the incoming command thread and
- // the CameraHardwareInterface callback thread both want to grab mLock.
- // An extra flag is used to tell the callback thread that it should stop
- // trying to deliver the callback messages if the client is not
- // interested in it anymore. For example, if the client is calling
- // stopPreview(), the preview frame messages do not need to be delivered
- // anymore.
-
- // This function takes the same parameter as the enableMsgType() and
- // disableMsgType() functions in CameraHardwareInterface.
- void enableMsgType(int32_t msgType);
- void disableMsgType(int32_t msgType);
- volatile int32_t mMsgEnabled;
-
- // This function keeps trying to grab mLock, or give up if the message
- // is found to be disabled. It returns true if mLock is grabbed.
- bool lockIfMessageWanted(int32_t msgType);
-};
-
-}
-
-#endif
diff --git a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
index a71a732..ee764ec 100644
--- a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
@@ -31,12 +31,15 @@
namespace android {
namespace camera2 {
+using android::camera3::CAMERA_STREAM_ROTATION_0;
+
CallbackProcessor::CallbackProcessor(sp<Camera2Client> client):
Thread(false),
mClient(client),
mDevice(client->getCameraDevice()),
mId(client->getCameraId()),
mCallbackAvailable(false),
+ mCallbackPaused(true),
mCallbackToApp(false),
mCallbackStreamId(NO_STREAM) {
}
@@ -154,8 +157,8 @@
callbackFormat, params.previewFormat);
res = device->createStream(mCallbackWindow,
params.previewWidth, params.previewHeight, callbackFormat,
- HAL_DATASPACE_V0_JFIF, CAMERA3_STREAM_ROTATION_0, &mCallbackStreamId,
- String8());
+ HAL_DATASPACE_V0_JFIF, CAMERA_STREAM_ROTATION_0, &mCallbackStreamId,
+ String8(), std::unordered_set<int32_t>{ANDROID_SENSOR_PIXEL_MODE_DEFAULT});
if (res != OK) {
ALOGE("%s: Camera %d: Can't create output stream for callbacks: "
"%s (%d)", __FUNCTION__, mId,
@@ -214,6 +217,14 @@
return mCallbackStreamId;
}
+void CallbackProcessor::unpauseCallback() {
+ mCallbackPaused = false;
+}
+
+void CallbackProcessor::pauseCallback() {
+ mCallbackPaused = true;
+}
+
void CallbackProcessor::dump(int /*fd*/, const Vector<String16>& /*args*/) const {
}
@@ -232,7 +243,7 @@
do {
sp<Camera2Client> client = mClient.promote();
- if (client == 0) {
+ if (client == 0 || mCallbackPaused) {
res = discardNewCallback();
} else {
res = processNewCallback(client);
diff --git a/services/camera/libcameraservice/api1/client2/CallbackProcessor.h b/services/camera/libcameraservice/api1/client2/CallbackProcessor.h
index 5231688..a336326 100644
--- a/services/camera/libcameraservice/api1/client2/CallbackProcessor.h
+++ b/services/camera/libcameraservice/api1/client2/CallbackProcessor.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_SERVERS_CAMERA_CAMERA2_CALLBACKPROCESSOR_H
#define ANDROID_SERVERS_CAMERA_CAMERA2_CALLBACKPROCESSOR_H
+#include <atomic>
+
#include <utils/Thread.h>
#include <utils/String16.h>
#include <utils/Vector.h>
@@ -52,6 +54,9 @@
status_t deleteStream();
int getStreamId() const;
+ void unpauseCallback();
+ void pauseCallback();
+
void dump(int fd, const Vector<String16>& args) const;
private:
static const nsecs_t kWaitDuration = 10000000; // 10 ms
@@ -67,6 +72,8 @@
NO_STREAM = -1
};
+ std::atomic<bool> mCallbackPaused;
+
// True if mCallbackWindow is a remote consumer, false if just the local
// mCallbackConsumer
bool mCallbackToApp;
diff --git a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
index 0c01a91..4c9b7ed 100644
--- a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
+++ b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
@@ -706,7 +706,7 @@
if (mCaptureRequest.entryCount() == 0) {
res = client->getCameraDevice()->createDefaultRequest(
- CAMERA2_TEMPLATE_STILL_CAPTURE,
+ camera_request_template_t::CAMERA_TEMPLATE_STILL_CAPTURE,
&mCaptureRequest);
if (res != OK) {
ALOGE("%s: Camera %d: Unable to create default still image request:"
diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
index ddfe5e3..eed2654 100755
--- a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
@@ -35,6 +35,8 @@
namespace android {
namespace camera2 {
+using android::camera3::CAMERA_STREAM_ROTATION_0;
+
JpegProcessor::JpegProcessor(
sp<Camera2Client> client,
wp<CaptureSequencer> sequencer):
@@ -148,8 +150,8 @@
res = device->createStream(mCaptureWindow,
params.pictureWidth, params.pictureHeight,
HAL_PIXEL_FORMAT_BLOB, HAL_DATASPACE_V0_JFIF,
- CAMERA3_STREAM_ROTATION_0, &mCaptureStreamId,
- String8());
+ CAMERA_STREAM_ROTATION_0, &mCaptureStreamId,
+ String8(), std::unordered_set<int32_t>{ANDROID_SENSOR_PIXEL_MODE_DEFAULT});
if (res != OK) {
ALOGE("%s: Camera %d: Can't create output stream for capture: "
"%s (%d)", __FUNCTION__, mId,
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index dbc863b..8164df0 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -29,7 +29,6 @@
#include "Parameters.h"
#include "system/camera.h"
-#include "hardware/camera_common.h"
#include <android/hardware/ICamera.h>
#include <media/MediaProfiles.h>
#include <media/mediarecorder.h>
@@ -37,6 +36,8 @@
namespace android {
namespace camera2 {
+using android::camera3::CAMERA_TEMPLATE_PREVIEW;
+
Parameters::Parameters(int cameraId,
int cameraFacing) :
cameraId(cameraId),
@@ -2468,7 +2469,7 @@
// Use focal length in preview template if it exists
CameraMetadata previewTemplate;
- status_t res = device->createDefaultRequest(CAMERA3_TEMPLATE_PREVIEW, &previewTemplate);
+ status_t res = device->createDefaultRequest(CAMERA_TEMPLATE_PREVIEW, &previewTemplate);
if (res != OK) {
ALOGE("%s: Failed to create default PREVIEW request: %s (%d)",
__FUNCTION__, strerror(-res), res);
@@ -3253,6 +3254,8 @@
status_t Parameters::calculatePictureFovs(float *horizFov, float *vertFov)
const {
+ // For external camera, use FOVs = (-1.0, -1.0) as default values. Calculate
+ // FOVs only if there is sufficient information.
if (fastInfo.isExternalCamera) {
if (horizFov != NULL) {
*horizFov = -1.0;
@@ -3260,16 +3263,29 @@
if (vertFov != NULL) {
*vertFov = -1.0;
}
- return OK;
}
camera_metadata_ro_entry_t sensorSize =
staticInfo(ANDROID_SENSOR_INFO_PHYSICAL_SIZE, 2, 2);
- if (!sensorSize.count) return NO_INIT;
+ if (!sensorSize.count) {
+ // It is non-fatal for external cameras since it has default values.
+ if (fastInfo.isExternalCamera) {
+ return OK;
+ } else {
+ return NO_INIT;
+ }
+ }
camera_metadata_ro_entry_t pixelArraySize =
staticInfo(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE, 2, 2);
- if (!pixelArraySize.count) return NO_INIT;
+ if (!pixelArraySize.count) {
+ // It is non-fatal for external cameras since it has default values.
+ if (fastInfo.isExternalCamera) {
+ return OK;
+ } else {
+ return NO_INIT;
+ }
+ }
float arrayAspect = static_cast<float>(fastInfo.arrayWidth) /
fastInfo.arrayHeight;
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h
index 3a709c9..02ac638 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -56,7 +56,7 @@
int previewTransform; // set by CAMERA_CMD_SET_DISPLAY_ORIENTATION
int pictureWidth, pictureHeight;
- // Store the picture size before they are overriden by video snapshot
+ // Store the picture size before they are overridden by video snapshot
int pictureWidthLastSet, pictureHeightLastSet;
bool pictureSizeOverriden;
diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
index 0786f53..2d3597c 100644
--- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
@@ -40,6 +40,10 @@
namespace android {
namespace camera2 {
+using android::camera3::CAMERA_STREAM_ROTATION_0;
+using android::camera3::CAMERA_TEMPLATE_PREVIEW;
+using android::camera3::CAMERA_TEMPLATE_ZERO_SHUTTER_LAG;
+
StreamingProcessor::StreamingProcessor(sp<Camera2Client> client):
mClient(client),
mDevice(client->getCameraDevice()),
@@ -113,12 +117,12 @@
return INVALID_OPERATION;
}
- // Use CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG for ZSL streaming case.
+ // Use CAMERA_TEMPLATE_ZERO_SHUTTER_LAG for ZSL streaming case.
if (params.useZeroShutterLag() && !params.recordingHint) {
res = device->createDefaultRequest(
- CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG, &mPreviewRequest);
+ CAMERA_TEMPLATE_ZERO_SHUTTER_LAG, &mPreviewRequest);
} else {
- res = device->createDefaultRequest(CAMERA3_TEMPLATE_PREVIEW,
+ res = device->createDefaultRequest(CAMERA_TEMPLATE_PREVIEW,
&mPreviewRequest);
}
@@ -194,7 +198,8 @@
res = device->createStream(mPreviewWindow,
params.previewWidth, params.previewHeight,
CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, HAL_DATASPACE_UNKNOWN,
- CAMERA3_STREAM_ROTATION_0, &mPreviewStreamId, String8());
+ CAMERA_STREAM_ROTATION_0, &mPreviewStreamId, String8(),
+ std::unordered_set<int32_t>{ANDROID_SENSOR_PIXEL_MODE_DEFAULT});
if (res != OK) {
ALOGE("%s: Camera %d: Unable to create preview stream: %s (%d)",
__FUNCTION__, mId, strerror(-res), res);
@@ -263,7 +268,7 @@
}
if (mRecordingRequest.entryCount() == 0) {
- res = device->createDefaultRequest(CAMERA2_TEMPLATE_VIDEO_RECORD,
+ res = device->createDefaultRequest(camera_request_template_t::CAMERA_TEMPLATE_VIDEO_RECORD,
&mRecordingRequest);
if (res != OK) {
ALOGE("%s: Camera %d: Unable to create default recording request:"
@@ -379,8 +384,8 @@
res = device->createStream(mRecordingWindow,
params.videoWidth, params.videoHeight,
params.videoFormat, params.videoDataSpace,
- CAMERA3_STREAM_ROTATION_0, &mRecordingStreamId,
- String8());
+ CAMERA_STREAM_ROTATION_0, &mRecordingStreamId,
+ String8(), std::unordered_set<int32_t>{ANDROID_SENSOR_PIXEL_MODE_DEFAULT});
if (res != OK) {
ALOGE("%s: Camera %d: Can't create output stream for recording: "
"%s (%d)", __FUNCTION__, mId,
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
index 8dc9863..8e598f1 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
@@ -42,6 +42,9 @@
namespace android {
namespace camera2 {
+using android::camera3::CAMERA_STREAM_ROTATION_0;
+using android::camera3::CAMERA_TEMPLATE_STILL_CAPTURE;
+
namespace {
struct TimestampFinder : public RingBufferConsumer::RingBufferComparator {
typedef RingBufferConsumer::BufferInfo BufferInfo;
@@ -172,7 +175,7 @@
mBufferQueueDepth = mFrameListDepth + 1;
mZslQueue.insertAt(0, mBufferQueueDepth);
- mFrameList.insertAt(0, mFrameListDepth);
+ mFrameList.resize(mFrameListDepth);
sp<CaptureSequencer> captureSequencer = mSequencer.promote();
if (captureSequencer != 0) captureSequencer->setZslProcessor(this);
}
@@ -208,7 +211,7 @@
// Corresponding buffer has been cleared. No need to push into mFrameList
if (timestamp <= mLatestClearedBufferTimestamp) return;
- mFrameList.editItemAt(mFrameListHead) = result.mMetadata;
+ mFrameList[mFrameListHead] = result.mMetadata;
mFrameListHead = (mFrameListHead + 1) % mFrameListDepth;
}
@@ -234,7 +237,7 @@
if (mInputStreamId == NO_STREAM) {
res = device->createInputStream(params.fastInfo.maxZslSize.width,
params.fastInfo.maxZslSize.height, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
- &mInputStreamId);
+ /*isMultiResolution*/false, &mInputStreamId);
if (res != OK) {
ALOGE("%s: Camera %d: Can't create input stream: "
"%s (%d)", __FUNCTION__, client->getCameraId(),
@@ -257,8 +260,8 @@
res = device->createStream(outSurface, params.fastInfo.maxZslSize.width,
params.fastInfo.maxZslSize.height, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
- HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0, &mZslStreamId,
- String8());
+ HAL_DATASPACE_UNKNOWN, CAMERA_STREAM_ROTATION_0, &mZslStreamId,
+ String8(), std::unordered_set<int32_t>{ANDROID_SENSOR_PIXEL_MODE_DEFAULT});
if (res != OK) {
ALOGE("%s: Camera %d: Can't create ZSL stream: "
"%s (%d)", __FUNCTION__, client->getCameraId(),
@@ -348,7 +351,7 @@
}
CameraMetadata stillTemplate;
- device->createDefaultRequest(CAMERA3_TEMPLATE_STILL_CAPTURE, &stillTemplate);
+ device->createDefaultRequest(CAMERA_TEMPLATE_STILL_CAPTURE, &stillTemplate);
// Find some of the post-processing tags, and assign the value from template to the request.
// Only check the aberration mode and noise reduction mode for now, as they are very important
@@ -671,7 +674,7 @@
void ZslProcessor::clearZslResultQueueLocked() {
mFrameList.clear();
mFrameListHead = 0;
- mFrameList.insertAt(0, mFrameListDepth);
+ mFrameList.resize(mFrameListDepth);
}
void ZslProcessor::dump(int fd, const Vector<String16>& /*args*/) const {
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.h b/services/camera/libcameraservice/api1/client2/ZslProcessor.h
index 1db2403..3186233 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor.h
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.h
@@ -125,7 +125,7 @@
static const int32_t kDefaultMaxPipelineDepth = 4;
size_t mBufferQueueDepth;
size_t mFrameListDepth;
- Vector<CameraMetadata> mFrameList;
+ std::vector<CameraMetadata> mFrameList;
size_t mFrameListHead;
ZslPair mNextPair;
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 022d686..343f4a7 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -21,6 +21,7 @@
#include <cutils/properties.h>
#include <utils/CameraThreadState.h>
#include <utils/Log.h>
+#include <utils/SessionConfigurationUtils.h>
#include <utils/Trace.h>
#include <gui/Surface.h>
#include <camera/camera2/CaptureRequest.h>
@@ -30,6 +31,7 @@
#include "device3/Camera3Device.h"
#include "device3/Camera3OutputStream.h"
#include "api2/CameraDeviceClient.h"
+#include "utils/CameraServiceProxyWrapper.h"
#include <camera_metadata_hidden.h>
@@ -49,6 +51,7 @@
namespace android {
using namespace camera2;
+using camera3::camera_stream_rotation_t::CAMERA_STREAM_ROTATION_0;
CameraDeviceClientBase::CameraDeviceClientBase(
const sp<CameraService>& cameraService,
@@ -58,6 +61,7 @@
const String8& cameraId,
int api1CameraId,
int cameraFacing,
+ int sensorOrientation,
int clientPid,
uid_t clientUid,
int servicePid) :
@@ -67,6 +71,7 @@
clientFeatureId,
cameraId,
cameraFacing,
+ sensorOrientation,
clientPid,
clientUid,
servicePid),
@@ -83,12 +88,13 @@
const std::optional<String16>& clientFeatureId,
const String8& cameraId,
int cameraFacing,
+ int sensorOrientation,
int clientPid,
uid_t clientUid,
int servicePid) :
Camera2ClientBase(cameraService, remoteCallback, clientPackageName, clientFeatureId,
cameraId, /*API1 camera ID*/ -1,
- cameraFacing, clientPid, clientUid, servicePid),
+ cameraFacing, sensorOrientation, clientPid, clientUid, servicePid),
mInputStream(),
mStreamingRequestId(REQUEST_ID_NONE),
mRequestIdCounter(0) {
@@ -122,8 +128,8 @@
/*listener*/this,
/*sendPartials*/true);
- auto deviceInfo = mDevice->info();
- camera_metadata_entry_t physicalKeysEntry = deviceInfo.find(
+ const CameraMetadata &deviceInfo = mDevice->info();
+ camera_metadata_ro_entry_t physicalKeysEntry = deviceInfo.find(
ANDROID_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS);
if (physicalKeysEntry.count > 0) {
mSupportedPhysicalRequestKeys.insert(mSupportedPhysicalRequestKeys.begin(),
@@ -132,6 +138,17 @@
}
mProviderManager = providerPtr;
+ // Cache physical camera ids corresponding to this device and also the high
+ // resolution sensors in this device + physical camera ids
+ mProviderManager->isLogicalCamera(mCameraIdStr.string(), &mPhysicalCameraIds);
+ if (isUltraHighResolutionSensor(mCameraIdStr)) {
+ mHighResolutionSensors.insert(mCameraIdStr.string());
+ }
+ for (auto &physicalId : mPhysicalCameraIds) {
+ if (isUltraHighResolutionSensor(String8(physicalId.c_str()))) {
+ mHighResolutionSensors.insert(physicalId.c_str());
+ }
+ }
return OK;
}
@@ -168,7 +185,6 @@
const StreamSurfaceId& streamSurfaceId = mStreamMap.valueAt(idx);
if (outSurfaceMap->find(streamSurfaceId.streamId()) == outSurfaceMap->end()) {
- (*outSurfaceMap)[streamSurfaceId.streamId()] = std::vector<size_t>();
outputStreamIds->push_back(streamSurfaceId.streamId());
}
(*outSurfaceMap)[streamSurfaceId.streamId()].push_back(streamSurfaceId.surfaceId());
@@ -184,6 +200,17 @@
return binder::Status::ok();
}
+static std::list<int> getIntersection(const std::unordered_set<int> &streamIdsForThisCamera,
+ const Vector<int> &streamIdsForThisRequest) {
+ std::list<int> intersection;
+ for (auto &streamId : streamIdsForThisRequest) {
+ if (streamIdsForThisCamera.find(streamId) != streamIdsForThisCamera.end()) {
+ intersection.emplace_back(streamId);
+ }
+ }
+ return intersection;
+}
+
binder::Status CameraDeviceClient::submitRequestList(
const std::vector<hardware::camera2::CaptureRequest>& requests,
bool streaming,
@@ -330,6 +357,24 @@
"Request settings are empty");
}
+ // Check whether the physical / logical stream has settings
+ // consistent with the sensor pixel mode(s) it was configured with.
+ // mCameraIdToStreamSet will only have ids that are high resolution
+ const auto streamIdSetIt = mHighResolutionCameraIdToStreamIdSet.find(it.id);
+ if (streamIdSetIt != mHighResolutionCameraIdToStreamIdSet.end()) {
+ std::list<int> streamIdsUsedInRequest = getIntersection(streamIdSetIt->second,
+ outputStreamIds);
+ if (!request.mIsReprocess &&
+ !isSensorPixelModeConsistent(streamIdsUsedInRequest, it.settings)) {
+ ALOGE("%s: Camera %s: Request settings CONTROL_SENSOR_PIXEL_MODE not "
+ "consistent with configured streams. Rejecting request.",
+ __FUNCTION__, it.id.c_str());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
+ "Request settings CONTROL_SENSOR_PIXEL_MODE are not consistent with "
+ "streams configured");
+ }
+ }
+
String8 physicalId(it.id.c_str());
if (physicalId != mDevice->getId()) {
auto found = std::find(requestedPhysicalIds.begin(), requestedPhysicalIds.end(),
@@ -470,7 +515,7 @@
}
binder::Status CameraDeviceClient::endConfigure(int operatingMode,
- const hardware::camera2::impl::CameraMetadataNative& sessionParams,
+ const hardware::camera2::impl::CameraMetadataNative& sessionParams, int64_t startTimeMs,
std::vector<int>* offlineStreamIds /*out*/) {
ATRACE_CALL();
ALOGV("%s: ending configure (%d input stream, %zu output surfaces)",
@@ -492,7 +537,8 @@
return STATUS_ERROR(CameraService::ERROR_DISCONNECTED, "Camera device no longer alive");
}
- res = checkOperatingMode(operatingMode, mDevice->info(), mCameraIdStr);
+ res = camera3::SessionConfigurationUtils::checkOperatingMode(operatingMode, mDevice->info(),
+ mCameraIdStr);
if (!res.isOk()) {
return res;
}
@@ -545,256 +591,20 @@
for (const auto& offlineStreamId : *offlineStreamIds) {
mStreamInfoMap[offlineStreamId].supportsOffline = true;
}
+
+ nsecs_t configureEnd = systemTime();
+ int32_t configureDurationMs = ns2ms(configureEnd) - startTimeMs;
+ CameraServiceProxyWrapper::logStreamConfigured(mCameraIdStr, operatingMode,
+ false /*internalReconfig*/, configureDurationMs);
}
return res;
}
-binder::Status CameraDeviceClient::checkSurfaceType(size_t numBufferProducers,
- bool deferredConsumer, int surfaceType) {
- if (numBufferProducers > MAX_SURFACES_PER_STREAM) {
- ALOGE("%s: GraphicBufferProducer count %zu for stream exceeds limit of %d",
- __FUNCTION__, numBufferProducers, MAX_SURFACES_PER_STREAM);
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, "Surface count is too high");
- } else if ((numBufferProducers == 0) && (!deferredConsumer)) {
- ALOGE("%s: Number of consumers cannot be smaller than 1", __FUNCTION__);
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, "No valid consumers.");
- }
-
- bool validSurfaceType = ((surfaceType == OutputConfiguration::SURFACE_TYPE_SURFACE_VIEW) ||
- (surfaceType == OutputConfiguration::SURFACE_TYPE_SURFACE_TEXTURE));
-
- if (deferredConsumer && !validSurfaceType) {
- ALOGE("%s: Target surface has invalid surfaceType = %d.", __FUNCTION__, surfaceType);
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, "Target Surface is invalid");
- }
-
- return binder::Status::ok();
-}
-
-binder::Status CameraDeviceClient::checkPhysicalCameraId(
- const std::vector<std::string> &physicalCameraIds, const String8 &physicalCameraId,
- const String8 &logicalCameraId) {
- if (physicalCameraId.size() == 0) {
- return binder::Status::ok();
- }
- if (std::find(physicalCameraIds.begin(), physicalCameraIds.end(),
- physicalCameraId.string()) == physicalCameraIds.end()) {
- String8 msg = String8::format("Camera %s: Camera doesn't support physicalCameraId %s.",
- logicalCameraId.string(), physicalCameraId.string());
- ALOGE("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
- }
- return binder::Status::ok();
-}
-
-binder::Status CameraDeviceClient::checkOperatingMode(int operatingMode,
- const CameraMetadata &staticInfo, const String8 &cameraId) {
- if (operatingMode < 0) {
- String8 msg = String8::format(
- "Camera %s: Invalid operating mode %d requested", cameraId.string(), operatingMode);
- ALOGE("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
- msg.string());
- }
-
- bool isConstrainedHighSpeed = (operatingMode == ICameraDeviceUser::CONSTRAINED_HIGH_SPEED_MODE);
- if (isConstrainedHighSpeed) {
- camera_metadata_ro_entry_t entry = staticInfo.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
- bool isConstrainedHighSpeedSupported = false;
- for(size_t i = 0; i < entry.count; ++i) {
- uint8_t capability = entry.data.u8[i];
- if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO) {
- isConstrainedHighSpeedSupported = true;
- break;
- }
- }
- if (!isConstrainedHighSpeedSupported) {
- String8 msg = String8::format(
- "Camera %s: Try to create a constrained high speed configuration on a device"
- " that doesn't support it.", cameraId.string());
- ALOGE("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
- msg.string());
- }
- }
-
- return binder::Status::ok();
-}
-
-void CameraDeviceClient::mapStreamInfo(const OutputStreamInfo &streamInfo,
- camera3_stream_rotation_t rotation, String8 physicalId,
- hardware::camera::device::V3_4::Stream *stream /*out*/) {
- if (stream == nullptr) {
- return;
- }
-
- stream->v3_2.streamType = hardware::camera::device::V3_2::StreamType::OUTPUT;
- stream->v3_2.width = streamInfo.width;
- stream->v3_2.height = streamInfo.height;
- stream->v3_2.format = Camera3Device::mapToPixelFormat(streamInfo.format);
- auto u = streamInfo.consumerUsage;
- camera3::Camera3OutputStream::applyZSLUsageQuirk(streamInfo.format, &u);
- stream->v3_2.usage = Camera3Device::mapToConsumerUsage(u);
- stream->v3_2.dataSpace = Camera3Device::mapToHidlDataspace(streamInfo.dataSpace);
- stream->v3_2.rotation = Camera3Device::mapToStreamRotation(rotation);
- stream->v3_2.id = -1; // Invalid stream id
- stream->physicalCameraId = std::string(physicalId.string());
- stream->bufferSize = 0;
-}
-
-binder::Status
-CameraDeviceClient::convertToHALStreamCombination(const SessionConfiguration& sessionConfiguration,
- const String8 &logicalCameraId, const CameraMetadata &deviceInfo,
- metadataGetter getMetadata, const std::vector<std::string> &physicalCameraIds,
- hardware::camera::device::V3_4::StreamConfiguration &streamConfiguration,
- bool *unsupported) {
- auto operatingMode = sessionConfiguration.getOperatingMode();
- binder::Status res = checkOperatingMode(operatingMode, deviceInfo, logicalCameraId);
- if (!res.isOk()) {
- return res;
- }
-
- if (unsupported == nullptr) {
- String8 msg("unsupported nullptr");
- ALOGE("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
- }
- *unsupported = false;
- auto ret = Camera3Device::mapToStreamConfigurationMode(
- static_cast<camera3_stream_configuration_mode_t> (operatingMode),
- /*out*/ &streamConfiguration.operationMode);
- if (ret != OK) {
- String8 msg = String8::format(
- "Camera %s: Failed mapping operating mode %d requested: %s (%d)",
- logicalCameraId.string(), operatingMode, strerror(-ret), ret);
- ALOGE("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
- msg.string());
- }
-
- bool isInputValid = (sessionConfiguration.getInputWidth() > 0) &&
- (sessionConfiguration.getInputHeight() > 0) &&
- (sessionConfiguration.getInputFormat() > 0);
- auto outputConfigs = sessionConfiguration.getOutputConfigurations();
- size_t streamCount = outputConfigs.size();
- streamCount = isInputValid ? streamCount + 1 : streamCount;
- streamConfiguration.streams.resize(streamCount);
- size_t streamIdx = 0;
- if (isInputValid) {
- streamConfiguration.streams[streamIdx++] = {{/*streamId*/0,
- hardware::camera::device::V3_2::StreamType::INPUT,
- static_cast<uint32_t> (sessionConfiguration.getInputWidth()),
- static_cast<uint32_t> (sessionConfiguration.getInputHeight()),
- Camera3Device::mapToPixelFormat(sessionConfiguration.getInputFormat()),
- /*usage*/ 0, HAL_DATASPACE_UNKNOWN,
- hardware::camera::device::V3_2::StreamRotation::ROTATION_0},
- /*physicalId*/ nullptr, /*bufferSize*/0};
- }
-
- for (const auto &it : outputConfigs) {
- const std::vector<sp<IGraphicBufferProducer>>& bufferProducers =
- it.getGraphicBufferProducers();
- bool deferredConsumer = it.isDeferred();
- String8 physicalCameraId = String8(it.getPhysicalCameraId());
- size_t numBufferProducers = bufferProducers.size();
- bool isStreamInfoValid = false;
- OutputStreamInfo streamInfo;
-
- res = checkSurfaceType(numBufferProducers, deferredConsumer, it.getSurfaceType());
- if (!res.isOk()) {
- return res;
- }
- res = checkPhysicalCameraId(physicalCameraIds, physicalCameraId,
- logicalCameraId);
- if (!res.isOk()) {
- return res;
- }
-
- if (deferredConsumer) {
- streamInfo.width = it.getWidth();
- streamInfo.height = it.getHeight();
- streamInfo.format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
- streamInfo.dataSpace = android_dataspace_t::HAL_DATASPACE_UNKNOWN;
- auto surfaceType = it.getSurfaceType();
- streamInfo.consumerUsage = GraphicBuffer::USAGE_HW_TEXTURE;
- if (surfaceType == OutputConfiguration::SURFACE_TYPE_SURFACE_VIEW) {
- streamInfo.consumerUsage |= GraphicBuffer::USAGE_HW_COMPOSER;
- }
- mapStreamInfo(streamInfo, CAMERA3_STREAM_ROTATION_0, physicalCameraId,
- &streamConfiguration.streams[streamIdx++]);
- isStreamInfoValid = true;
-
- if (numBufferProducers == 0) {
- continue;
- }
- }
-
- for (auto& bufferProducer : bufferProducers) {
- sp<Surface> surface;
- const CameraMetadata &physicalDeviceInfo = getMetadata(physicalCameraId);
- res = createSurfaceFromGbp(streamInfo, isStreamInfoValid, surface, bufferProducer,
- logicalCameraId,
- physicalCameraId.size() > 0 ? physicalDeviceInfo : deviceInfo );
-
- if (!res.isOk())
- return res;
-
- if (!isStreamInfoValid) {
- bool isDepthCompositeStream =
- camera3::DepthCompositeStream::isDepthCompositeStream(surface);
- bool isHeicCompositeStream =
- camera3::HeicCompositeStream::isHeicCompositeStream(surface);
- if (isDepthCompositeStream || isHeicCompositeStream) {
- // We need to take in to account that composite streams can have
- // additional internal camera streams.
- std::vector<OutputStreamInfo> compositeStreams;
- if (isDepthCompositeStream) {
- ret = camera3::DepthCompositeStream::getCompositeStreamInfo(streamInfo,
- deviceInfo, &compositeStreams);
- } else {
- ret = camera3::HeicCompositeStream::getCompositeStreamInfo(streamInfo,
- deviceInfo, &compositeStreams);
- }
- if (ret != OK) {
- String8 msg = String8::format(
- "Camera %s: Failed adding composite streams: %s (%d)",
- logicalCameraId.string(), strerror(-ret), ret);
- ALOGE("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
- }
-
- if (compositeStreams.size() == 0) {
- // No internal streams means composite stream not
- // supported.
- *unsupported = true;
- return binder::Status::ok();
- } else if (compositeStreams.size() > 1) {
- streamCount += compositeStreams.size() - 1;
- streamConfiguration.streams.resize(streamCount);
- }
-
- for (const auto& compositeStream : compositeStreams) {
- mapStreamInfo(compositeStream,
- static_cast<camera3_stream_rotation_t> (it.getRotation()),
- physicalCameraId, &streamConfiguration.streams[streamIdx++]);
- }
- } else {
- mapStreamInfo(streamInfo,
- static_cast<camera3_stream_rotation_t> (it.getRotation()),
- physicalCameraId, &streamConfiguration.streams[streamIdx++]);
- }
- isStreamInfoValid = true;
- }
- }
- }
- return binder::Status::ok();
-}
-
binder::Status CameraDeviceClient::isSessionConfigurationSupported(
const SessionConfiguration& sessionConfiguration, bool *status /*out*/) {
- ATRACE_CALL();
+ ATRACE_CALL();
binder::Status res;
status_t ret = OK;
if (!(res = checkPidStatus(__FUNCTION__)).isOk()) return res;
@@ -806,7 +616,8 @@
}
auto operatingMode = sessionConfiguration.getOperatingMode();
- res = checkOperatingMode(operatingMode, mDevice->info(), mCameraIdStr);
+ res = camera3::SessionConfigurationUtils::checkOperatingMode(operatingMode, mDevice->info(),
+ mCameraIdStr);
if (!res.isOk()) {
return res;
}
@@ -816,13 +627,14 @@
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
}
- hardware::camera::device::V3_4::StreamConfiguration streamConfiguration;
+ hardware::camera::device::V3_7::StreamConfiguration streamConfiguration;
bool earlyExit = false;
metadataGetter getMetadata = [this](const String8 &id) {return mDevice->infoPhysical(id);};
std::vector<std::string> physicalCameraIds;
mProviderManager->isLogicalCamera(mCameraIdStr.string(), &physicalCameraIds);
- res = convertToHALStreamCombination(sessionConfiguration, mCameraIdStr,
- mDevice->info(), getMetadata, physicalCameraIds, streamConfiguration, &earlyExit);
+ res = camera3::SessionConfigurationUtils::convertToHALStreamCombination(sessionConfiguration,
+ mCameraIdStr, mDevice->info(), getMetadata, physicalCameraIds, streamConfiguration,
+ &earlyExit);
if (!res.isOk()) {
return res;
}
@@ -945,6 +757,13 @@
}
mCompositeStreamMap.removeItemsAt(compositeIndex);
}
+ for (auto &mapIt: mHighResolutionCameraIdToStreamIdSet) {
+ auto &streamSet = mapIt.second;
+ if (streamSet.find(streamId) != streamSet.end()) {
+ streamSet.erase(streamId);
+ break;
+ }
+ }
}
}
@@ -969,8 +788,9 @@
bool isShared = outputConfiguration.isShared();
String8 physicalCameraId = String8(outputConfiguration.getPhysicalCameraId());
bool deferredConsumerOnly = deferredConsumer && numBufferProducers == 0;
+ bool isMultiResolution = outputConfiguration.isMultiResolution();
- res = checkSurfaceType(numBufferProducers, deferredConsumer,
+ res = camera3::SessionConfigurationUtils::checkSurfaceType(numBufferProducers, deferredConsumer,
outputConfiguration.getSurfaceType());
if (!res.isOk()) {
return res;
@@ -979,9 +799,8 @@
if (!mDevice.get()) {
return STATUS_ERROR(CameraService::ERROR_DISCONNECTED, "Camera device no longer alive");
}
- std::vector<std::string> physicalCameraIds;
- mProviderManager->isLogicalCamera(mCameraIdStr.string(), &physicalCameraIds);
- res = checkPhysicalCameraId(physicalCameraIds, physicalCameraId, mCameraIdStr);
+ res = camera3::SessionConfigurationUtils::checkPhysicalCameraId(mPhysicalCameraIds,
+ physicalCameraId, mCameraIdStr);
if (!res.isOk()) {
return res;
}
@@ -997,6 +816,8 @@
OutputStreamInfo streamInfo;
bool isStreamInfoValid = false;
+ const std::vector<int32_t> &sensorPixelModesUsed =
+ outputConfiguration.getSensorPixelModesUsed();
for (auto& bufferProducer : bufferProducers) {
// Don't create multiple streams for the same target surface
sp<IBinder> binder = IInterface::asBinder(bufferProducer);
@@ -1009,8 +830,9 @@
}
sp<Surface> surface;
- res = createSurfaceFromGbp(streamInfo, isStreamInfoValid, surface, bufferProducer,
- mCameraIdStr, mDevice->infoPhysical(physicalCameraId));
+ res = camera3::SessionConfigurationUtils::createSurfaceFromGbp(streamInfo,
+ isStreamInfoValid, surface, bufferProducer, mCameraIdStr,
+ mDevice->infoPhysical(physicalCameraId), sensorPixelModesUsed);
if (!res.isOk())
return res;
@@ -1022,10 +844,10 @@
binders.push_back(IInterface::asBinder(bufferProducer));
surfaces.push_back(surface);
}
-
int streamId = camera3::CAMERA3_STREAM_ID_INVALID;
std::vector<int> surfaceIds;
- bool isDepthCompositeStream = camera3::DepthCompositeStream::isDepthCompositeStream(surfaces[0]);
+ bool isDepthCompositeStream =
+ camera3::DepthCompositeStream::isDepthCompositeStream(surfaces[0]);
bool isHeicCompisiteStream = camera3::HeicCompositeStream::isHeicCompositeStream(surfaces[0]);
if (isDepthCompositeStream || isHeicCompisiteStream) {
sp<CompositeStream> compositeStream;
@@ -1037,9 +859,9 @@
err = compositeStream->createStream(surfaces, deferredConsumer, streamInfo.width,
streamInfo.height, streamInfo.format,
- static_cast<camera3_stream_rotation_t>(outputConfiguration.getRotation()),
- &streamId, physicalCameraId, &surfaceIds, outputConfiguration.getSurfaceSetID(),
- isShared);
+ static_cast<camera_stream_rotation_t>(outputConfiguration.getRotation()),
+ &streamId, physicalCameraId, streamInfo.sensorPixelModesUsed, &surfaceIds,
+ outputConfiguration.getSurfaceSetID(), isShared, isMultiResolution);
if (err == OK) {
mCompositeStreamMap.add(IInterface::asBinder(surfaces[0]->getIGraphicBufferProducer()),
compositeStream);
@@ -1047,9 +869,9 @@
} else {
err = mDevice->createStream(surfaces, deferredConsumer, streamInfo.width,
streamInfo.height, streamInfo.format, streamInfo.dataSpace,
- static_cast<camera3_stream_rotation_t>(outputConfiguration.getRotation()),
- &streamId, physicalCameraId, &surfaceIds, outputConfiguration.getSurfaceSetID(),
- isShared);
+ static_cast<camera_stream_rotation_t>(outputConfiguration.getRotation()),
+ &streamId, physicalCameraId, streamInfo.sensorPixelModesUsed, &surfaceIds,
+ outputConfiguration.getSurfaceSetID(), isShared, isMultiResolution);
}
if (err != OK) {
@@ -1077,6 +899,16 @@
// Set transform flags to ensure preview to be rotated correctly.
res = setStreamTransformLocked(streamId);
+ // Fill in mHighResolutionCameraIdToStreamIdSet map
+ const String8 &cameraIdUsed =
+ physicalCameraId.size() != 0 ? physicalCameraId : mCameraIdStr;
+ const char *cameraIdUsedCStr = cameraIdUsed.string();
+ // Only needed for high resolution sensors
+ if (mHighResolutionSensors.find(cameraIdUsedCStr) !=
+ mHighResolutionSensors.end()) {
+ mHighResolutionCameraIdToStreamIdSet[cameraIdUsedCStr].insert(streamId);
+ }
+
*newStreamId = streamId;
}
@@ -1113,12 +945,27 @@
std::vector<sp<Surface>> noSurface;
std::vector<int> surfaceIds;
String8 physicalCameraId(outputConfiguration.getPhysicalCameraId());
+ const String8 &cameraIdUsed =
+ physicalCameraId.size() != 0 ? physicalCameraId : mCameraIdStr;
+ // Here, we override sensor pixel modes
+ std::unordered_set<int32_t> overriddenSensorPixelModesUsed;
+ const std::vector<int32_t> &sensorPixelModesUsed =
+ outputConfiguration.getSensorPixelModesUsed();
+ if (camera3::SessionConfigurationUtils::checkAndOverrideSensorPixelModesUsed(
+ sensorPixelModesUsed, format, width, height, getStaticInfo(cameraIdUsed),
+ /*allowRounding*/ false, &overriddenSensorPixelModesUsed) != OK) {
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
+ "sensor pixel modes used not valid for deferred stream");
+ }
+
err = mDevice->createStream(noSurface, /*hasDeferredConsumer*/true, width,
height, format, dataSpace,
- static_cast<camera3_stream_rotation_t>(outputConfiguration.getRotation()),
- &streamId, physicalCameraId, &surfaceIds,
+ static_cast<camera_stream_rotation_t>(outputConfiguration.getRotation()),
+ &streamId, physicalCameraId,
+ overriddenSensorPixelModesUsed,
+ &surfaceIds,
outputConfiguration.getSurfaceSetID(), isShared,
- consumerUsage);
+ outputConfiguration.isMultiResolution(), consumerUsage);
if (err != OK) {
res = STATUS_ERROR_FMT(CameraService::ERROR_INVALID_OPERATION,
@@ -1129,9 +976,9 @@
// a separate list to track. Once the deferred surface is set, this id will be
// relocated to mStreamMap.
mDeferredStreams.push_back(streamId);
-
mStreamInfoMap.emplace(std::piecewise_construct, std::forward_as_tuple(streamId),
- std::forward_as_tuple(width, height, format, dataSpace, consumerUsage));
+ std::forward_as_tuple(width, height, format, dataSpace, consumerUsage,
+ overriddenSensorPixelModesUsed));
ALOGV("%s: Camera %s: Successfully created a new stream ID %d for a deferred surface"
" (%d x %d) stream with format 0x%x.",
@@ -1141,6 +988,13 @@
res = setStreamTransformLocked(streamId);
*newStreamId = streamId;
+ // Fill in mHighResolutionCameraIdToStreamIdSet
+ const char *cameraIdUsedCStr = cameraIdUsed.string();
+ // Only needed for high resolution sensors
+ if (mHighResolutionSensors.find(cameraIdUsedCStr) !=
+ mHighResolutionSensors.end()) {
+ mHighResolutionCameraIdToStreamIdSet[cameraIdUsed.string()].insert(streamId);
+ }
}
return res;
}
@@ -1173,12 +1027,13 @@
}
binder::Status CameraDeviceClient::createInputStream(
- int width, int height, int format,
+ int width, int height, int format, bool isMultiResolution,
/*out*/
int32_t* newStreamId) {
ATRACE_CALL();
- ALOGV("%s (w = %d, h = %d, f = 0x%x)", __FUNCTION__, width, height, format);
+ ALOGV("%s (w = %d, h = %d, f = 0x%x, isMultiResolution %d)", __FUNCTION__,
+ width, height, format, isMultiResolution);
binder::Status res;
if (!(res = checkPidStatus(__FUNCTION__)).isOk()) return res;
@@ -1197,7 +1052,7 @@
}
int streamId = -1;
- status_t err = mDevice->createInputStream(width, height, format, &streamId);
+ status_t err = mDevice->createInputStream(width, height, format, isMultiResolution, &streamId);
if (err == OK) {
mInputStream.configured = true;
mInputStream.width = width;
@@ -1309,12 +1164,15 @@
newOutputsMap.removeItemsAt(idx);
}
}
+ const std::vector<int32_t> &sensorPixelModesUsed =
+ outputConfiguration.getSensorPixelModesUsed();
for (size_t i = 0; i < newOutputsMap.size(); i++) {
OutputStreamInfo outInfo;
sp<Surface> surface;
- res = createSurfaceFromGbp(outInfo, /*isStreamInfoValid*/ false, surface,
- newOutputsMap.valueAt(i), mCameraIdStr, mDevice->infoPhysical(physicalCameraId));
+ res = camera3::SessionConfigurationUtils::createSurfaceFromGbp(outInfo,
+ /*isStreamInfoValid*/ false, surface, newOutputsMap.valueAt(i), mCameraIdStr,
+ mDevice->infoPhysical(physicalCameraId), sensorPixelModesUsed);
if (!res.isOk())
return res;
@@ -1364,226 +1222,6 @@
return res;
}
-bool CameraDeviceClient::isPublicFormat(int32_t format)
-{
- switch(format) {
- case HAL_PIXEL_FORMAT_RGBA_8888:
- case HAL_PIXEL_FORMAT_RGBX_8888:
- case HAL_PIXEL_FORMAT_RGB_888:
- case HAL_PIXEL_FORMAT_RGB_565:
- case HAL_PIXEL_FORMAT_BGRA_8888:
- case HAL_PIXEL_FORMAT_YV12:
- case HAL_PIXEL_FORMAT_Y8:
- case HAL_PIXEL_FORMAT_Y16:
- case HAL_PIXEL_FORMAT_RAW16:
- case HAL_PIXEL_FORMAT_RAW10:
- case HAL_PIXEL_FORMAT_RAW12:
- case HAL_PIXEL_FORMAT_RAW_OPAQUE:
- case HAL_PIXEL_FORMAT_BLOB:
- case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
- case HAL_PIXEL_FORMAT_YCbCr_420_888:
- case HAL_PIXEL_FORMAT_YCbCr_422_SP:
- case HAL_PIXEL_FORMAT_YCrCb_420_SP:
- case HAL_PIXEL_FORMAT_YCbCr_422_I:
- return true;
- default:
- return false;
- }
-}
-
-binder::Status CameraDeviceClient::createSurfaceFromGbp(
- OutputStreamInfo& streamInfo, bool isStreamInfoValid,
- sp<Surface>& surface, const sp<IGraphicBufferProducer>& gbp,
- const String8 &cameraId, const CameraMetadata &physicalCameraMetadata) {
-
- // bufferProducer must be non-null
- if (gbp == nullptr) {
- String8 msg = String8::format("Camera %s: Surface is NULL", cameraId.string());
- ALOGW("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
- }
- // HACK b/10949105
- // Query consumer usage bits to set async operation mode for
- // GLConsumer using controlledByApp parameter.
- bool useAsync = false;
- uint64_t consumerUsage = 0;
- status_t err;
- if ((err = gbp->getConsumerUsage(&consumerUsage)) != OK) {
- String8 msg = String8::format("Camera %s: Failed to query Surface consumer usage: %s (%d)",
- cameraId.string(), strerror(-err), err);
- ALOGE("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
- }
- if (consumerUsage & GraphicBuffer::USAGE_HW_TEXTURE) {
- ALOGW("%s: Camera %s with consumer usage flag: %" PRIu64 ": Forcing asynchronous mode for stream",
- __FUNCTION__, cameraId.string(), consumerUsage);
- useAsync = true;
- }
-
- uint64_t disallowedFlags = GraphicBuffer::USAGE_HW_VIDEO_ENCODER |
- GRALLOC_USAGE_RENDERSCRIPT;
- uint64_t allowedFlags = GraphicBuffer::USAGE_SW_READ_MASK |
- GraphicBuffer::USAGE_HW_TEXTURE |
- GraphicBuffer::USAGE_HW_COMPOSER;
- bool flexibleConsumer = (consumerUsage & disallowedFlags) == 0 &&
- (consumerUsage & allowedFlags) != 0;
-
- surface = new Surface(gbp, useAsync);
- ANativeWindow *anw = surface.get();
-
- int width, height, format;
- android_dataspace dataSpace;
- if ((err = anw->query(anw, NATIVE_WINDOW_WIDTH, &width)) != OK) {
- String8 msg = String8::format("Camera %s: Failed to query Surface width: %s (%d)",
- cameraId.string(), strerror(-err), err);
- ALOGE("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
- }
- if ((err = anw->query(anw, NATIVE_WINDOW_HEIGHT, &height)) != OK) {
- String8 msg = String8::format("Camera %s: Failed to query Surface height: %s (%d)",
- cameraId.string(), strerror(-err), err);
- ALOGE("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
- }
- if ((err = anw->query(anw, NATIVE_WINDOW_FORMAT, &format)) != OK) {
- String8 msg = String8::format("Camera %s: Failed to query Surface format: %s (%d)",
- cameraId.string(), strerror(-err), err);
- ALOGE("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
- }
- if ((err = anw->query(anw, NATIVE_WINDOW_DEFAULT_DATASPACE,
- reinterpret_cast<int*>(&dataSpace))) != OK) {
- String8 msg = String8::format("Camera %s: Failed to query Surface dataspace: %s (%d)",
- cameraId.string(), strerror(-err), err);
- ALOGE("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
- }
-
- // FIXME: remove this override since the default format should be
- // IMPLEMENTATION_DEFINED. b/9487482 & b/35317944
- if ((format >= HAL_PIXEL_FORMAT_RGBA_8888 && format <= HAL_PIXEL_FORMAT_BGRA_8888) &&
- ((consumerUsage & GRALLOC_USAGE_HW_MASK) &&
- ((consumerUsage & GRALLOC_USAGE_SW_READ_MASK) == 0))) {
- ALOGW("%s: Camera %s: Overriding format %#x to IMPLEMENTATION_DEFINED",
- __FUNCTION__, cameraId.string(), format);
- format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
- }
- // Round dimensions to the nearest dimensions available for this format
- if (flexibleConsumer && isPublicFormat(format) &&
- !CameraDeviceClient::roundBufferDimensionNearest(width, height,
- format, dataSpace, physicalCameraMetadata, /*out*/&width, /*out*/&height)) {
- String8 msg = String8::format("Camera %s: No supported stream configurations with "
- "format %#x defined, failed to create output stream",
- cameraId.string(), format);
- ALOGE("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
- }
-
- if (!isStreamInfoValid) {
- streamInfo.width = width;
- streamInfo.height = height;
- streamInfo.format = format;
- streamInfo.dataSpace = dataSpace;
- streamInfo.consumerUsage = consumerUsage;
- return binder::Status::ok();
- }
- if (width != streamInfo.width) {
- String8 msg = String8::format("Camera %s:Surface width doesn't match: %d vs %d",
- cameraId.string(), width, streamInfo.width);
- ALOGE("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
- }
- if (height != streamInfo.height) {
- String8 msg = String8::format("Camera %s:Surface height doesn't match: %d vs %d",
- cameraId.string(), height, streamInfo.height);
- ALOGE("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
- }
- if (format != streamInfo.format) {
- String8 msg = String8::format("Camera %s:Surface format doesn't match: %d vs %d",
- cameraId.string(), format, streamInfo.format);
- ALOGE("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
- }
- if (format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
- if (dataSpace != streamInfo.dataSpace) {
- String8 msg = String8::format("Camera %s:Surface dataSpace doesn't match: %d vs %d",
- cameraId.string(), dataSpace, streamInfo.dataSpace);
- ALOGE("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
- }
- //At the native side, there isn't a way to check whether 2 surfaces come from the same
- //surface class type. Use usage flag to approximate the comparison.
- if (consumerUsage != streamInfo.consumerUsage) {
- String8 msg = String8::format(
- "Camera %s:Surface usage flag doesn't match %" PRIu64 " vs %" PRIu64 "",
- cameraId.string(), consumerUsage, streamInfo.consumerUsage);
- ALOGE("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
- }
- }
- return binder::Status::ok();
-}
-
-bool CameraDeviceClient::roundBufferDimensionNearest(int32_t width, int32_t height,
- int32_t format, android_dataspace dataSpace, const CameraMetadata& info,
- /*out*/int32_t* outWidth, /*out*/int32_t* outHeight) {
-
- camera_metadata_ro_entry streamConfigs =
- (dataSpace == HAL_DATASPACE_DEPTH) ?
- info.find(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS) :
- (dataSpace == static_cast<android_dataspace>(HAL_DATASPACE_HEIF)) ?
- info.find(ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS) :
- info.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
-
- int32_t bestWidth = -1;
- int32_t bestHeight = -1;
-
- // Iterate through listed stream configurations and find the one with the smallest euclidean
- // distance from the given dimensions for the given format.
- for (size_t i = 0; i < streamConfigs.count; i += 4) {
- int32_t fmt = streamConfigs.data.i32[i];
- int32_t w = streamConfigs.data.i32[i + 1];
- int32_t h = streamConfigs.data.i32[i + 2];
-
- // Ignore input/output type for now
- if (fmt == format) {
- if (w == width && h == height) {
- bestWidth = width;
- bestHeight = height;
- break;
- } else if (w <= ROUNDING_WIDTH_CAP && (bestWidth == -1 ||
- CameraDeviceClient::euclidDistSquare(w, h, width, height) <
- CameraDeviceClient::euclidDistSquare(bestWidth, bestHeight, width, height))) {
- bestWidth = w;
- bestHeight = h;
- }
- }
- }
-
- if (bestWidth == -1) {
- // Return false if no configurations for this format were listed
- return false;
- }
-
- // Set the outputs to the closet width/height
- if (outWidth != NULL) {
- *outWidth = bestWidth;
- }
- if (outHeight != NULL) {
- *outHeight = bestHeight;
- }
-
- // Return true if at least one configuration for this format was listed
- return true;
-}
-
-int64_t CameraDeviceClient::euclidDistSquare(int32_t x0, int32_t y0, int32_t x1, int32_t y1) {
- int64_t d0 = x0 - x1;
- int64_t d1 = y0 - y1;
- return d0 * d0 + d1 * d1;
-}
-
// Create a request object from a template.
binder::Status CameraDeviceClient::createDefaultRequest(int templateId,
/*out*/
@@ -1601,9 +1239,12 @@
return STATUS_ERROR(CameraService::ERROR_DISCONNECTED, "Camera device no longer alive");
}
- CameraMetadata metadata;
status_t err;
- if ( (err = mDevice->createDefaultRequest(templateId, &metadata) ) == OK &&
+ camera_request_template_t tempId = camera_request_template_t::CAMERA_TEMPLATE_COUNT;
+ if (!(res = mapRequestTemplate(templateId, &tempId)).isOk()) return res;
+
+ CameraMetadata metadata;
+ if ( (err = mDevice->createDefaultRequest(tempId, &metadata) ) == OK &&
request != NULL) {
request->swap(metadata);
@@ -1886,6 +1527,8 @@
}
std::vector<sp<Surface>> consumerSurfaces;
+ const std::vector<int32_t> &sensorPixelModesUsed =
+ outputConfiguration.getSensorPixelModesUsed();
for (auto& bufferProducer : bufferProducers) {
// Don't create multiple streams for the same target surface
ssize_t index = mStreamMap.indexOfKey(IInterface::asBinder(bufferProducer));
@@ -1896,8 +1539,9 @@
}
sp<Surface> surface;
- res = createSurfaceFromGbp(mStreamInfoMap[streamId], true /*isStreamInfoValid*/,
- surface, bufferProducer, mCameraIdStr, mDevice->infoPhysical(physicalId));
+ res = camera3::SessionConfigurationUtils::createSurfaceFromGbp(mStreamInfoMap[streamId],
+ true /*isStreamInfoValid*/, surface, bufferProducer, mCameraIdStr,
+ mDevice->infoPhysical(physicalId), sensorPixelModesUsed);
if (!res.isOk())
return res;
@@ -1977,6 +1621,14 @@
static_cast<camera_metadata_enum_android_scaler_rotate_and_crop_t>(rotateAndCrop));
}
+bool CameraDeviceClient::supportsCameraMute() {
+ return mDevice->supportsCameraMute();
+}
+
+status_t CameraDeviceClient::setCameraMute(bool enabled) {
+ return mDevice->setCameraMute(enabled);
+}
+
binder::Status CameraDeviceClient::switchToOffline(
const sp<hardware::camera2::ICameraDeviceCallbacks>& cameraCb,
const std::vector<int>& offlineOutputIds,
@@ -2062,7 +1714,8 @@
if (offlineSession.get() != nullptr) {
offlineClient = new CameraOfflineSessionClient(sCameraService,
offlineSession, offlineCompositeStreamMap, cameraCb, mClientPackageName,
- mClientFeatureId, mCameraIdStr, mCameraFacing, mClientPid, mClientUid, mServicePid);
+ mClientFeatureId, mCameraIdStr, mCameraFacing, mOrientation, mClientPid, mClientUid,
+ mServicePid);
ret = sCameraService->addOfflineClient(mCameraIdStr, offlineClient);
}
@@ -2162,14 +1815,16 @@
mStreamingRequestId = REQUEST_ID_NONE;
}
-void CameraDeviceClient::notifyIdle() {
+void CameraDeviceClient::notifyIdle(
+ int64_t requestCount, int64_t resultErrorCount, bool deviceError,
+ const std::vector<hardware::CameraStreamStats>& streamStats) {
// Thread safe. Don't bother locking.
sp<hardware::camera2::ICameraDeviceCallbacks> remoteCb = getRemoteCallback();
if (remoteCb != 0) {
remoteCb->onDeviceIdle();
}
- Camera2ClientBase::notifyIdle();
+ Camera2ClientBase::notifyIdle(requestCount, resultErrorCount, deviceError, streamStats);
}
void CameraDeviceClient::notifyShutter(const CaptureResultExtras& resultExtras,
@@ -2205,6 +1860,7 @@
void CameraDeviceClient::detachDevice() {
if (mDevice == 0) return;
+ nsecs_t startTime = systemTime();
ALOGV("Camera %s: Stopping processors", mCameraIdStr.string());
mFrameProcessor->removeListener(camera2::FrameProcessorBase::FRAME_PROCESSOR_LISTENER_MIN_ID,
@@ -2239,6 +1895,9 @@
mCompositeStreamMap.clear();
Camera2ClientBase::detachDevice();
+
+ int32_t closeLatencyMs = ns2ms(systemTime() - startTime);
+ CameraServiceProxyWrapper::logClose(mCameraIdStr, closeLatencyMs);
}
/** Device-related methods */
@@ -2328,4 +1987,91 @@
return CameraUtils::getRotationTransform(staticInfo, transform);
}
+binder::Status CameraDeviceClient::mapRequestTemplate(int templateId,
+ camera_request_template_t* tempId /*out*/) {
+ binder::Status ret = binder::Status::ok();
+
+ if (tempId == nullptr) {
+ ret = STATUS_ERROR_FMT(CameraService::ERROR_ILLEGAL_ARGUMENT,
+ "Camera %s: Invalid template argument", mCameraIdStr.string());
+ return ret;
+ }
+ switch(templateId) {
+ case ICameraDeviceUser::TEMPLATE_PREVIEW:
+ *tempId = camera_request_template_t::CAMERA_TEMPLATE_PREVIEW;
+ break;
+ case ICameraDeviceUser::TEMPLATE_RECORD:
+ *tempId = camera_request_template_t::CAMERA_TEMPLATE_VIDEO_RECORD;
+ break;
+ case ICameraDeviceUser::TEMPLATE_STILL_CAPTURE:
+ *tempId = camera_request_template_t::CAMERA_TEMPLATE_STILL_CAPTURE;
+ break;
+ case ICameraDeviceUser::TEMPLATE_VIDEO_SNAPSHOT:
+ *tempId = camera_request_template_t::CAMERA_TEMPLATE_VIDEO_SNAPSHOT;
+ break;
+ case ICameraDeviceUser::TEMPLATE_ZERO_SHUTTER_LAG:
+ *tempId = camera_request_template_t::CAMERA_TEMPLATE_ZERO_SHUTTER_LAG;
+ break;
+ case ICameraDeviceUser::TEMPLATE_MANUAL:
+ *tempId = camera_request_template_t::CAMERA_TEMPLATE_MANUAL;
+ break;
+ default:
+ ret = STATUS_ERROR_FMT(CameraService::ERROR_ILLEGAL_ARGUMENT,
+ "Camera %s: Template ID %d is invalid or not supported",
+ mCameraIdStr.string(), templateId);
+ return ret;
+ }
+
+ return ret;
+}
+
+const CameraMetadata &CameraDeviceClient::getStaticInfo(const String8 &cameraId) {
+ if (mDevice->getId() == cameraId) {
+ return mDevice->info();
+ }
+ return mDevice->infoPhysical(cameraId);
+}
+
+bool CameraDeviceClient::isUltraHighResolutionSensor(const String8 &cameraId) {
+ const CameraMetadata &deviceInfo = getStaticInfo(cameraId);
+ return camera3::SessionConfigurationUtils::isUltraHighResolutionSensor(deviceInfo);
+}
+
+bool CameraDeviceClient::isSensorPixelModeConsistent(
+ const std::list<int> &streamIdList, const CameraMetadata &settings) {
+ // First we get the sensorPixelMode from the settings metadata.
+ int32_t sensorPixelMode = ANDROID_SENSOR_PIXEL_MODE_DEFAULT;
+ camera_metadata_ro_entry sensorPixelModeEntry = settings.find(ANDROID_SENSOR_PIXEL_MODE);
+ if (sensorPixelModeEntry.count != 0) {
+ sensorPixelMode = sensorPixelModeEntry.data.u8[0];
+ if (sensorPixelMode != ANDROID_SENSOR_PIXEL_MODE_DEFAULT &&
+ sensorPixelMode != ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) {
+ ALOGE("%s: Request sensor pixel mode not is not one of the valid values %d",
+ __FUNCTION__, sensorPixelMode);
+ return false;
+ }
+ }
+ // Check whether each stream has max resolution allowed.
+ bool consistent = true;
+ for (auto it : streamIdList) {
+ auto const streamInfoIt = mStreamInfoMap.find(it);
+ if (streamInfoIt == mStreamInfoMap.end()) {
+ ALOGE("%s: stream id %d not created, skipping", __FUNCTION__, it);
+ return false;
+ }
+ consistent =
+ streamInfoIt->second.sensorPixelModesUsed.find(sensorPixelMode) !=
+ streamInfoIt->second.sensorPixelModesUsed.end();
+ if (!consistent) {
+ ALOGE("sensorPixelMode used %i not consistent with configured modes", sensorPixelMode);
+ for (auto m : streamInfoIt->second.sensorPixelModesUsed) {
+ ALOGE("sensor pixel mode used list: %i", m);
+ }
+ break;
+ }
+ }
+
+ return consistent;
+}
+
} // namespace android
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 5cd16ee..44ffeef 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -28,6 +28,7 @@
#include "common/FrameProcessorBase.h"
#include "common/Camera2ClientBase.h"
#include "CompositeStream.h"
+#include "utils/SessionConfigurationUtils.h"
using android::camera3::OutputStreamInfo;
using android::camera3::CompositeStream;
@@ -54,6 +55,7 @@
const String8& cameraId,
int api1CameraId,
int cameraFacing,
+ int sensorOrientation,
int clientPid,
uid_t clientUid,
int servicePid);
@@ -95,6 +97,7 @@
virtual binder::Status endConfigure(int operatingMode,
const hardware::camera2::impl::CameraMetadataNative& sessionParams,
+ int64_t startTimeMs,
/*out*/
std::vector<int>* offlineStreamIds) override;
@@ -114,6 +117,7 @@
// Create an input stream of width, height, and format.
virtual binder::Status createInputStream(int width, int height, int format,
+ bool isMultiResolution,
/*out*/
int32_t* newStreamId = NULL) override;
@@ -178,6 +182,7 @@
const std::optional<String16>& clientFeatureId,
const String8& cameraId,
int cameraFacing,
+ int sensorOrientation,
int clientPid,
uid_t clientUid,
int servicePid);
@@ -188,6 +193,9 @@
virtual status_t setRotateAndCropOverride(uint8_t rotateAndCrop) override;
+ virtual bool supportsCameraMute();
+ virtual status_t setCameraMute(bool enabled);
+
virtual status_t dump(int fd, const Vector<String16>& args);
virtual status_t dumpClient(int fd, const Vector<String16>& args);
@@ -196,7 +204,8 @@
* Device listener interface
*/
- virtual void notifyIdle();
+ virtual void notifyIdle(int64_t requestCount, int64_t resultErrorCount, bool deviceError,
+ const std::vector<hardware::CameraStreamStats>& streamStats);
virtual void notifyError(int32_t errorCode,
const CaptureResultExtras& resultExtras);
virtual void notifyShutter(const CaptureResultExtras& resultExtras, nsecs_t timestamp);
@@ -204,16 +213,7 @@
virtual void notifyRequestQueueEmpty();
virtual void notifyRepeatingRequestError(long lastFrameNumber);
- // utility function to convert AIDL SessionConfiguration to HIDL
- // streamConfiguration. Also checks for validity of SessionConfiguration and
- // returns a non-ok binder::Status if the passed in session configuration
- // isn't valid.
- static binder::Status
- convertToHALStreamCombination(const SessionConfiguration& sessionConfiguration,
- const String8 &cameraId, const CameraMetadata &deviceInfo,
- metadataGetter getMetadata, const std::vector<std::string> &physicalCameraIds,
- hardware::camera::device::V3_4::StreamConfiguration &streamConfiguration,
- bool *earlyExit);
+ void setImageDumpMask(int mask) { if (mDevice != nullptr) mDevice->setImageDumpMask(mask); }
/**
* Interface used by independent components of CameraDeviceClient.
*/
@@ -225,6 +225,13 @@
// Calculate the ANativeWindow transform from android.sensor.orientation
status_t getRotationTransformLocked(/*out*/int32_t* transform);
+ bool isUltraHighResolutionSensor(const String8 &cameraId);
+
+ bool isSensorPixelModeConsistent(const std::list<int> &streamIdList,
+ const CameraMetadata &settings);
+
+ const CameraMetadata &getStaticInfo(const String8 &cameraId);
+
private:
// StreamSurfaceId encapsulates streamId + surfaceId for a particular surface.
// streamId specifies the index of the stream the surface belongs to, and the
@@ -266,18 +273,8 @@
/** Utility members */
binder::Status checkPidStatus(const char* checkLocation);
- static binder::Status checkOperatingMode(int operatingMode, const CameraMetadata &staticInfo,
- const String8 &cameraId);
- static binder::Status checkSurfaceType(size_t numBufferProducers, bool deferredConsumer,
- int surfaceType);
- static void mapStreamInfo(const OutputStreamInfo &streamInfo,
- camera3_stream_rotation_t rotation, String8 physicalId,
- hardware::camera::device::V3_4::Stream *stream /*out*/);
bool enforceRequestPermissions(CameraMetadata& metadata);
- // Find the square of the euclidean distance between two points
- static int64_t euclidDistSquare(int32_t x0, int32_t y0, int32_t x1, int32_t y1);
-
// Create an output stream with surface deferred for future.
binder::Status createDeferredSurfaceStreamLocked(
const hardware::camera2::params::OutputConfiguration &outputConfiguration,
@@ -288,32 +285,14 @@
// cases.
binder::Status setStreamTransformLocked(int streamId);
- // Find the closest dimensions for a given format in available stream configurations with
- // a width <= ROUNDING_WIDTH_CAP
- static const int32_t ROUNDING_WIDTH_CAP = 1920;
- static bool roundBufferDimensionNearest(int32_t width, int32_t height, int32_t format,
- android_dataspace dataSpace, const CameraMetadata& info,
- /*out*/int32_t* outWidth, /*out*/int32_t* outHeight);
-
- //check if format is not custom format
- static bool isPublicFormat(int32_t format);
-
- // Create a Surface from an IGraphicBufferProducer. Returns error if
- // IGraphicBufferProducer's property doesn't match with streamInfo
- static binder::Status createSurfaceFromGbp(OutputStreamInfo& streamInfo, bool isStreamInfoValid,
- sp<Surface>& surface, const sp<IGraphicBufferProducer>& gbp, const String8 &cameraId,
- const CameraMetadata &physicalCameraMetadata);
-
-
// Utility method to insert the surface into SurfaceMap
binder::Status insertGbpLocked(const sp<IGraphicBufferProducer>& gbp,
/*out*/SurfaceMap* surfaceMap, /*out*/Vector<int32_t>* streamIds,
/*out*/int32_t* currentStreamId);
- // Check that the physicalCameraId passed in is spported by the camera
- // device.
- static binder::Status checkPhysicalCameraId(const std::vector<std::string> &physicalCameraIds,
- const String8 &physicalCameraId, const String8 &logicalCameraId);
+ // Utility method that maps AIDL request templates.
+ binder::Status mapRequestTemplate(int templateId,
+ camera_request_template_t* tempId /*out*/);
// IGraphicsBufferProducer binder -> Stream ID + Surface ID for output streams
KeyedVector<sp<IBinder>, StreamSurfaceId> mStreamMap;
@@ -336,6 +315,8 @@
int32_t mRequestIdCounter;
+ std::vector<std::string> mPhysicalCameraIds;
+
// The list of output streams whose surfaces are deferred. We have to track them separately
// as there are no surfaces available and can not be put into mStreamMap. Once the deferred
// Surface is configured, the stream id will be moved to mStreamMap.
@@ -344,9 +325,14 @@
// stream ID -> outputStreamInfo mapping
std::unordered_map<int32_t, OutputStreamInfo> mStreamInfoMap;
+ // map high resolution camera id (logical / physical) -> list of stream ids configured
+ std::unordered_map<std::string, std::unordered_set<int>> mHighResolutionCameraIdToStreamIdSet;
+
+ // set of high resolution camera id (logical / physical)
+ std::unordered_set<std::string> mHighResolutionSensors;
+
KeyedVector<sp<IBinder>, sp<CompositeStream>> mCompositeStreamMap;
- static const int32_t MAX_SURFACES_PER_STREAM = 4;
sp<CameraProviderManager> mProviderManager;
};
diff --git a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
index 237c24b..ef15f2d 100644
--- a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
@@ -72,6 +72,16 @@
return OK;
}
+bool CameraOfflineSessionClient::supportsCameraMute() {
+ // Offline mode doesn't support muting
+ return false;
+}
+
+status_t CameraOfflineSessionClient::setCameraMute(bool) {
+ return INVALID_OPERATION;
+}
+
+
status_t CameraOfflineSessionClient::dump(int fd, const Vector<String16>& args) {
return BasicClient::dump(fd, args);
}
@@ -265,10 +275,17 @@
}
}
-void CameraOfflineSessionClient::notifyIdle() {
+status_t CameraOfflineSessionClient::notifyActive() {
+ return startCameraStreamingOps();
+}
+
+void CameraOfflineSessionClient::notifyIdle(
+ int64_t /*requestCount*/, int64_t /*resultErrorCount*/, bool /*deviceError*/,
+ const std::vector<hardware::CameraStreamStats>& /*streamStats*/) {
if (mRemoteCallback.get() != nullptr) {
mRemoteCallback->onDeviceIdle();
}
+ finishCameraStreamingOps();
}
void CameraOfflineSessionClient::notifyAutoFocus(uint8_t newState, int triggerId) {
diff --git a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
index 03621c8..b219a4c 100644
--- a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
+++ b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
@@ -49,13 +49,13 @@
const sp<ICameraDeviceCallbacks>& remoteCallback,
const String16& clientPackageName,
const std::optional<String16>& clientFeatureId,
- const String8& cameraIdStr, int cameraFacing,
+ const String8& cameraIdStr, int cameraFacing, int sensorOrientation,
int clientPid, uid_t clientUid, int servicePid) :
CameraService::BasicClient(
cameraService,
IInterface::asBinder(remoteCallback),
clientPackageName, clientFeatureId,
- cameraIdStr, cameraFacing, clientPid, clientUid, servicePid),
+ cameraIdStr, cameraFacing, sensorOrientation, clientPid, clientUid, servicePid),
mRemoteCallback(remoteCallback), mOfflineSession(session),
mCompositeStreamMap(offlineCompositeStreamMap) {}
@@ -76,6 +76,9 @@
status_t setRotateAndCropOverride(uint8_t rotateAndCrop) override;
+ bool supportsCameraMute() override;
+ status_t setCameraMute(bool enabled) override;
+
// permissions management
status_t startCameraOps() override;
status_t finishCameraOps() override;
@@ -86,7 +89,9 @@
// NotificationListener API
void notifyError(int32_t errorCode, const CaptureResultExtras& resultExtras) override;
void notifyShutter(const CaptureResultExtras& resultExtras, nsecs_t timestamp) override;
- void notifyIdle() override;
+ status_t notifyActive() override;
+ void notifyIdle(int64_t requestCount, int64_t resultErrorCount, bool deviceError,
+ const std::vector<hardware::CameraStreamStats>& streamStats) override;
void notifyAutoFocus(uint8_t newState, int triggerId) override;
void notifyAutoExposure(uint8_t newState, int triggerId) override;
void notifyAutoWhitebalance(uint8_t newState, int triggerId) override;
diff --git a/services/camera/libcameraservice/api2/CompositeStream.cpp b/services/camera/libcameraservice/api2/CompositeStream.cpp
index a61dac7..4b840fc 100644
--- a/services/camera/libcameraservice/api2/CompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/CompositeStream.cpp
@@ -46,8 +46,10 @@
status_t CompositeStream::createStream(const std::vector<sp<Surface>>& consumers,
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
- camera3_stream_rotation_t rotation, int * id, const String8& physicalCameraId,
- std::vector<int> * surfaceIds, int streamSetId, bool isShared) {
+ camera_stream_rotation_t rotation, int * id, const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ std::vector<int> * surfaceIds,
+ int streamSetId, bool isShared, bool isMultiResolution) {
if (hasDeferredConsumer) {
ALOGE("%s: Deferred consumers not supported in case of composite streams!",
__FUNCTION__);
@@ -66,8 +68,14 @@
return BAD_VALUE;
}
- return createInternalStreams(consumers, hasDeferredConsumer, width, height, format, rotation, id,
- physicalCameraId, surfaceIds, streamSetId, isShared);
+ if (isMultiResolution) {
+ ALOGE("%s: Multi-resolution output not supported in case of composite streams!",
+ __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ return createInternalStreams(consumers, hasDeferredConsumer, width, height, format, rotation,
+ id, physicalCameraId, sensorPixelModesUsed, surfaceIds, streamSetId, isShared);
}
status_t CompositeStream::deleteStream() {
diff --git a/services/camera/libcameraservice/api2/CompositeStream.h b/services/camera/libcameraservice/api2/CompositeStream.h
index 5f62d47..600bd28 100644
--- a/services/camera/libcameraservice/api2/CompositeStream.h
+++ b/services/camera/libcameraservice/api2/CompositeStream.h
@@ -43,8 +43,10 @@
status_t createStream(const std::vector<sp<Surface>>& consumers,
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
- camera3_stream_rotation_t rotation, int *id, const String8& physicalCameraId,
- std::vector<int> *surfaceIds, int streamSetId, bool isShared);
+ camera_stream_rotation_t rotation, int *id, const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ std::vector<int> *surfaceIds,
+ int streamSetId, bool isShared, bool isMultiResolution);
status_t deleteStream();
@@ -54,8 +56,10 @@
// Create and register all internal camera streams.
virtual status_t createInternalStreams(const std::vector<sp<Surface>>& consumers,
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
- camera3_stream_rotation_t rotation, int *id, const String8& physicalCameraId,
- std::vector<int> *surfaceIds, int streamSetId, bool isShared) = 0;
+ camera_stream_rotation_t rotation, int *id, const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ std::vector<int> *surfaceIds,
+ int streamSetId, bool isShared) = 0;
// Release all internal streams and corresponding resources.
virtual status_t deleteInternalStreams() = 0;
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
index c6859be..19b54e0 100644
--- a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
@@ -20,6 +20,7 @@
#include "api1/client2/JpegProcessor.h"
#include "common/CameraProviderManager.h"
+#include "utils/SessionConfigurationUtils.h"
#include <gui/Surface.h>
#include <utils/Log.h>
#include <utils/Trace.h>
@@ -78,7 +79,10 @@
}
}
- getSupportedDepthSizes(staticInfo, &mSupportedDepthSizes);
+ getSupportedDepthSizes(staticInfo, /*maxResolution*/false, &mSupportedDepthSizes);
+ if (SessionConfigurationUtils::isUltraHighResolutionSensor(staticInfo)) {
+ getSupportedDepthSizes(staticInfo, true, &mSupportedDepthSizesMaximumResolution);
+ }
}
}
@@ -341,7 +345,7 @@
return res;
}
- size_t finalJpegSize = actualJpegSize + sizeof(struct camera3_jpeg_blob);
+ size_t finalJpegSize = actualJpegSize + sizeof(struct camera_jpeg_blob);
if (finalJpegSize > finalJpegBufferSize) {
ALOGE("%s: Final jpeg buffer not large enough for the jpeg blob header", __FUNCTION__);
outputANW->cancelBuffer(mOutputSurface.get(), anb, /*fence*/ -1);
@@ -357,9 +361,9 @@
ALOGV("%s: Final jpeg size: %zu", __func__, finalJpegSize);
uint8_t* header = static_cast<uint8_t *> (dstBuffer) +
- (gb->getWidth() - sizeof(struct camera3_jpeg_blob));
- struct camera3_jpeg_blob *blob = reinterpret_cast<struct camera3_jpeg_blob*> (header);
- blob->jpeg_blob_id = CAMERA3_JPEG_BLOB_ID;
+ (gb->getWidth() - sizeof(struct camera_jpeg_blob));
+ struct camera_jpeg_blob *blob = reinterpret_cast<struct camera_jpeg_blob*> (header);
+ blob->jpeg_blob_id = CAMERA_JPEG_BLOB_ID;
blob->jpeg_size = actualJpegSize;
outputANW->queueBuffer(mOutputSurface.get(), anb, /*fence*/ -1);
@@ -484,17 +488,82 @@
return false;
}
+static bool setContains(std::unordered_set<int32_t> containerSet, int32_t value) {
+ return containerSet.find(value) != containerSet.end();
+}
+
+status_t DepthCompositeStream::checkAndGetMatchingDepthSize(size_t width, size_t height,
+ const std::vector<std::tuple<size_t, size_t>> &depthSizes,
+ const std::vector<std::tuple<size_t, size_t>> &depthSizesMaximumResolution,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ size_t *depthWidth, size_t *depthHeight) {
+ if (depthWidth == nullptr || depthHeight == nullptr) {
+ return BAD_VALUE;
+ }
+ size_t chosenDepthWidth = 0, chosenDepthHeight = 0;
+ bool hasDefaultSensorPixelMode =
+ setContains(sensorPixelModesUsed, ANDROID_SENSOR_PIXEL_MODE_DEFAULT);
+
+ bool hasMaximumResolutionSensorPixelMode =
+ setContains(sensorPixelModesUsed, ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION);
+
+ if (!hasDefaultSensorPixelMode && !hasMaximumResolutionSensorPixelMode) {
+ ALOGE("%s: sensor pixel modes don't contain either maximum resolution or default modes",
+ __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ if (hasDefaultSensorPixelMode) {
+ auto ret = getMatchingDepthSize(width, height, depthSizes, &chosenDepthWidth,
+ &chosenDepthHeight);
+ if (ret != OK) {
+ ALOGE("%s: No matching depth stream size found", __FUNCTION__);
+ return ret;
+ }
+ }
+
+ if (hasMaximumResolutionSensorPixelMode) {
+ size_t depthWidth = 0, depthHeight = 0;
+ auto ret = getMatchingDepthSize(width, height,
+ depthSizesMaximumResolution, &depthWidth, &depthHeight);
+ if (ret != OK) {
+ ALOGE("%s: No matching max resolution depth stream size found", __FUNCTION__);
+ return ret;
+ }
+ // Both matching depth sizes should be the same.
+ if (chosenDepthWidth != 0 && chosenDepthWidth != depthWidth &&
+ chosenDepthHeight != depthHeight) {
+ ALOGE("%s: Maximum resolution sensor pixel mode and default sensor pixel mode don't"
+ " have matching depth sizes", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ if (chosenDepthWidth == 0) {
+ chosenDepthWidth = depthWidth;
+ chosenDepthHeight = depthHeight;
+ }
+ }
+ *depthWidth = chosenDepthWidth;
+ *depthHeight = chosenDepthHeight;
+ return OK;
+}
+
+
status_t DepthCompositeStream::createInternalStreams(const std::vector<sp<Surface>>& consumers,
bool /*hasDeferredConsumer*/, uint32_t width, uint32_t height, int format,
- camera3_stream_rotation_t rotation, int *id, const String8& physicalCameraId,
- std::vector<int> *surfaceIds, int /*streamSetId*/, bool /*isShared*/) {
+ camera_stream_rotation_t rotation, int *id, const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ std::vector<int> *surfaceIds,
+ int /*streamSetId*/, bool /*isShared*/) {
if (mSupportedDepthSizes.empty()) {
ALOGE("%s: This camera device doesn't support any depth map streams!", __FUNCTION__);
return INVALID_OPERATION;
}
size_t depthWidth, depthHeight;
- auto ret = getMatchingDepthSize(width, height, mSupportedDepthSizes, &depthWidth, &depthHeight);
+ auto ret =
+ checkAndGetMatchingDepthSize(width, height, mSupportedDepthSizes,
+ mSupportedDepthSizesMaximumResolution, sensorPixelModesUsed, &depthWidth,
+ &depthHeight);
if (ret != OK) {
ALOGE("%s: Failed to find an appropriate depth stream size!", __FUNCTION__);
return ret;
@@ -515,7 +584,7 @@
mBlobSurface = new Surface(producer);
ret = device->createStream(mBlobSurface, width, height, format, kJpegDataSpace, rotation,
- id, physicalCameraId, surfaceIds);
+ id, physicalCameraId, sensorPixelModesUsed, surfaceIds);
if (ret == OK) {
mBlobStreamId = *id;
mBlobSurfaceId = (*surfaceIds)[0];
@@ -531,7 +600,8 @@
mDepthSurface = new Surface(producer);
std::vector<int> depthSurfaceId;
ret = device->createStream(mDepthSurface, depthWidth, depthHeight, kDepthMapPixelFormat,
- kDepthMapDataSpace, rotation, &mDepthStreamId, physicalCameraId, &depthSurfaceId);
+ kDepthMapDataSpace, rotation, &mDepthStreamId, physicalCameraId, sensorPixelModesUsed,
+ &depthSurfaceId);
if (ret == OK) {
mDepthSurfaceId = depthSurfaceId[0];
} else {
@@ -666,13 +736,11 @@
status_t DepthCompositeStream::insertGbp(SurfaceMap* /*out*/outSurfaceMap,
Vector<int32_t> * /*out*/outputStreamIds, int32_t* /*out*/currentStreamId) {
if (outSurfaceMap->find(mDepthStreamId) == outSurfaceMap->end()) {
- (*outSurfaceMap)[mDepthStreamId] = std::vector<size_t>();
outputStreamIds->push_back(mDepthStreamId);
}
(*outSurfaceMap)[mDepthStreamId].push_back(mDepthSurfaceId);
if (outSurfaceMap->find(mBlobStreamId) == outSurfaceMap->end()) {
- (*outSurfaceMap)[mBlobStreamId] = std::vector<size_t>();
outputStreamIds->push_back(mBlobStreamId);
}
(*outSurfaceMap)[mBlobStreamId].push_back(mBlobSurfaceId);
@@ -751,13 +819,15 @@
return ((*depthWidth > 0) && (*depthHeight > 0)) ? OK : BAD_VALUE;
}
-void DepthCompositeStream::getSupportedDepthSizes(const CameraMetadata& ch,
+void DepthCompositeStream::getSupportedDepthSizes(const CameraMetadata& ch, bool maxResolution,
std::vector<std::tuple<size_t, size_t>>* depthSizes /*out*/) {
if (depthSizes == nullptr) {
return;
}
- auto entry = ch.find(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS);
+ auto entry = ch.find(
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, maxResolution));
if (entry.count > 0) {
// Depth stream dimensions have four int32_t components
// (pixelformat, width, height, type)
@@ -781,30 +851,43 @@
}
std::vector<std::tuple<size_t, size_t>> depthSizes;
- getSupportedDepthSizes(ch, &depthSizes);
+ std::vector<std::tuple<size_t, size_t>> depthSizesMaximumResolution;
+ getSupportedDepthSizes(ch, /*maxResolution*/false, &depthSizes);
if (depthSizes.empty()) {
ALOGE("%s: No depth stream configurations present", __FUNCTION__);
return BAD_VALUE;
}
- size_t depthWidth, depthHeight;
- auto ret = getMatchingDepthSize(streamInfo.width, streamInfo.height, depthSizes, &depthWidth,
- &depthHeight);
+ if (SessionConfigurationUtils::isUltraHighResolutionSensor(ch)) {
+ getSupportedDepthSizes(ch, /*maxResolution*/true, &depthSizesMaximumResolution);
+ if (depthSizesMaximumResolution.empty()) {
+ ALOGE("%s: No depth stream configurations for maximum resolution present",
+ __FUNCTION__);
+ return BAD_VALUE;
+ }
+ }
+
+ size_t chosenDepthWidth = 0, chosenDepthHeight = 0;
+ auto ret = checkAndGetMatchingDepthSize(streamInfo.width, streamInfo.height, depthSizes,
+ depthSizesMaximumResolution, streamInfo.sensorPixelModesUsed, &chosenDepthWidth,
+ &chosenDepthHeight);
+
if (ret != OK) {
- ALOGE("%s: No matching depth stream size found", __FUNCTION__);
+ ALOGE("%s: Couldn't get matching depth sizes", __FUNCTION__);
return ret;
}
compositeOutput->clear();
compositeOutput->insert(compositeOutput->end(), 2, streamInfo);
+ // Sensor pixel modes should stay the same here. They're already overridden.
// Jpeg/Blob stream info
(*compositeOutput)[0].dataSpace = kJpegDataSpace;
(*compositeOutput)[0].consumerUsage = GRALLOC_USAGE_SW_READ_OFTEN;
// Depth stream info
- (*compositeOutput)[1].width = depthWidth;
- (*compositeOutput)[1].height = depthHeight;
+ (*compositeOutput)[1].width = chosenDepthWidth;
+ (*compositeOutput)[1].height = chosenDepthHeight;
(*compositeOutput)[1].format = kDepthMapPixelFormat;
(*compositeOutput)[1].dataSpace = kDepthMapDataSpace;
(*compositeOutput)[1].consumerUsage = GRALLOC_USAGE_SW_READ_OFTEN;
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.h b/services/camera/libcameraservice/api2/DepthCompositeStream.h
index cab52b6..a520bbf 100644
--- a/services/camera/libcameraservice/api2/DepthCompositeStream.h
+++ b/services/camera/libcameraservice/api2/DepthCompositeStream.h
@@ -50,8 +50,10 @@
// CompositeStream overrides
status_t createInternalStreams(const std::vector<sp<Surface>>& consumers,
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
- camera3_stream_rotation_t rotation, int *id, const String8& physicalCameraId,
- std::vector<int> *surfaceIds, int streamSetId, bool isShared) override;
+ camera_stream_rotation_t rotation, int *id, const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ std::vector<int> *surfaceIds,
+ int streamSetId, bool isShared) override;
status_t deleteInternalStreams() override;
status_t configureStream() override;
status_t insertGbp(SurfaceMap* /*out*/outSurfaceMap, Vector<int32_t>* /*out*/outputStreamIds,
@@ -86,11 +88,17 @@
};
// Helper methods
- static void getSupportedDepthSizes(const CameraMetadata& ch,
+ static void getSupportedDepthSizes(const CameraMetadata& ch, bool maxResolution,
std::vector<std::tuple<size_t, size_t>>* depthSizes /*out*/);
static status_t getMatchingDepthSize(size_t width, size_t height,
const std::vector<std::tuple<size_t, size_t>>& supporedDepthSizes,
size_t *depthWidth /*out*/, size_t *depthHeight /*out*/);
+ static status_t checkAndGetMatchingDepthSize(size_t width, size_t height,
+ const std::vector<std::tuple<size_t, size_t>> &depthSizes,
+ const std::vector<std::tuple<size_t, size_t>> &depthSizesMaximumResolution,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ size_t *depthWidth /*out*/, size_t *depthHeight /*out*/);
+
// Dynamic depth processing
status_t encodeGrayscaleJpeg(size_t width, size_t height, uint8_t *in, void *out,
@@ -126,6 +134,7 @@
ssize_t mMaxJpegSize;
std::vector<std::tuple<size_t, size_t>> mSupportedDepthSizes;
+ std::vector<std::tuple<size_t, size_t>> mSupportedDepthSizesMaximumResolution;
std::vector<float> mIntrinsicCalibration, mLensDistortion;
bool mIsLogicalCamera;
diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
index 2462fd5..582001d 100644
--- a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
@@ -36,6 +36,7 @@
#include "common/CameraDeviceBase.h"
#include "utils/ExifUtils.h"
+#include "utils/SessionConfigurationUtils.h"
#include "HeicEncoderInfoManager.h"
#include "HeicCompositeStream.h"
@@ -114,8 +115,10 @@
status_t HeicCompositeStream::createInternalStreams(const std::vector<sp<Surface>>& consumers,
bool /*hasDeferredConsumer*/, uint32_t width, uint32_t height, int format,
- camera3_stream_rotation_t rotation, int *id, const String8& physicalCameraId,
- std::vector<int> *surfaceIds, int /*streamSetId*/, bool /*isShared*/) {
+ camera_stream_rotation_t rotation, int *id, const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ std::vector<int> *surfaceIds,
+ int /*streamSetId*/, bool /*isShared*/) {
sp<CameraDeviceBase> device = mDevice.promote();
if (!device.get()) {
@@ -141,7 +144,8 @@
mStaticInfo = device->info();
res = device->createStream(mAppSegmentSurface, mAppSegmentMaxSize, 1, format,
- kAppSegmentDataSpace, rotation, &mAppSegmentStreamId, physicalCameraId, surfaceIds);
+ kAppSegmentDataSpace, rotation, &mAppSegmentStreamId, physicalCameraId,
+ sensorPixelModesUsed,surfaceIds);
if (res == OK) {
mAppSegmentSurfaceId = (*surfaceIds)[0];
} else {
@@ -177,7 +181,7 @@
int srcStreamFmt = mUseGrid ? HAL_PIXEL_FORMAT_YCbCr_420_888 :
HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
res = device->createStream(mMainImageSurface, width, height, srcStreamFmt, kHeifDataSpace,
- rotation, id, physicalCameraId, &sourceSurfaceId);
+ rotation, id, physicalCameraId, sensorPixelModesUsed, &sourceSurfaceId);
if (res == OK) {
mMainImageSurfaceId = sourceSurfaceId[0];
mMainImageStreamId = *id;
@@ -509,7 +513,8 @@
sp<camera3::StatusTracker> statusTracker = mStatusTracker.promote();
if (statusTracker != nullptr) {
- mStatusId = statusTracker->addComponent();
+ std::string name = std::string("HeicStream ") + std::to_string(getStreamId());
+ mStatusId = statusTracker->addComponent(name);
}
run("HeicCompositeStreamProc");
@@ -520,13 +525,11 @@
status_t HeicCompositeStream::insertGbp(SurfaceMap* /*out*/outSurfaceMap,
Vector<int32_t>* /*out*/outputStreamIds, int32_t* /*out*/currentStreamId) {
if (outSurfaceMap->find(mAppSegmentStreamId) == outSurfaceMap->end()) {
- (*outSurfaceMap)[mAppSegmentStreamId] = std::vector<size_t>();
outputStreamIds->push_back(mAppSegmentStreamId);
}
(*outSurfaceMap)[mAppSegmentStreamId].push_back(mAppSegmentSurfaceId);
if (outSurfaceMap->find(mMainImageStreamId) == outSurfaceMap->end()) {
- (*outSurfaceMap)[mMainImageStreamId] = std::vector<size_t>();
outputStreamIds->push_back(mMainImageStreamId);
}
(*outSurfaceMap)[mMainImageStreamId].push_back(mMainImageSurfaceId);
diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.h b/services/camera/libcameraservice/api2/HeicCompositeStream.h
index a373127..1077a1f 100644
--- a/services/camera/libcameraservice/api2/HeicCompositeStream.h
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.h
@@ -45,8 +45,10 @@
status_t createInternalStreams(const std::vector<sp<Surface>>& consumers,
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
- camera3_stream_rotation_t rotation, int *id, const String8& physicalCameraId,
- std::vector<int> *surfaceIds, int streamSetId, bool isShared) override;
+ camera_stream_rotation_t rotation, int *id, const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ std::vector<int> *surfaceIds,
+ int streamSetId, bool isShared) override;
status_t deleteInternalStreams() override;
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index 609698c..ce479a1 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -27,12 +27,15 @@
#include <gui/Surface.h>
#include <gui/Surface.h>
+#include <camera/CameraSessionStats.h>
+
#include "common/Camera2ClientBase.h"
#include "api2/CameraDeviceClient.h"
#include "device3/Camera3Device.h"
#include "utils/CameraThreadState.h"
+#include "utils/CameraServiceProxyWrapper.h"
namespace android {
using namespace camera2;
@@ -48,11 +51,13 @@
const String8& cameraId,
int api1CameraId,
int cameraFacing,
+ int sensorOrientation,
int clientPid,
uid_t clientUid,
int servicePid):
TClientBase(cameraService, remoteCallback, clientPackageName, clientFeatureId,
- cameraId, api1CameraId, cameraFacing, clientPid, clientUid, servicePid),
+ cameraId, api1CameraId, cameraFacing, sensorOrientation, clientPid, clientUid,
+ servicePid),
mSharedCameraCallbacks(remoteCallback),
mDeviceVersion(cameraService->getDeviceVersion(TClientBase::mCameraIdStr)),
mDevice(new Camera3Device(cameraId)),
@@ -194,7 +199,7 @@
CameraService::BasicClient::disconnect();
- ALOGV("Camera %s: Shut down complete complete", TClientBase::mCameraIdStr.string());
+ ALOGV("Camera %s: Shut down complete", TClientBase::mCameraIdStr.string());
return res;
}
@@ -245,13 +250,34 @@
}
template <typename TClientBase>
-void Camera2ClientBase<TClientBase>::notifyIdle() {
+status_t Camera2ClientBase<TClientBase>::notifyActive() {
+ if (!mDeviceActive) {
+ status_t res = TClientBase::startCameraStreamingOps();
+ if (res != OK) {
+ ALOGE("%s: Camera %s: Error starting camera streaming ops: %d", __FUNCTION__,
+ TClientBase::mCameraIdStr.string(), res);
+ return res;
+ }
+ CameraServiceProxyWrapper::logActive(TClientBase::mCameraIdStr);
+ }
+ mDeviceActive = true;
+
+ ALOGV("Camera device is now active");
+ return OK;
+}
+
+template <typename TClientBase>
+void Camera2ClientBase<TClientBase>::notifyIdle(
+ int64_t requestCount, int64_t resultErrorCount, bool deviceError,
+ const std::vector<hardware::CameraStreamStats>& streamStats) {
if (mDeviceActive) {
- getCameraService()->updateProxyDeviceState(
- hardware::ICameraServiceProxy::CAMERA_STATE_IDLE, TClientBase::mCameraIdStr,
- TClientBase::mCameraFacing, TClientBase::mClientPackageName,
- ((mApi1CameraId < 0) ? hardware::ICameraServiceProxy::CAMERA_API_LEVEL_2 :
- hardware::ICameraServiceProxy::CAMERA_API_LEVEL_1));
+ status_t res = TClientBase::finishCameraStreamingOps();
+ if (res != OK) {
+ ALOGE("%s: Camera %s: Error finishing streaming ops: %d", __FUNCTION__,
+ TClientBase::mCameraIdStr.string(), res);
+ }
+ CameraServiceProxyWrapper::logIdle(TClientBase::mCameraIdStr,
+ requestCount, resultErrorCount, deviceError, streamStats);
}
mDeviceActive = false;
@@ -264,15 +290,6 @@
(void)resultExtras;
(void)timestamp;
- if (!mDeviceActive) {
- getCameraService()->updateProxyDeviceState(
- hardware::ICameraServiceProxy::CAMERA_STATE_ACTIVE, TClientBase::mCameraIdStr,
- TClientBase::mCameraFacing, TClientBase::mClientPackageName,
- ((mApi1CameraId < 0) ? hardware::ICameraServiceProxy::CAMERA_API_LEVEL_2 :
- hardware::ICameraServiceProxy::CAMERA_API_LEVEL_1));
- }
- mDeviceActive = true;
-
ALOGV("%s: Shutter notification for request id %" PRId32 " at time %" PRId64,
__FUNCTION__, resultExtras.requestId, timestamp);
}
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index d7506af..b3a38a2 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -52,6 +52,7 @@
const String8& cameraId,
int api1CameraId,
int cameraFacing,
+ int sensorOrientation,
int clientPid,
uid_t clientUid,
int servicePid);
@@ -66,7 +67,10 @@
virtual void notifyError(int32_t errorCode,
const CaptureResultExtras& resultExtras);
- virtual void notifyIdle();
+ virtual status_t notifyActive(); // Returns errors on app ops permission failures
+ virtual void notifyIdle(int64_t requestCount, int64_t resultErrorCount,
+ bool deviceError,
+ const std::vector<hardware::CameraStreamStats>& streamStats);
virtual void notifyShutter(const CaptureResultExtras& resultExtras,
nsecs_t timestamp);
virtual void notifyAutoFocus(uint8_t newState, int triggerId);
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index a537ef5..85b0cc2 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -28,7 +28,6 @@
#include <utils/List.h>
#include "hardware/camera2.h"
-#include "hardware/camera3.h"
#include "camera/CameraMetadata.h"
#include "camera/CaptureResult.h"
#include "gui/IGraphicBufferProducer.h"
@@ -41,6 +40,41 @@
namespace android {
+namespace camera3 {
+
+typedef enum camera_request_template {
+ CAMERA_TEMPLATE_PREVIEW = 1,
+ CAMERA_TEMPLATE_STILL_CAPTURE = 2,
+ CAMERA_TEMPLATE_VIDEO_RECORD = 3,
+ CAMERA_TEMPLATE_VIDEO_SNAPSHOT = 4,
+ CAMERA_TEMPLATE_ZERO_SHUTTER_LAG = 5,
+ CAMERA_TEMPLATE_MANUAL = 6,
+ CAMERA_TEMPLATE_COUNT,
+ CAMERA_VENDOR_TEMPLATE_START = 0x40000000
+} camera_request_template_t;
+
+typedef enum camera_stream_configuration_mode {
+ CAMERA_STREAM_CONFIGURATION_NORMAL_MODE = 0,
+ CAMERA_STREAM_CONFIGURATION_CONSTRAINED_HIGH_SPEED_MODE = 1,
+ CAMERA_VENDOR_STREAM_CONFIGURATION_MODE_START = 0x8000
+} camera_stream_configuration_mode_t;
+
+typedef struct camera_jpeg_blob {
+ uint16_t jpeg_blob_id;
+ uint32_t jpeg_size;
+} camera_jpeg_blob_t;
+
+enum {
+ CAMERA_JPEG_BLOB_ID = 0x00FF,
+ CAMERA_JPEG_APP_SEGMENTS_BLOB_ID = 0x0100,
+};
+
+} // namespace camera3
+
+using camera3::camera_request_template_t;;
+using camera3::camera_stream_configuration_mode_t;
+using camera3::camera_stream_rotation_t;
+
class CameraProviderManager;
// Mapping of output stream index to surface ids
@@ -128,11 +162,13 @@
*/
virtual status_t createStream(sp<Surface> consumer,
uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
+ android_dataspace dataSpace, camera_stream_rotation_t rotation, int *id,
const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
std::vector<int> *surfaceIds = nullptr,
int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
- bool isShared = false, uint64_t consumerUsage = 0) = 0;
+ bool isShared = false, bool isMultiResolution = false,
+ uint64_t consumerUsage = 0) = 0;
/**
* Create an output stream of the requested size, format, rotation and
@@ -143,11 +179,13 @@
*/
virtual status_t createStream(const std::vector<sp<Surface>>& consumers,
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
+ android_dataspace dataSpace, camera_stream_rotation_t rotation, int *id,
const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
std::vector<int> *surfaceIds = nullptr,
int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
- bool isShared = false, uint64_t consumerUsage = 0) = 0;
+ bool isShared = false, bool isMultiResolution = false,
+ uint64_t consumerUsage = 0) = 0;
/**
* Create an input stream of width, height, and format.
@@ -155,7 +193,7 @@
* Return value is the stream ID if non-negative and an error if negative.
*/
virtual status_t createInputStream(uint32_t width, uint32_t height,
- int32_t format, /*out*/ int32_t *id) = 0;
+ int32_t format, bool multiResolution, /*out*/ int32_t *id) = 0;
struct StreamInfo {
uint32_t width;
@@ -225,7 +263,8 @@
* - INVALID_OPERATION if the device was in the wrong state
*/
virtual status_t configureStreams(const CameraMetadata& sessionParams,
- int operatingMode = 0) = 0;
+ int operatingMode =
+ camera_stream_configuration_mode_t::CAMERA_STREAM_CONFIGURATION_NORMAL_MODE) = 0;
/**
* Retrieve a list of all stream ids that were advertised as capable of
@@ -241,7 +280,7 @@
* Create a metadata buffer with fields that the HAL device believes are
* best for the given use case
*/
- virtual status_t createDefaultRequest(int templateId,
+ virtual status_t createDefaultRequest(camera_request_template_t templateId,
CameraMetadata *request) = 0;
/**
@@ -364,9 +403,32 @@
camera_metadata_enum_android_scaler_rotate_and_crop_t rotateAndCropValue) = 0;
/**
+ * Whether camera muting (producing black-only output) is supported.
+ *
+ * Calling setCameraMute(true) when this returns false will return an
+ * INVALID_OPERATION error.
+ */
+ virtual bool supportsCameraMute() = 0;
+
+ /**
+ * Mute the camera.
+ *
+ * When muted, black image data is output on all output streams.
+ */
+ virtual status_t setCameraMute(bool enabled) = 0;
+
+ /**
* Get the status tracker of the camera device
*/
virtual wp<camera3::StatusTracker> getStatusTracker() = 0;
+
+ /**
+ * Set bitmask for image dump flag
+ */
+ void setImageDumpMask(int mask) { mImageDumpMask = mask; }
+
+protected:
+ bool mImageDumpMask = 0;
};
}; // namespace android
diff --git a/services/camera/libcameraservice/common/CameraOfflineSessionBase.h b/services/camera/libcameraservice/common/CameraOfflineSessionBase.h
index 1f835a9..54e42a6 100644
--- a/services/camera/libcameraservice/common/CameraOfflineSessionBase.h
+++ b/services/camera/libcameraservice/common/CameraOfflineSessionBase.h
@@ -17,11 +17,14 @@
#ifndef ANDROID_SERVERS_CAMERA_CAMERAOFFLINESESSIONBASE_H
#define ANDROID_SERVERS_CAMERA_CAMERAOFFLINESESSIONBASE_H
+#include <vector>
+
#include <utils/RefBase.h>
#include <utils/String8.h>
#include <utils/Timers.h>
#include "camera/CaptureResult.h"
+#include "camera/CameraSessionStats.h"
#include "FrameProducer.h"
namespace android {
@@ -37,9 +40,11 @@
// Required for API 1 and 2
virtual void notifyError(int32_t errorCode,
const CaptureResultExtras &resultExtras) = 0;
+ virtual status_t notifyActive() = 0; // May return an error since it checks appops
+ virtual void notifyIdle(int64_t requestCount, int64_t resultError, bool deviceError,
+ const std::vector<hardware::CameraStreamStats>& streamStats) = 0;
// Required only for API2
- virtual void notifyIdle() = 0;
virtual void notifyShutter(const CaptureResultExtras &resultExtras,
nsecs_t timestamp) = 0;
virtual void notifyPrepared(int streamId) = 0;
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index 876d70d..6dffc5d 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -20,7 +20,7 @@
#include "CameraProviderManager.h"
-#include <android/hardware/camera/device/3.5/ICameraDevice.h>
+#include <android/hardware/camera/device/3.7/ICameraDevice.h>
#include <algorithm>
#include <chrono>
@@ -28,7 +28,6 @@
#include <dlfcn.h>
#include <future>
#include <inttypes.h>
-#include <hardware/camera_common.h>
#include <android/hidl/manager/1.2/IServiceManager.h>
#include <hidl/ServiceManagement.h>
#include <functional>
@@ -49,7 +48,7 @@
using namespace ::android::hardware::camera::common::V1_0;
using std::literals::chrono_literals::operator""s;
using hardware::camera2::utils::CameraIdAndSessionConfiguration;
-using hardware::camera::provider::V2_6::CameraIdAndStreamCombination;
+using hardware::camera::provider::V2_7::CameraIdAndStreamCombination;
namespace {
const bool kEnableLazyHal(property_get_bool("ro.camera.enableLazyHal", false));
@@ -267,7 +266,7 @@
}
status_t CameraProviderManager::isSessionConfigurationSupported(const std::string& id,
- const hardware::camera::device::V3_4::StreamConfiguration &configuration,
+ const hardware::camera::device::V3_7::StreamConfiguration &configuration,
bool *status /*out*/) const {
std::lock_guard<std::mutex> lock(mInterfaceMutex);
auto deviceInfo = findDeviceInfoLocked(id);
@@ -417,46 +416,6 @@
return mapToStatusT(status);
}
-status_t CameraProviderManager::openSession(const std::string &id,
- const sp<device::V1_0::ICameraDeviceCallback>& callback,
- /*out*/
- sp<device::V1_0::ICameraDevice> *session) {
-
- std::lock_guard<std::mutex> lock(mInterfaceMutex);
-
- auto deviceInfo = findDeviceInfoLocked(id,
- /*minVersion*/ {1,0}, /*maxVersion*/ {2,0});
- if (deviceInfo == nullptr) return NAME_NOT_FOUND;
-
- auto *deviceInfo1 = static_cast<ProviderInfo::DeviceInfo1*>(deviceInfo);
- sp<ProviderInfo> parentProvider = deviceInfo->mParentProvider.promote();
- if (parentProvider == nullptr) {
- return DEAD_OBJECT;
- }
- const sp<provider::V2_4::ICameraProvider> provider = parentProvider->startProviderInterface();
- if (provider == nullptr) {
- return DEAD_OBJECT;
- }
- saveRef(DeviceMode::CAMERA, id, provider);
-
- auto interface = deviceInfo1->startDeviceInterface<
- CameraProviderManager::ProviderInfo::DeviceInfo1::InterfaceT>();
- if (interface == nullptr) {
- return DEAD_OBJECT;
- }
- hardware::Return<Status> status = interface->open(callback);
- if (!status.isOk()) {
- removeRef(DeviceMode::CAMERA, id);
- ALOGE("%s: Transaction error opening a session for camera device %s: %s",
- __FUNCTION__, id.c_str(), status.description().c_str());
- return DEAD_OBJECT;
- }
- if (status == Status::OK) {
- *session = interface;
- }
- return mapToStatusT(status);
-}
-
void CameraProviderManager::saveRef(DeviceMode usageType, const std::string &cameraId,
sp<provider::V2_4::ICameraProvider> provider) {
if (!kEnableLazyHal) {
@@ -515,12 +474,12 @@
hardware::Return<void> CameraProviderManager::onRegistration(
const hardware::hidl_string& /*fqName*/,
const hardware::hidl_string& name,
- bool /*preexisting*/) {
+ bool preexisting) {
std::lock_guard<std::mutex> providerLock(mProviderLifecycleLock);
{
std::lock_guard<std::mutex> lock(mInterfaceMutex);
- addProviderLocked(name);
+ addProviderLocked(name, preexisting);
}
sp<StatusListener> listener = getStatusListener();
@@ -727,9 +686,39 @@
}
}
-status_t CameraProviderManager::ProviderInfo::DeviceInfo3::addDynamicDepthTags() {
- uint32_t depthExclTag = ANDROID_DEPTH_DEPTH_IS_EXCLUSIVE;
- uint32_t depthSizesTag = ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS;
+status_t CameraProviderManager::ProviderInfo::DeviceInfo3::addDynamicDepthTags(
+ bool maxResolution) {
+ const int32_t depthExclTag = ANDROID_DEPTH_DEPTH_IS_EXCLUSIVE;
+
+ const int32_t scalerSizesTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, maxResolution);
+ const int32_t scalerMinFrameDurationsTag =
+ ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS;
+ const int32_t scalerStallDurationsTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_SCALER_AVAILABLE_STALL_DURATIONS, maxResolution);
+
+ const int32_t depthSizesTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, maxResolution);
+ const int32_t depthStallDurationsTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS, maxResolution);
+ const int32_t depthMinFrameDurationsTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS, maxResolution);
+
+ const int32_t dynamicDepthSizesTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS, maxResolution);
+ const int32_t dynamicDepthStallDurationsTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS, maxResolution);
+ const int32_t dynamicDepthMinFrameDurationsTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS, maxResolution);
+
auto& c = mCameraCharacteristics;
std::vector<std::tuple<size_t, size_t>> supportedBlobSizes, supportedDepthSizes,
supportedDynamicDepthSizes, internalDepthSizes;
@@ -759,7 +748,7 @@
return BAD_VALUE;
}
- getSupportedSizes(c, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, HAL_PIXEL_FORMAT_BLOB,
+ getSupportedSizes(c, scalerSizesTag, HAL_PIXEL_FORMAT_BLOB,
&supportedBlobSizes);
getSupportedSizes(c, depthSizesTag, HAL_PIXEL_FORMAT_Y16, &supportedDepthSizes);
if (supportedBlobSizes.empty() || supportedDepthSizes.empty()) {
@@ -786,10 +775,10 @@
std::vector<int64_t> blobMinDurations, blobStallDurations;
std::vector<int64_t> dynamicDepthMinDurations, dynamicDepthStallDurations;
- getSupportedDurations(c, ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS,
- HAL_PIXEL_FORMAT_Y16, internalDepthSizes, &depthMinDurations);
- getSupportedDurations(c, ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
- HAL_PIXEL_FORMAT_BLOB, supportedDynamicDepthSizes, &blobMinDurations);
+ getSupportedDurations(c, depthMinFrameDurationsTag, HAL_PIXEL_FORMAT_Y16, internalDepthSizes,
+ &depthMinDurations);
+ getSupportedDurations(c, scalerMinFrameDurationsTag, HAL_PIXEL_FORMAT_BLOB,
+ supportedDynamicDepthSizes, &blobMinDurations);
if (blobMinDurations.empty() || depthMinDurations.empty() ||
(depthMinDurations.size() != blobMinDurations.size())) {
ALOGE("%s: Unexpected number of available depth min durations! %zu vs. %zu",
@@ -797,10 +786,10 @@
return BAD_VALUE;
}
- getSupportedDurations(c, ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS,
- HAL_PIXEL_FORMAT_Y16, internalDepthSizes, &depthStallDurations);
- getSupportedDurations(c, ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
- HAL_PIXEL_FORMAT_BLOB, supportedDynamicDepthSizes, &blobStallDurations);
+ getSupportedDurations(c, depthStallDurationsTag, HAL_PIXEL_FORMAT_Y16, internalDepthSizes,
+ &depthStallDurations);
+ getSupportedDurations(c, scalerStallDurationsTag, HAL_PIXEL_FORMAT_BLOB,
+ supportedDynamicDepthSizes, &blobStallDurations);
if (blobStallDurations.empty() || depthStallDurations.empty() ||
(depthStallDurations.size() != blobStallDurations.size())) {
ALOGE("%s: Unexpected number of available depth stall durations! %zu vs. %zu",
@@ -845,15 +834,14 @@
supportedChTags.reserve(chTags.count + 3);
supportedChTags.insert(supportedChTags.end(), chTags.data.i32,
chTags.data.i32 + chTags.count);
- supportedChTags.push_back(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS);
- supportedChTags.push_back(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS);
- supportedChTags.push_back(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS);
- c.update(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS,
- dynamicDepthEntries.data(), dynamicDepthEntries.size());
- c.update(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS,
- dynamicDepthMinDurationEntries.data(), dynamicDepthMinDurationEntries.size());
- c.update(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS,
- dynamicDepthStallDurationEntries.data(), dynamicDepthStallDurationEntries.size());
+ supportedChTags.push_back(dynamicDepthSizesTag);
+ supportedChTags.push_back(dynamicDepthMinFrameDurationsTag);
+ supportedChTags.push_back(dynamicDepthStallDurationsTag);
+ c.update(dynamicDepthSizesTag, dynamicDepthEntries.data(), dynamicDepthEntries.size());
+ c.update(dynamicDepthMinFrameDurationsTag, dynamicDepthMinDurationEntries.data(),
+ dynamicDepthMinDurationEntries.size());
+ c.update(dynamicDepthStallDurationsTag, dynamicDepthStallDurationEntries.data(),
+ dynamicDepthStallDurationEntries.size());
c.update(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, supportedChTags.data(),
supportedChTags.size());
@@ -1087,7 +1075,24 @@
return OK;
}
-status_t CameraProviderManager::ProviderInfo::DeviceInfo3::deriveHeicTags() {
+status_t CameraProviderManager::ProviderInfo::DeviceInfo3::deriveHeicTags(bool maxResolution) {
+ int32_t scalerStreamSizesTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, maxResolution);
+ int32_t scalerMinFrameDurationsTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS, maxResolution);
+
+ int32_t heicStreamSizesTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS, maxResolution);
+ int32_t heicMinFrameDurationsTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS, maxResolution);
+ int32_t heicStallDurationsTag =
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS, maxResolution);
+
auto& c = mCameraCharacteristics;
camera_metadata_entry halHeicSupport = c.find(ANDROID_HEIC_INFO_SUPPORTED);
@@ -1116,10 +1121,8 @@
std::vector<int64_t> heicDurations;
std::vector<int64_t> heicStallDurations;
- camera_metadata_entry halStreamConfigs =
- c.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
- camera_metadata_entry minFrameDurations =
- c.find(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS);
+ camera_metadata_entry halStreamConfigs = c.find(scalerStreamSizesTag);
+ camera_metadata_entry minFrameDurations = c.find(scalerMinFrameDurationsTag);
status_t res = fillHeicStreamCombinations(&heicOutputs, &heicDurations, &heicStallDurations,
halStreamConfigs, minFrameDurations);
@@ -1129,12 +1132,9 @@
return res;
}
- c.update(ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS,
- heicOutputs.data(), heicOutputs.size());
- c.update(ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS,
- heicDurations.data(), heicDurations.size());
- c.update(ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS,
- heicStallDurations.data(), heicStallDurations.size());
+ c.update(heicStreamSizesTag, heicOutputs.data(), heicOutputs.size());
+ c.update(heicMinFrameDurationsTag, heicDurations.data(), heicDurations.size());
+ c.update(heicStallDurationsTag, heicStallDurations.data(), heicStallDurations.size());
return OK;
}
@@ -1230,31 +1230,53 @@
return falseRet;
}
-status_t CameraProviderManager::addProviderLocked(const std::string& newProvider) {
- for (const auto& providerInfo : mProviders) {
- if (providerInfo->mProviderName == newProvider) {
- ALOGW("%s: Camera provider HAL with name '%s' already registered", __FUNCTION__,
- newProvider.c_str());
- return ALREADY_EXISTS;
- }
- }
-
+status_t CameraProviderManager::tryToInitializeProviderLocked(
+ const std::string& providerName, const sp<ProviderInfo>& providerInfo) {
sp<provider::V2_4::ICameraProvider> interface;
- interface = mServiceProxy->tryGetService(newProvider);
+ interface = mServiceProxy->tryGetService(providerName);
if (interface == nullptr) {
- ALOGE("%s: Camera provider HAL '%s' is not actually available", __FUNCTION__,
- newProvider.c_str());
+ // The interface may not be started yet. In that case, this is not a
+ // fatal error.
+ ALOGW("%s: Camera provider HAL '%s' is not actually available", __FUNCTION__,
+ providerName.c_str());
return BAD_VALUE;
}
- sp<ProviderInfo> providerInfo = new ProviderInfo(newProvider, this);
- status_t res = providerInfo->initialize(interface, mDeviceState);
- if (res != OK) {
- return res;
+ return providerInfo->initialize(interface, mDeviceState);
+}
+
+status_t CameraProviderManager::addProviderLocked(const std::string& newProvider,
+ bool preexisting) {
+ // Several camera provider instances can be temporarily present.
+ // Defer initialization of a new instance until the older instance is properly removed.
+ auto providerInstance = newProvider + "-" + std::to_string(mProviderInstanceId);
+ bool providerPresent = false;
+ for (const auto& providerInfo : mProviders) {
+ if (providerInfo->mProviderName == newProvider) {
+ ALOGW("%s: Camera provider HAL with name '%s' already registered",
+ __FUNCTION__, newProvider.c_str());
+ if (preexisting) {
+ return ALREADY_EXISTS;
+ } else{
+ ALOGW("%s: The new provider instance will get initialized immediately after the"
+ " currently present instance is removed!", __FUNCTION__);
+ providerPresent = true;
+ break;
+ }
+ }
+ }
+
+ sp<ProviderInfo> providerInfo = new ProviderInfo(newProvider, providerInstance, this);
+ if (!providerPresent) {
+ status_t res = tryToInitializeProviderLocked(newProvider, providerInfo);
+ if (res != OK) {
+ return res;
+ }
}
mProviders.push_back(providerInfo);
+ mProviderInstanceId++;
return OK;
}
@@ -1264,12 +1286,14 @@
std::unique_lock<std::mutex> lock(mInterfaceMutex);
std::vector<String8> removedDeviceIds;
status_t res = NAME_NOT_FOUND;
+ std::string removedProviderName;
for (auto it = mProviders.begin(); it != mProviders.end(); it++) {
- if ((*it)->mProviderName == provider) {
+ if ((*it)->mProviderInstance == provider) {
removedDeviceIds.reserve((*it)->mDevices.size());
for (auto& deviceInfo : (*it)->mDevices) {
removedDeviceIds.push_back(String8(deviceInfo->mId.c_str()));
}
+ removedProviderName = (*it)->mProviderName;
mProviders.erase(it);
res = OK;
break;
@@ -1279,6 +1303,14 @@
ALOGW("%s: Camera provider HAL with name '%s' is not registered", __FUNCTION__,
provider.c_str());
} else {
+ // Check if there are any newer camera instances from the same provider and try to
+ // initialize.
+ for (const auto& providerInfo : mProviders) {
+ if (providerInfo->mProviderName == removedProviderName) {
+ return tryToInitializeProviderLocked(removedProviderName, providerInfo);
+ }
+ }
+
// Inform camera service of loss of presence for all the devices from this provider,
// without lock held for reentrancy
sp<StatusListener> listener = getStatusListener();
@@ -1287,7 +1319,9 @@
for (auto& id : removedDeviceIds) {
listener->onDeviceStatusChanged(id, CameraDeviceStatus::NOT_PRESENT);
}
+ lock.lock();
}
+
}
return res;
}
@@ -1301,8 +1335,10 @@
CameraProviderManager::ProviderInfo::ProviderInfo(
const std::string &providerName,
+ const std::string &providerInstance,
CameraProviderManager *manager) :
mProviderName(providerName),
+ mProviderInstance(providerInstance),
mProviderTagid(generateVendorTagId(providerName)),
mUniqueDeviceCount(0),
mManager(manager) {
@@ -1342,6 +1378,14 @@
mMinorVersion = 5;
}
}
+ } else {
+ auto cast2_7 = provider::V2_7::ICameraProvider::castFrom(interface);
+ if (cast2_7.isOk()) {
+ sp<provider::V2_7::ICameraProvider> interface2_7 = cast2_7;
+ if (interface2_7 != nullptr) {
+ mMinorVersion = 7;
+ }
+ }
}
// cameraDeviceStatusChange callbacks may be called (and causing new devices added)
@@ -1567,9 +1611,9 @@
std::unique_ptr<DeviceInfo> deviceInfo;
switch (major) {
case 1:
- deviceInfo = initializeDeviceInfo<DeviceInfo1>(name, mProviderTagid,
- id, minor);
- break;
+ ALOGE("%s: Device %s: Unsupported HIDL device HAL major version %d:", __FUNCTION__,
+ name.c_str(), major);
+ return BAD_VALUE;
case 3:
deviceInfo = initializeDeviceInfo<DeviceInfo3>(name, mProviderTagid,
id, minor);
@@ -1618,7 +1662,7 @@
status_t CameraProviderManager::ProviderInfo::dump(int fd, const Vector<String16>&) const {
dprintf(fd, "== Camera Provider HAL %s (v2.%d, %s) static info: %zu devices: ==\n",
- mProviderName.c_str(),
+ mProviderInstance.c_str(),
mMinorVersion,
mIsRemote ? "remote" : "passthrough",
mDevices.size());
@@ -1934,12 +1978,12 @@
void CameraProviderManager::ProviderInfo::serviceDied(uint64_t cookie,
const wp<hidl::base::V1_0::IBase>& who) {
(void) who;
- ALOGI("Camera provider '%s' has died; removing it", mProviderName.c_str());
+ ALOGI("Camera provider '%s' has died; removing it", mProviderInstance.c_str());
if (cookie != mId) {
ALOGW("%s: Unexpected serviceDied cookie %" PRIu64 ", expected %" PRIu32,
__FUNCTION__, cookie, mId);
}
- mManager->removeProvider(mProviderName);
+ mManager->removeProvider(mProviderInstance);
}
status_t CameraProviderManager::ProviderInfo::setUpVendorTags() {
@@ -2013,38 +2057,71 @@
// TODO: This might be some other problem
return INVALID_OPERATION;
}
- auto castResult = provider::V2_6::ICameraProvider::castFrom(interface);
- if (castResult.isOk()) {
- sp<provider::V2_6::ICameraProvider> interface_2_6 = castResult;
- if (interface_2_6 != nullptr) {
- Status callStatus;
- auto cb =
- [&isSupported, &callStatus](Status s, bool supported) {
- callStatus = s;
- *isSupported = supported; };
+ auto castResult2_6 = provider::V2_6::ICameraProvider::castFrom(interface);
+ auto castResult2_7 = provider::V2_7::ICameraProvider::castFrom(interface);
+ Status callStatus;
+ auto cb =
+ [&isSupported, &callStatus](Status s, bool supported) {
+ callStatus = s;
+ *isSupported = supported; };
- auto ret = interface_2_6->isConcurrentStreamCombinationSupported(
- halCameraIdsAndStreamCombinations, cb);
- if (ret.isOk()) {
- switch (callStatus) {
- case Status::OK:
- // Expected case, do nothing.
- res = OK;
- break;
- case Status::METHOD_NOT_SUPPORTED:
- res = INVALID_OPERATION;
- break;
- default:
- ALOGE("%s: Session configuration query failed: %d", __FUNCTION__,
- callStatus);
- res = UNKNOWN_ERROR;
- }
- } else {
- ALOGE("%s: Unexpected binder error: %s", __FUNCTION__, ret.description().c_str());
- res = UNKNOWN_ERROR;
- }
- return res;
+ ::android::hardware::Return<void> ret;
+ sp<provider::V2_7::ICameraProvider> interface_2_7;
+ sp<provider::V2_6::ICameraProvider> interface_2_6;
+ if (mMinorVersion >= 7 && castResult2_7.isOk()) {
+ interface_2_7 = castResult2_7;
+ if (interface_2_7 != nullptr) {
+ ret = interface_2_7->isConcurrentStreamCombinationSupported_2_7(
+ halCameraIdsAndStreamCombinations, cb);
}
+ } else if (mMinorVersion == 6 && castResult2_6.isOk()) {
+ interface_2_6 = castResult2_6;
+ if (interface_2_6 != nullptr) {
+ hardware::hidl_vec<provider::V2_6::CameraIdAndStreamCombination>
+ halCameraIdsAndStreamCombinations_2_6;
+ size_t numStreams = halCameraIdsAndStreamCombinations.size();
+ halCameraIdsAndStreamCombinations_2_6.resize(numStreams);
+ for (size_t i = 0; i < numStreams; i++) {
+ using namespace camera3;
+ auto const& combination = halCameraIdsAndStreamCombinations[i];
+ halCameraIdsAndStreamCombinations_2_6[i].cameraId = combination.cameraId;
+ bool success =
+ SessionConfigurationUtils::convertHALStreamCombinationFromV37ToV34(
+ halCameraIdsAndStreamCombinations_2_6[i].streamConfiguration,
+ combination.streamConfiguration);
+ if (!success) {
+ *isSupported = false;
+ return OK;
+ }
+ camera3::SessionConfigurationUtils::convertHALStreamCombinationFromV37ToV34(
+ halCameraIdsAndStreamCombinations_2_6[i].streamConfiguration,
+ combination.streamConfiguration);
+ }
+ ret = interface_2_6->isConcurrentStreamCombinationSupported(
+ halCameraIdsAndStreamCombinations_2_6, cb);
+ }
+ }
+
+ if (interface_2_7 != nullptr || interface_2_6 != nullptr) {
+ if (ret.isOk()) {
+ switch (callStatus) {
+ case Status::OK:
+ // Expected case, do nothing.
+ res = OK;
+ break;
+ case Status::METHOD_NOT_SUPPORTED:
+ res = INVALID_OPERATION;
+ break;
+ default:
+ ALOGE("%s: Session configuration query failed: %d", __FUNCTION__,
+ callStatus);
+ res = UNKNOWN_ERROR;
+ }
+ } else {
+ ALOGE("%s: Unexpected binder error: %s", __FUNCTION__, ret.description().c_str());
+ res = UNKNOWN_ERROR;
+ }
+ return res;
}
}
// unsupported operation
@@ -2113,35 +2190,6 @@
}
template<>
-sp<device::V1_0::ICameraDevice>
-CameraProviderManager::ProviderInfo::startDeviceInterface
- <device::V1_0::ICameraDevice>(const std::string &name) {
- Status status;
- sp<device::V1_0::ICameraDevice> cameraInterface;
- hardware::Return<void> ret;
- const sp<provider::V2_4::ICameraProvider> interface = startProviderInterface();
- if (interface == nullptr) {
- return nullptr;
- }
- ret = interface->getCameraDeviceInterface_V1_x(name, [&status, &cameraInterface](
- Status s, sp<device::V1_0::ICameraDevice> interface) {
- status = s;
- cameraInterface = interface;
- });
- if (!ret.isOk()) {
- ALOGE("%s: Transaction error trying to obtain interface for camera device %s: %s",
- __FUNCTION__, name.c_str(), ret.description().c_str());
- return nullptr;
- }
- if (status != Status::OK) {
- ALOGE("%s: Unable to obtain interface for camera device %s: %s", __FUNCTION__,
- name.c_str(), statusToString(status));
- return nullptr;
- }
- return cameraInterface;
-}
-
-template<>
sp<device::V3_2::ICameraDevice>
CameraProviderManager::ProviderInfo::startDeviceInterface
<device::V3_2::ICameraDevice>(const std::string &name) {
@@ -2194,126 +2242,6 @@
return mapToStatusT(s);
}
-CameraProviderManager::ProviderInfo::DeviceInfo1::DeviceInfo1(const std::string& name,
- const metadata_vendor_id_t tagId, const std::string &id,
- uint16_t minorVersion,
- const CameraResourceCost& resourceCost,
- sp<ProviderInfo> parentProvider,
- const std::vector<std::string>& publicCameraIds,
- sp<InterfaceT> interface) :
- DeviceInfo(name, tagId, id, hardware::hidl_version{1, minorVersion},
- publicCameraIds, resourceCost, parentProvider) {
- // Get default parameters and initialize flash unit availability
- // Requires powering on the camera device
- hardware::Return<Status> status = interface->open(nullptr);
- if (!status.isOk()) {
- ALOGE("%s: Transaction error opening camera device %s to check for a flash unit: %s",
- __FUNCTION__, id.c_str(), status.description().c_str());
- return;
- }
- if (status != Status::OK) {
- ALOGE("%s: Unable to open camera device %s to check for a flash unit: %s", __FUNCTION__,
- id.c_str(), CameraProviderManager::statusToString(status));
- return;
- }
- hardware::Return<void> ret;
- ret = interface->getParameters([this](const hardware::hidl_string& parms) {
- mDefaultParameters.unflatten(String8(parms.c_str()));
- });
- if (!ret.isOk()) {
- ALOGE("%s: Transaction error reading camera device %s params to check for a flash unit: %s",
- __FUNCTION__, id.c_str(), status.description().c_str());
- return;
- }
- const char *flashMode =
- mDefaultParameters.get(CameraParameters::KEY_SUPPORTED_FLASH_MODES);
- if (flashMode && strstr(flashMode, CameraParameters::FLASH_MODE_TORCH)) {
- mHasFlashUnit = true;
- }
-
- status_t res = cacheCameraInfo(interface);
- if (res != OK) {
- ALOGE("%s: Could not cache CameraInfo", __FUNCTION__);
- return;
- }
-
- ret = interface->close();
- if (!ret.isOk()) {
- ALOGE("%s: Transaction error closing camera device %s after check for a flash unit: %s",
- __FUNCTION__, id.c_str(), status.description().c_str());
- }
-
- if (!kEnableLazyHal) {
- // Save HAL reference indefinitely
- mSavedInterface = interface;
- }
-}
-
-CameraProviderManager::ProviderInfo::DeviceInfo1::~DeviceInfo1() {}
-
-status_t CameraProviderManager::ProviderInfo::DeviceInfo1::setTorchMode(bool enabled) {
- return setTorchModeForDevice<InterfaceT>(enabled);
-}
-
-status_t CameraProviderManager::ProviderInfo::DeviceInfo1::getCameraInfo(
- hardware::CameraInfo *info) const {
- if (info == nullptr) return BAD_VALUE;
- *info = mInfo;
- return OK;
-}
-
-status_t CameraProviderManager::ProviderInfo::DeviceInfo1::cacheCameraInfo(
- sp<CameraProviderManager::ProviderInfo::DeviceInfo1::InterfaceT> interface) {
- Status status;
- device::V1_0::CameraInfo cInfo;
- hardware::Return<void> ret;
- ret = interface->getCameraInfo([&status, &cInfo](Status s, device::V1_0::CameraInfo camInfo) {
- status = s;
- cInfo = camInfo;
- });
- if (!ret.isOk()) {
- ALOGE("%s: Transaction error reading camera info from device %s: %s",
- __FUNCTION__, mId.c_str(), ret.description().c_str());
- return DEAD_OBJECT;
- }
- if (status != Status::OK) {
- return mapToStatusT(status);
- }
-
- switch(cInfo.facing) {
- case device::V1_0::CameraFacing::BACK:
- mInfo.facing = hardware::CAMERA_FACING_BACK;
- break;
- case device::V1_0::CameraFacing::EXTERNAL:
- // Map external to front for legacy API
- case device::V1_0::CameraFacing::FRONT:
- mInfo.facing = hardware::CAMERA_FACING_FRONT;
- break;
- default:
- ALOGW("%s: Device %s: Unknown camera facing: %d",
- __FUNCTION__, mId.c_str(), cInfo.facing);
- mInfo.facing = hardware::CAMERA_FACING_BACK;
- }
- mInfo.orientation = cInfo.orientation;
-
- return OK;
-}
-
-status_t CameraProviderManager::ProviderInfo::DeviceInfo1::dumpState(int fd) {
- native_handle_t* handle = native_handle_create(1,0);
- handle->data[0] = fd;
- const sp<InterfaceT> interface = startDeviceInterface<InterfaceT>();
- if (interface == nullptr) {
- return DEAD_OBJECT;
- }
- hardware::Return<Status> s = interface->dumpState(handle);
- native_handle_delete(handle);
- if (!s.isOk()) {
- return INVALID_OPERATION;
- }
- return mapToStatusT(s);
-}
-
CameraProviderManager::ProviderInfo::DeviceInfo3::DeviceInfo3(const std::string& name,
const metadata_vendor_id_t tagId, const std::string &id,
uint16_t minorVersion,
@@ -2373,6 +2301,21 @@
ALOGE("%s: Unable to derive HEIC tags based on camera and media capabilities: %s (%d)",
__FUNCTION__, strerror(-res), res);
}
+
+ if (camera3::SessionConfigurationUtils::isUltraHighResolutionSensor(mCameraCharacteristics)) {
+ status_t status = addDynamicDepthTags(/*maxResolution*/true);
+ if (OK != status) {
+ ALOGE("%s: Failed appending dynamic depth tags for maximum resolution mode: %s (%d)",
+ __FUNCTION__, strerror(-status), status);
+ }
+
+ status = deriveHeicTags(/*maxResolution*/true);
+ if (OK != status) {
+ ALOGE("%s: Unable to derive HEIC tags based on camera and media capabilities for"
+ "maximum resolution mode: %s (%d)", __FUNCTION__, strerror(-status), status);
+ }
+ }
+
res = addRotateCropTags();
if (OK != res) {
ALOGE("%s: Unable to add default SCALER_ROTATE_AND_CROP tags: %s (%d)", __FUNCTION__,
@@ -2563,7 +2506,7 @@
}
status_t CameraProviderManager::ProviderInfo::DeviceInfo3::isSessionConfigurationSupported(
- const hardware::camera::device::V3_4::StreamConfiguration &configuration,
+ const hardware::camera::device::V3_7::StreamConfiguration &configuration,
bool *status /*out*/) {
const sp<CameraProviderManager::ProviderInfo::DeviceInfo3::InterfaceT> interface =
@@ -2571,19 +2514,33 @@
if (interface == nullptr) {
return DEAD_OBJECT;
}
- auto castResult = device::V3_5::ICameraDevice::castFrom(interface);
- sp<hardware::camera::device::V3_5::ICameraDevice> interface_3_5 = castResult;
- if (interface_3_5 == nullptr) {
- return INVALID_OPERATION;
- }
+ auto castResult_3_5 = device::V3_5::ICameraDevice::castFrom(interface);
+ sp<hardware::camera::device::V3_5::ICameraDevice> interface_3_5 = castResult_3_5;
+ auto castResult_3_7 = device::V3_7::ICameraDevice::castFrom(interface);
+ sp<hardware::camera::device::V3_7::ICameraDevice> interface_3_7 = castResult_3_7;
status_t res;
Status callStatus;
- auto ret = interface_3_5->isStreamCombinationSupported(configuration,
+ ::android::hardware::Return<void> ret;
+ auto halCb =
[&callStatus, &status] (Status s, bool combStatus) {
callStatus = s;
*status = combStatus;
- });
+ };
+ if (interface_3_7 != nullptr) {
+ ret = interface_3_7->isStreamCombinationSupported_3_7(configuration, halCb);
+ } else if (interface_3_5 != nullptr) {
+ hardware::camera::device::V3_4::StreamConfiguration configuration_3_4;
+ bool success = camera3::SessionConfigurationUtils::convertHALStreamCombinationFromV37ToV34(
+ configuration_3_4, configuration);
+ if (!success) {
+ *status = false;
+ return OK;
+ }
+ ret = interface_3_5->isStreamCombinationSupported(configuration_3_4, halCb);
+ } else {
+ return INVALID_OPERATION;
+ }
if (ret.isOk()) {
switch (callStatus) {
case Status::OK:
@@ -2958,13 +2915,13 @@
bool shouldExit = false;
status_t res = OK;
for (auto &cameraIdAndSessionConfig : cameraIdsAndSessionConfigs) {
- hardware::camera::device::V3_4::StreamConfiguration streamConfiguration;
+ hardware::camera::device::V3_7::StreamConfiguration streamConfiguration;
CameraMetadata deviceInfo;
res = getCameraCharacteristicsLocked(cameraIdAndSessionConfig.mCameraId, &deviceInfo);
if (res != OK) {
return res;
}
- metadataGetter getMetadata =
+ camera3::metadataGetter getMetadata =
[this](const String8 &id) {
CameraMetadata physicalDeviceInfo;
getCameraCharacteristicsLocked(id.string(), &physicalDeviceInfo);
@@ -2973,7 +2930,7 @@
std::vector<std::string> physicalCameraIds;
isLogicalCameraLocked(cameraIdAndSessionConfig.mCameraId, &physicalCameraIds);
bStatus =
- SessionConfigurationUtils::convertToHALStreamCombination(
+ camera3::SessionConfigurationUtils::convertToHALStreamCombination(
cameraIdAndSessionConfig.mSessionConfiguration,
String8(cameraIdAndSessionConfig.mCameraId.c_str()), deviceInfo, getMetadata,
physicalCameraIds, streamConfiguration, &shouldExit);
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index a0e5f8f..5531dd7 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -33,7 +33,8 @@
#include <android/hardware/camera/provider/2.5/ICameraProvider.h>
#include <android/hardware/camera/provider/2.6/ICameraProviderCallback.h>
#include <android/hardware/camera/provider/2.6/ICameraProvider.h>
-#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
+#include <android/hardware/camera/provider/2.7/ICameraProvider.h>
+#include <android/hardware/camera/device/3.7/types.h>
#include <android/hidl/manager/1.0/IServiceNotification.h>
#include <camera/VendorTagDescriptor.h>
@@ -78,6 +79,16 @@
HIDDEN_SECURE_CAMERA
};
+#define CAMERA_DEVICE_API_VERSION_1_0 HARDWARE_DEVICE_API_VERSION(1, 0)
+#define CAMERA_DEVICE_API_VERSION_3_0 HARDWARE_DEVICE_API_VERSION(3, 0)
+#define CAMERA_DEVICE_API_VERSION_3_1 HARDWARE_DEVICE_API_VERSION(3, 1)
+#define CAMERA_DEVICE_API_VERSION_3_2 HARDWARE_DEVICE_API_VERSION(3, 2)
+#define CAMERA_DEVICE_API_VERSION_3_3 HARDWARE_DEVICE_API_VERSION(3, 3)
+#define CAMERA_DEVICE_API_VERSION_3_4 HARDWARE_DEVICE_API_VERSION(3, 4)
+#define CAMERA_DEVICE_API_VERSION_3_5 HARDWARE_DEVICE_API_VERSION(3, 5)
+#define CAMERA_DEVICE_API_VERSION_3_6 HARDWARE_DEVICE_API_VERSION(3, 6)
+#define CAMERA_DEVICE_API_VERSION_3_7 HARDWARE_DEVICE_API_VERSION(3, 7)
+
/**
* A manager for all camera providers available on an Android device.
*
@@ -227,7 +238,7 @@
* Check for device support of specific stream combination.
*/
status_t isSessionConfigurationSupported(const std::string& id,
- const hardware::camera::device::V3_4::StreamConfiguration &configuration,
+ const hardware::camera::device::V3_7::StreamConfiguration &configuration,
bool *status /*out*/) const;
/**
@@ -270,11 +281,6 @@
/*out*/
sp<hardware::camera::device::V3_2::ICameraDeviceSession> *session);
- status_t openSession(const std::string &id,
- const sp<hardware::camera::device::V1_0::ICameraDeviceCallback>& callback,
- /*out*/
- sp<hardware::camera::device::V1_0::ICameraDevice> *session);
-
/**
* Save the ICameraProvider while it is being used by a camera or torch client
*/
@@ -359,6 +365,7 @@
virtual public hardware::hidl_death_recipient
{
const std::string mProviderName;
+ const std::string mProviderInstance;
const metadata_vendor_id_t mProviderTagid;
int mMinorVersion;
sp<VendorTagDescriptor> mVendorTagDescriptor;
@@ -373,7 +380,7 @@
sp<hardware::camera::provider::V2_4::ICameraProvider> mSavedInterface;
- ProviderInfo(const std::string &providerName,
+ ProviderInfo(const std::string &providerName, const std::string &providerInstance,
CameraProviderManager *manager);
~ProviderInfo();
@@ -435,7 +442,7 @@
*/
status_t isConcurrentSessionConfigurationSupported(
const hardware::hidl_vec<
- hardware::camera::provider::V2_6::CameraIdAndStreamCombination>
+ hardware::camera::provider::V2_7::CameraIdAndStreamCombination>
&halCameraIdsAndStreamCombinations,
bool *isSupported);
@@ -475,7 +482,7 @@
}
virtual status_t isSessionConfigurationSupported(
- const hardware::camera::device::V3_4::StreamConfiguration &/*configuration*/,
+ const hardware::camera::device::V3_7::StreamConfiguration &/*configuration*/,
bool * /*status*/) {
return INVALID_OPERATION;
}
@@ -521,27 +528,6 @@
// physical camera IDs.
std::vector<std::string> mProviderPublicCameraIds;
- // HALv1-specific camera fields, including the actual device interface
- struct DeviceInfo1 : public DeviceInfo {
- typedef hardware::camera::device::V1_0::ICameraDevice InterfaceT;
-
- virtual status_t setTorchMode(bool enabled) override;
- virtual status_t getCameraInfo(hardware::CameraInfo *info) const override;
- //In case of Device1Info assume that we are always API1 compatible
- virtual bool isAPI1Compatible() const override { return true; }
- virtual status_t dumpState(int fd) override;
- DeviceInfo1(const std::string& name, const metadata_vendor_id_t tagId,
- const std::string &id, uint16_t minorVersion,
- const hardware::camera::common::V1_0::CameraResourceCost& resourceCost,
- sp<ProviderInfo> parentProvider,
- const std::vector<std::string>& publicCameraIds,
- sp<InterfaceT> interface);
- virtual ~DeviceInfo1();
- private:
- CameraParameters2 mDefaultParameters;
- status_t cacheCameraInfo(sp<InterfaceT> interface);
- };
-
// HALv3-specific camera fields, including the actual device interface
struct DeviceInfo3 : public DeviceInfo {
typedef hardware::camera::device::V3_2::ICameraDevice InterfaceT;
@@ -555,7 +541,7 @@
virtual status_t getPhysicalCameraCharacteristics(const std::string& physicalCameraId,
CameraMetadata *characteristics) const override;
virtual status_t isSessionConfigurationSupported(
- const hardware::camera::device::V3_4::StreamConfiguration &configuration,
+ const hardware::camera::device::V3_7::StreamConfiguration &configuration,
bool *status /*out*/)
override;
@@ -571,8 +557,8 @@
void queryPhysicalCameraIds();
SystemCameraKind getSystemCameraKind();
status_t fixupMonochromeTags();
- status_t addDynamicDepthTags();
- status_t deriveHeicTags();
+ status_t addDynamicDepthTags(bool maxResolution = false);
+ status_t deriveHeicTags(bool maxResolution = false);
status_t addRotateCropTags();
status_t addPreCorrectionActiveArraySize();
@@ -672,7 +658,10 @@
hardware::hidl_version minVersion = hardware::hidl_version{0,0},
hardware::hidl_version maxVersion = hardware::hidl_version{1000,0}) const;
- status_t addProviderLocked(const std::string& newProvider);
+ status_t addProviderLocked(const std::string& newProvider, bool preexisting = false);
+
+ status_t tryToInitializeProviderLocked(const std::string& providerName,
+ const sp<ProviderInfo>& providerInfo);
bool isLogicalCameraLocked(const std::string& id, std::vector<std::string>* physicalCameraIds);
@@ -681,6 +670,7 @@
bool isValidDeviceLocked(const std::string &id, uint16_t majorVersion) const;
+ size_t mProviderInstanceId = 0;
std::vector<sp<ProviderInfo>> mProviders;
void addProviderToMap(
@@ -710,7 +700,7 @@
status_t convertToHALStreamCombinationAndCameraIdsLocked(
const std::vector<hardware::camera2::utils::CameraIdAndSessionConfiguration>
&cameraIdsAndSessionConfigs,
- hardware::hidl_vec<hardware::camera::provider::V2_6::CameraIdAndStreamCombination>
+ hardware::hidl_vec<hardware::camera::provider::V2_7::CameraIdAndStreamCombination>
*halCameraIdsAndStreamCombinations,
bool *earlyExit);
};
diff --git a/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp b/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
deleted file mode 100644
index 62ef681..0000000
--- a/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
+++ /dev/null
@@ -1,818 +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.
- */
-#define LOG_TAG "CameraHardwareInterface"
-//#define LOG_NDEBUG 0
-
-#include <inttypes.h>
-#include <media/hardware/HardwareAPI.h> // For VideoNativeHandleMetadata
-#include "CameraHardwareInterface.h"
-
-namespace android {
-
-using namespace hardware::camera::device::V1_0;
-using namespace hardware::camera::common::V1_0;
-using hardware::hidl_handle;
-
-CameraHardwareInterface::~CameraHardwareInterface()
-{
- ALOGI("Destroying camera %s", mName.string());
- if (mHidlDevice != nullptr) {
- mHidlDevice->close();
- mHidlDevice.clear();
- cleanupCirculatingBuffers();
- }
-}
-
-status_t CameraHardwareInterface::initialize(sp<CameraProviderManager> manager) {
- ALOGI("Opening camera %s", mName.string());
-
- status_t ret = manager->openSession(mName.string(), this, &mHidlDevice);
- if (ret != OK) {
- ALOGE("%s: openSession failed! %s (%d)", __FUNCTION__, strerror(-ret), ret);
- }
- return ret;
-}
-
-status_t CameraHardwareInterface::setPreviewScalingMode(int scalingMode)
-{
- int rc = OK;
- mPreviewScalingMode = scalingMode;
- if (mPreviewWindow != nullptr) {
- rc = native_window_set_scaling_mode(mPreviewWindow.get(),
- scalingMode);
- }
- return rc;
-}
-
-status_t CameraHardwareInterface::setPreviewTransform(int transform) {
- int rc = OK;
- mPreviewTransform = transform;
- if (mPreviewWindow != nullptr) {
- rc = native_window_set_buffers_transform(mPreviewWindow.get(),
- mPreviewTransform);
- }
- return rc;
-}
-
-/**
- * Implementation of android::hardware::camera::device::V1_0::ICameraDeviceCallback
- */
-hardware::Return<void> CameraHardwareInterface::notifyCallback(
- NotifyCallbackMsg msgType, int32_t ext1, int32_t ext2) {
- sNotifyCb((int32_t) msgType, ext1, ext2, (void*) this);
- return hardware::Void();
-}
-
-hardware::Return<uint32_t> CameraHardwareInterface::registerMemory(
- const hardware::hidl_handle& descriptor,
- uint32_t bufferSize, uint32_t bufferCount) {
- if (descriptor->numFds != 1) {
- ALOGE("%s: camera memory descriptor has numFds %d (expect 1)",
- __FUNCTION__, descriptor->numFds);
- return 0;
- }
- if (descriptor->data[0] < 0) {
- ALOGE("%s: camera memory descriptor has FD %d (expect >= 0)",
- __FUNCTION__, descriptor->data[0]);
- return 0;
- }
-
- camera_memory_t* mem = sGetMemory(descriptor->data[0], bufferSize, bufferCount, this);
- sp<CameraHeapMemory> camMem(static_cast<CameraHeapMemory *>(mem->handle));
- int memPoolId = camMem->mHeap->getHeapID();
- if (memPoolId < 0) {
- ALOGE("%s: CameraHeapMemory has FD %d (expect >= 0)", __FUNCTION__, memPoolId);
- return 0;
- }
- std::lock_guard<std::mutex> lock(mHidlMemPoolMapLock);
- mHidlMemPoolMap.insert(std::make_pair(memPoolId, mem));
- return memPoolId;
-}
-
-hardware::Return<void> CameraHardwareInterface::unregisterMemory(uint32_t memId) {
- camera_memory_t* mem = nullptr;
- {
- std::lock_guard<std::mutex> lock(mHidlMemPoolMapLock);
- if (mHidlMemPoolMap.count(memId) == 0) {
- ALOGE("%s: memory pool ID %d not found", __FUNCTION__, memId);
- return hardware::Void();
- }
- mem = mHidlMemPoolMap.at(memId);
- mHidlMemPoolMap.erase(memId);
- }
- sPutMemory(mem);
- return hardware::Void();
-}
-
-hardware::Return<void> CameraHardwareInterface::dataCallback(
- DataCallbackMsg msgType, uint32_t data, uint32_t bufferIndex,
- const hardware::camera::device::V1_0::CameraFrameMetadata& metadata) {
- camera_memory_t* mem = nullptr;
- {
- std::lock_guard<std::mutex> lock(mHidlMemPoolMapLock);
- if (mHidlMemPoolMap.count(data) == 0) {
- ALOGE("%s: memory pool ID %d not found", __FUNCTION__, data);
- return hardware::Void();
- }
- mem = mHidlMemPoolMap.at(data);
- }
- camera_frame_metadata_t md;
- md.number_of_faces = metadata.faces.size();
- md.faces = (camera_face_t*) metadata.faces.data();
- sDataCb((int32_t) msgType, mem, bufferIndex, &md, this);
- return hardware::Void();
-}
-
-hardware::Return<void> CameraHardwareInterface::dataCallbackTimestamp(
- DataCallbackMsg msgType, uint32_t data,
- uint32_t bufferIndex, int64_t timestamp) {
- camera_memory_t* mem = nullptr;
- {
- std::lock_guard<std::mutex> lock(mHidlMemPoolMapLock);
- if (mHidlMemPoolMap.count(data) == 0) {
- ALOGE("%s: memory pool ID %d not found", __FUNCTION__, data);
- return hardware::Void();
- }
- mem = mHidlMemPoolMap.at(data);
- }
- sDataCbTimestamp(timestamp, (int32_t) msgType, mem, bufferIndex, this);
- return hardware::Void();
-}
-
-hardware::Return<void> CameraHardwareInterface::handleCallbackTimestamp(
- DataCallbackMsg msgType, const hidl_handle& frameData, uint32_t data,
- uint32_t bufferIndex, int64_t timestamp) {
- camera_memory_t* mem = nullptr;
- {
- std::lock_guard<std::mutex> lock(mHidlMemPoolMapLock);
- if (mHidlMemPoolMap.count(data) == 0) {
- ALOGE("%s: memory pool ID %d not found", __FUNCTION__, data);
- return hardware::Void();
- }
- 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]->unsecurePointer();
- md->pHandle = const_cast<native_handle_t*>(frameData.getNativeHandle());
- sDataCbTimestamp(timestamp, (int32_t) msgType, mem, bufferIndex, this);
- return hardware::Void();
-}
-
-hardware::Return<void> CameraHardwareInterface::handleCallbackTimestampBatch(
- DataCallbackMsg msgType,
- const hardware::hidl_vec<hardware::camera::device::V1_0::HandleTimestampMessage>& messages) {
- std::vector<android::HandleTimestampMessage> msgs;
- msgs.reserve(messages.size());
- {
- std::lock_guard<std::mutex> lock(mHidlMemPoolMapLock);
- for (const auto& hidl_msg : messages) {
- if (mHidlMemPoolMap.count(hidl_msg.data) == 0) {
- ALOGE("%s: memory pool ID %d not found", __FUNCTION__, hidl_msg.data);
- return hardware::Void();
- }
- sp<CameraHeapMemory> mem(
- static_cast<CameraHeapMemory *>(mHidlMemPoolMap.at(hidl_msg.data)->handle));
-
- if (hidl_msg.bufferIndex >= mem->mNumBufs) {
- ALOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__,
- 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]->unsecurePointer();
- md->pHandle = const_cast<native_handle_t*>(hidl_msg.frameData.getNativeHandle());
-
- msgs.push_back({hidl_msg.timestamp, mem->mBuffers[hidl_msg.bufferIndex]});
- }
- }
- mDataCbTimestampBatch((int32_t) msgType, msgs, mCbUser);
- return hardware::Void();
-}
-
-std::pair<bool, uint64_t> CameraHardwareInterface::getBufferId(
- ANativeWindowBuffer* anb) {
- std::lock_guard<std::mutex> lock(mBufferIdMapLock);
-
- buffer_handle_t& buf = anb->handle;
- auto it = mBufferIdMap.find(buf);
- if (it == mBufferIdMap.end()) {
- uint64_t bufId = mNextBufferId++;
- mBufferIdMap[buf] = bufId;
- mReversedBufMap[bufId] = anb;
- return std::make_pair(true, bufId);
- } else {
- return std::make_pair(false, it->second);
- }
-}
-
-void CameraHardwareInterface::cleanupCirculatingBuffers() {
- std::lock_guard<std::mutex> lock(mBufferIdMapLock);
- mBufferIdMap.clear();
- mReversedBufMap.clear();
-}
-
-hardware::Return<void>
-CameraHardwareInterface::dequeueBuffer(dequeueBuffer_cb _hidl_cb) {
- ANativeWindow *a = mPreviewWindow.get();
- if (a == nullptr) {
- ALOGE("%s: preview window is null", __FUNCTION__);
- return hardware::Void();
- }
- ANativeWindowBuffer* anb;
- int rc = native_window_dequeue_buffer_and_wait(a, &anb);
- Status s = Status::INTERNAL_ERROR;
- uint64_t bufferId = 0;
- uint32_t stride = 0;
- hidl_handle buf = nullptr;
- if (rc == OK) {
- s = Status::OK;
- auto pair = getBufferId(anb);
- buf = (pair.first) ? anb->handle : nullptr;
- bufferId = pair.second;
- stride = anb->stride;
- }
-
- _hidl_cb(s, bufferId, buf, stride);
- return hardware::Void();
-}
-
-hardware::Return<Status>
-CameraHardwareInterface::enqueueBuffer(uint64_t bufferId) {
- ANativeWindow *a = mPreviewWindow.get();
- if (a == nullptr) {
- ALOGE("%s: preview window is null", __FUNCTION__);
- return Status::INTERNAL_ERROR;
- }
- if (mReversedBufMap.count(bufferId) == 0) {
- ALOGE("%s: bufferId %" PRIu64 " not found", __FUNCTION__, bufferId);
- return Status::ILLEGAL_ARGUMENT;
- }
- int rc = a->queueBuffer(a, mReversedBufMap.at(bufferId), -1);
- if (rc == 0) {
- return Status::OK;
- }
- return Status::INTERNAL_ERROR;
-}
-
-hardware::Return<Status>
-CameraHardwareInterface::cancelBuffer(uint64_t bufferId) {
- ANativeWindow *a = mPreviewWindow.get();
- if (a == nullptr) {
- ALOGE("%s: preview window is null", __FUNCTION__);
- return Status::INTERNAL_ERROR;
- }
- if (mReversedBufMap.count(bufferId) == 0) {
- ALOGE("%s: bufferId %" PRIu64 " not found", __FUNCTION__, bufferId);
- return Status::ILLEGAL_ARGUMENT;
- }
- int rc = a->cancelBuffer(a, mReversedBufMap.at(bufferId), -1);
- if (rc == 0) {
- return Status::OK;
- }
- return Status::INTERNAL_ERROR;
-}
-
-hardware::Return<Status>
-CameraHardwareInterface::setBufferCount(uint32_t count) {
- ANativeWindow *a = mPreviewWindow.get();
- if (a != nullptr) {
- // Workaround for b/27039775
- // Previously, setting the buffer count would reset the buffer
- // queue's flag that allows for all buffers to be dequeued on the
- // producer side, instead of just the producer's declared max count,
- // if no filled buffers have yet been queued by the producer. This
- // reset no longer happens, but some HALs depend on this behavior,
- // so it needs to be maintained for HAL backwards compatibility.
- // Simulate the prior behavior by disconnecting/reconnecting to the
- // window and setting the values again. This has the drawback of
- // actually causing memory reallocation, which may not have happened
- // in the past.
- native_window_api_disconnect(a, NATIVE_WINDOW_API_CAMERA);
- native_window_api_connect(a, NATIVE_WINDOW_API_CAMERA);
- if (mPreviewScalingMode != NOT_SET) {
- native_window_set_scaling_mode(a, mPreviewScalingMode);
- }
- if (mPreviewTransform != NOT_SET) {
- native_window_set_buffers_transform(a, mPreviewTransform);
- }
- if (mPreviewWidth != NOT_SET) {
- native_window_set_buffers_dimensions(a,
- mPreviewWidth, mPreviewHeight);
- native_window_set_buffers_format(a, mPreviewFormat);
- }
- if (mPreviewUsage != 0) {
- native_window_set_usage(a, mPreviewUsage);
- }
- if (mPreviewSwapInterval != NOT_SET) {
- a->setSwapInterval(a, mPreviewSwapInterval);
- }
- if (mPreviewCrop.left != NOT_SET) {
- native_window_set_crop(a, &(mPreviewCrop));
- }
- }
- int rc = native_window_set_buffer_count(a, count);
- if (rc == OK) {
- cleanupCirculatingBuffers();
- return Status::OK;
- }
- return Status::INTERNAL_ERROR;
-}
-
-hardware::Return<Status>
-CameraHardwareInterface::setBuffersGeometry(
- uint32_t w, uint32_t h, hardware::graphics::common::V1_0::PixelFormat format) {
- Status s = Status::INTERNAL_ERROR;
- ANativeWindow *a = mPreviewWindow.get();
- if (a == nullptr) {
- ALOGE("%s: preview window is null", __FUNCTION__);
- return s;
- }
- mPreviewWidth = w;
- mPreviewHeight = h;
- mPreviewFormat = (int) format;
- int rc = native_window_set_buffers_dimensions(a, w, h);
- if (rc == OK) {
- rc = native_window_set_buffers_format(a, mPreviewFormat);
- }
- if (rc == OK) {
- cleanupCirculatingBuffers();
- s = Status::OK;
- }
- return s;
-}
-
-hardware::Return<Status>
-CameraHardwareInterface::setCrop(int32_t left, int32_t top, int32_t right, int32_t bottom) {
- Status s = Status::INTERNAL_ERROR;
- ANativeWindow *a = mPreviewWindow.get();
- if (a == nullptr) {
- ALOGE("%s: preview window is null", __FUNCTION__);
- return s;
- }
- mPreviewCrop.left = left;
- mPreviewCrop.top = top;
- mPreviewCrop.right = right;
- mPreviewCrop.bottom = bottom;
- int rc = native_window_set_crop(a, &mPreviewCrop);
- if (rc == OK) {
- s = Status::OK;
- }
- return s;
-}
-
-hardware::Return<Status>
-CameraHardwareInterface::setUsage(hardware::graphics::common::V1_0::BufferUsage usage) {
- Status s = Status::INTERNAL_ERROR;
- ANativeWindow *a = mPreviewWindow.get();
- if (a == nullptr) {
- ALOGE("%s: preview window is null", __FUNCTION__);
- return s;
- }
- mPreviewUsage = static_cast<uint64_t> (usage);
- int rc = native_window_set_usage(a, mPreviewUsage);
- if (rc == OK) {
- cleanupCirculatingBuffers();
- s = Status::OK;
- }
- return s;
-}
-
-hardware::Return<Status>
-CameraHardwareInterface::setSwapInterval(int32_t interval) {
- Status s = Status::INTERNAL_ERROR;
- ANativeWindow *a = mPreviewWindow.get();
- if (a == nullptr) {
- ALOGE("%s: preview window is null", __FUNCTION__);
- return s;
- }
- mPreviewSwapInterval = interval;
- int rc = a->setSwapInterval(a, interval);
- if (rc == OK) {
- s = Status::OK;
- }
- return s;
-}
-
-hardware::Return<void>
-CameraHardwareInterface::getMinUndequeuedBufferCount(getMinUndequeuedBufferCount_cb _hidl_cb) {
- ANativeWindow *a = mPreviewWindow.get();
- if (a == nullptr) {
- ALOGE("%s: preview window is null", __FUNCTION__);
- return hardware::Void();
- }
- int count = 0;
- int rc = a->query(a, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &count);
- Status s = Status::INTERNAL_ERROR;
- if (rc == OK) {
- s = Status::OK;
- }
- _hidl_cb(s, count);
- return hardware::Void();
-}
-
-hardware::Return<Status>
-CameraHardwareInterface::setTimestamp(int64_t timestamp) {
- Status s = Status::INTERNAL_ERROR;
- ANativeWindow *a = mPreviewWindow.get();
- if (a == nullptr) {
- ALOGE("%s: preview window is null", __FUNCTION__);
- return s;
- }
- int rc = native_window_set_buffers_timestamp(a, timestamp);
- if (rc == OK) {
- s = Status::OK;
- }
- return s;
-}
-
-status_t CameraHardwareInterface::setPreviewWindow(const sp<ANativeWindow>& buf)
-{
- ALOGV("%s(%s) buf %p", __FUNCTION__, mName.string(), buf.get());
- if (CC_LIKELY(mHidlDevice != nullptr)) {
- mPreviewWindow = buf;
- if (buf != nullptr) {
- if (mPreviewScalingMode != NOT_SET) {
- setPreviewScalingMode(mPreviewScalingMode);
- }
- if (mPreviewTransform != NOT_SET) {
- setPreviewTransform(mPreviewTransform);
- }
- }
- return CameraProviderManager::mapToStatusT(
- mHidlDevice->setPreviewWindow(buf.get() ? this : nullptr));
- }
- return INVALID_OPERATION;
-}
-
-void CameraHardwareInterface::setCallbacks(notify_callback notify_cb,
- data_callback data_cb,
- data_callback_timestamp data_cb_timestamp,
- data_callback_timestamp_batch data_cb_timestamp_batch,
- void* user)
-{
- mNotifyCb = notify_cb;
- mDataCb = data_cb;
- mDataCbTimestamp = data_cb_timestamp;
- mDataCbTimestampBatch = data_cb_timestamp_batch;
- mCbUser = user;
-
- ALOGV("%s(%s)", __FUNCTION__, mName.string());
-}
-
-void CameraHardwareInterface::enableMsgType(int32_t msgType)
-{
- ALOGV("%s(%s)", __FUNCTION__, mName.string());
- if (CC_LIKELY(mHidlDevice != nullptr)) {
- mHidlDevice->enableMsgType(msgType);
- }
-}
-
-void CameraHardwareInterface::disableMsgType(int32_t msgType)
-{
- ALOGV("%s(%s)", __FUNCTION__, mName.string());
- if (CC_LIKELY(mHidlDevice != nullptr)) {
- mHidlDevice->disableMsgType(msgType);
- }
-}
-
-int CameraHardwareInterface::msgTypeEnabled(int32_t msgType)
-{
- ALOGV("%s(%s)", __FUNCTION__, mName.string());
- if (CC_LIKELY(mHidlDevice != nullptr)) {
- return mHidlDevice->msgTypeEnabled(msgType);
- }
- return false;
-}
-
-status_t CameraHardwareInterface::startPreview()
-{
- ALOGV("%s(%s)", __FUNCTION__, mName.string());
- if (CC_LIKELY(mHidlDevice != nullptr)) {
- return CameraProviderManager::mapToStatusT(
- mHidlDevice->startPreview());
- }
- return INVALID_OPERATION;
-}
-
-void CameraHardwareInterface::stopPreview()
-{
- ALOGV("%s(%s)", __FUNCTION__, mName.string());
- if (CC_LIKELY(mHidlDevice != nullptr)) {
- mHidlDevice->stopPreview();
- }
-}
-
-int CameraHardwareInterface::previewEnabled()
-{
- ALOGV("%s(%s)", __FUNCTION__, mName.string());
- if (CC_LIKELY(mHidlDevice != nullptr)) {
- return mHidlDevice->previewEnabled();
- }
- return false;
-}
-
-status_t CameraHardwareInterface::storeMetaDataInBuffers(int enable)
-{
- ALOGV("%s(%s)", __FUNCTION__, mName.string());
- if (CC_LIKELY(mHidlDevice != nullptr)) {
- return CameraProviderManager::mapToStatusT(
- mHidlDevice->storeMetaDataInBuffers(enable));
- }
- return enable ? INVALID_OPERATION: OK;
-}
-
-status_t CameraHardwareInterface::startRecording()
-{
- ALOGV("%s(%s)", __FUNCTION__, mName.string());
- if (CC_LIKELY(mHidlDevice != nullptr)) {
- return CameraProviderManager::mapToStatusT(
- mHidlDevice->startRecording());
- }
- return INVALID_OPERATION;
-}
-
-/**
- * Stop a previously started recording.
- */
-void CameraHardwareInterface::stopRecording()
-{
- ALOGV("%s(%s)", __FUNCTION__, mName.string());
- if (CC_LIKELY(mHidlDevice != nullptr)) {
- mHidlDevice->stopRecording();
- }
-}
-
-/**
- * Returns true if recording is enabled.
- */
-int CameraHardwareInterface::recordingEnabled()
-{
- ALOGV("%s(%s)", __FUNCTION__, mName.string());
- if (CC_LIKELY(mHidlDevice != nullptr)) {
- return mHidlDevice->recordingEnabled();
- }
- return false;
-}
-
-void CameraHardwareInterface::releaseRecordingFrame(const sp<IMemory>& mem)
-{
- ALOGV("%s(%s)", __FUNCTION__, mName.string());
- ssize_t offset;
- size_t size;
- sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
- int heapId = heap->getHeapID();
- int bufferIndex = offset / size;
- if (CC_LIKELY(mHidlDevice != nullptr)) {
- if (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* 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;
- mHidlDevice->releaseRecordingFrameHandle(heapId, bufferIndex, frame);
- native_handle_close(nh);
- native_handle_delete(nh);
- } else {
- mHidlDevice->releaseRecordingFrame(heapId, bufferIndex);
- }
- }
-}
-
-void CameraHardwareInterface::releaseRecordingFrameBatch(const std::vector<sp<IMemory>>& frames)
-{
- ALOGV("%s(%s)", __FUNCTION__, mName.string());
- size_t n = frames.size();
- std::vector<VideoFrameMessage> msgs;
- msgs.reserve(n);
- for (auto& mem : frames) {
- if (CC_LIKELY(mHidlDevice != nullptr)) {
- ssize_t offset;
- size_t size;
- sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
- if (size == sizeof(VideoNativeHandleMetadata)) {
- uint32_t heapId = heap->getHeapID();
- uint32_t bufferIndex = offset / 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).
- 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;
- msgs.push_back({nh, heapId, bufferIndex});
- } else {
- ALOGE("%s only supports VideoNativeHandleMetadata mode", __FUNCTION__);
- return;
- }
- }
- }
-
- mHidlDevice->releaseRecordingFrameHandleBatch(msgs);
-
- for (auto& msg : msgs) {
- native_handle_t* nh = const_cast<native_handle_t*>(msg.frameData.getNativeHandle());
- native_handle_close(nh);
- native_handle_delete(nh);
- }
-}
-
-status_t CameraHardwareInterface::autoFocus()
-{
- ALOGV("%s(%s)", __FUNCTION__, mName.string());
- if (CC_LIKELY(mHidlDevice != nullptr)) {
- return CameraProviderManager::mapToStatusT(
- mHidlDevice->autoFocus());
- }
- return INVALID_OPERATION;
-}
-
-status_t CameraHardwareInterface::cancelAutoFocus()
-{
- ALOGV("%s(%s)", __FUNCTION__, mName.string());
- if (CC_LIKELY(mHidlDevice != nullptr)) {
- return CameraProviderManager::mapToStatusT(
- mHidlDevice->cancelAutoFocus());
- }
- return INVALID_OPERATION;
-}
-
-status_t CameraHardwareInterface::takePicture()
-{
- ALOGV("%s(%s)", __FUNCTION__, mName.string());
- if (CC_LIKELY(mHidlDevice != nullptr)) {
- return CameraProviderManager::mapToStatusT(
- mHidlDevice->takePicture());
- }
- return INVALID_OPERATION;
-}
-
-status_t CameraHardwareInterface::cancelPicture()
-{
- ALOGV("%s(%s)", __FUNCTION__, mName.string());
- if (CC_LIKELY(mHidlDevice != nullptr)) {
- return CameraProviderManager::mapToStatusT(
- mHidlDevice->cancelPicture());
- }
- return INVALID_OPERATION;
-}
-
-status_t CameraHardwareInterface::setParameters(const CameraParameters ¶ms)
-{
- ALOGV("%s(%s)", __FUNCTION__, mName.string());
- if (CC_LIKELY(mHidlDevice != nullptr)) {
- return CameraProviderManager::mapToStatusT(
- mHidlDevice->setParameters(params.flatten().string()));
- }
- return INVALID_OPERATION;
-}
-
-CameraParameters CameraHardwareInterface::getParameters() const
-{
- ALOGV("%s(%s)", __FUNCTION__, mName.string());
- CameraParameters parms;
- if (CC_LIKELY(mHidlDevice != nullptr)) {
- hardware::hidl_string outParam;
- mHidlDevice->getParameters(
- [&outParam](const auto& outStr) {
- outParam = outStr;
- });
- String8 tmp(outParam.c_str());
- parms.unflatten(tmp);
- }
- return parms;
-}
-
-status_t CameraHardwareInterface::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2)
-{
- ALOGV("%s(%s)", __FUNCTION__, mName.string());
- if (CC_LIKELY(mHidlDevice != nullptr)) {
- return CameraProviderManager::mapToStatusT(
- mHidlDevice->sendCommand((CommandType) cmd, arg1, arg2));
- }
- return INVALID_OPERATION;
-}
-
-/**
- * Release the hardware resources owned by this object. Note that this is
- * *not* done in the destructor.
- */
-void CameraHardwareInterface::release() {
- ALOGV("%s(%s)", __FUNCTION__, mName.string());
- if (CC_LIKELY(mHidlDevice != nullptr)) {
- mHidlDevice->close();
- mHidlDevice.clear();
- }
-}
-
-/**
- * Dump state of the camera hardware
- */
-status_t CameraHardwareInterface::dump(int fd, const Vector<String16>& /*args*/) const
-{
- ALOGV("%s(%s)", __FUNCTION__, mName.string());
- if (CC_LIKELY(mHidlDevice != nullptr)) {
- native_handle_t* handle = native_handle_create(1,0);
- handle->data[0] = fd;
- Status s = mHidlDevice->dumpState(handle);
- native_handle_delete(handle);
- return CameraProviderManager::mapToStatusT(s);
- }
- return OK; // It's fine if the HAL doesn't implement dump()
-}
-
-void CameraHardwareInterface::sNotifyCb(int32_t msg_type, int32_t ext1,
- int32_t ext2, void *user)
-{
- ALOGV("%s", __FUNCTION__);
- CameraHardwareInterface *object =
- static_cast<CameraHardwareInterface *>(user);
- object->mNotifyCb(msg_type, ext1, ext2, object->mCbUser);
-}
-
-void CameraHardwareInterface::sDataCb(int32_t msg_type,
- const camera_memory_t *data, unsigned int index,
- camera_frame_metadata_t *metadata,
- void *user)
-{
- ALOGV("%s", __FUNCTION__);
- CameraHardwareInterface *object =
- static_cast<CameraHardwareInterface *>(user);
- sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(data->handle));
- if (index >= mem->mNumBufs) {
- ALOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__,
- index, mem->mNumBufs);
- return;
- }
- object->mDataCb(msg_type, mem->mBuffers[index], metadata, object->mCbUser);
-}
-
-void CameraHardwareInterface::sDataCbTimestamp(nsecs_t timestamp, int32_t msg_type,
- const camera_memory_t *data, unsigned index,
- void *user)
-{
- ALOGV("%s", __FUNCTION__);
- CameraHardwareInterface *object =
- static_cast<CameraHardwareInterface *>(user);
- // Start refcounting the heap object from here on. When the clients
- // drop all references, it will be destroyed (as well as the enclosed
- // MemoryHeapBase.
- sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(data->handle));
- if (index >= mem->mNumBufs) {
- ALOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__,
- index, mem->mNumBufs);
- return;
- }
- object->mDataCbTimestamp(timestamp, msg_type, mem->mBuffers[index], object->mCbUser);
-}
-
-camera_memory_t* CameraHardwareInterface::sGetMemory(
- int fd, size_t buf_size, uint_t num_bufs,
- void *user __attribute__((unused)))
-{
- CameraHeapMemory *mem;
- if (fd < 0) {
- mem = new CameraHeapMemory(buf_size, num_bufs);
- } else {
- mem = new CameraHeapMemory(fd, buf_size, num_bufs);
- }
- mem->incStrong(mem);
- return &mem->handle;
-}
-
-void CameraHardwareInterface::sPutMemory(camera_memory_t *data)
-{
- if (!data) {
- return;
- }
-
- CameraHeapMemory *mem = static_cast<CameraHeapMemory *>(data->handle);
- mem->decStrong(mem);
-}
-
-}; // namespace android
diff --git a/services/camera/libcameraservice/device1/CameraHardwareInterface.h b/services/camera/libcameraservice/device1/CameraHardwareInterface.h
deleted file mode 100644
index e519b04..0000000
--- a/services/camera/libcameraservice/device1/CameraHardwareInterface.h
+++ /dev/null
@@ -1,488 +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_CAMERA_HARDWARE_INTERFACE_H
-#define ANDROID_HARDWARE_CAMERA_HARDWARE_INTERFACE_H
-
-#include <unordered_map>
-#include <binder/IMemory.h>
-#include <binder/MemoryBase.h>
-#include <binder/MemoryHeapBase.h>
-#include <utils/RefBase.h>
-#include <ui/GraphicBuffer.h>
-#include <camera/Camera.h>
-#include <camera/CameraParameters.h>
-#include <system/window.h>
-#include <hardware/camera.h>
-
-#include <common/CameraProviderManager.h>
-
-namespace android {
-
-typedef void (*notify_callback)(int32_t msgType,
- int32_t ext1,
- int32_t ext2,
- void* user);
-
-typedef void (*data_callback)(int32_t msgType,
- const sp<IMemory> &dataPtr,
- camera_frame_metadata_t *metadata,
- void* user);
-
-typedef void (*data_callback_timestamp)(nsecs_t timestamp,
- int32_t msgType,
- const sp<IMemory> &dataPtr,
- void *user);
-
-struct HandleTimestampMessage {
- nsecs_t timestamp;
- const sp<IMemory> dataPtr;
-};
-
-typedef void (*data_callback_timestamp_batch)(
- int32_t msgType,
- const std::vector<HandleTimestampMessage>&, void* user);
-
-/**
- * CameraHardwareInterface.h defines the interface to the
- * camera hardware abstraction layer, used for setting and getting
- * parameters, live previewing, and taking pictures. It is used for
- * HAL devices with version CAMERA_DEVICE_API_VERSION_1_0 only.
- *
- * It is a referenced counted interface with RefBase as its base class.
- * CameraService calls openCameraHardware() to retrieve a strong pointer to the
- * instance of this interface and may be called multiple times. The
- * following steps describe a typical sequence:
- *
- * -# After CameraService calls openCameraHardware(), getParameters() and
- * setParameters() are used to initialize the camera instance.
- * -# startPreview() is called.
- *
- * Prior to taking a picture, CameraService often calls autofocus(). When auto
- * focusing has completed, the camera instance sends a CAMERA_MSG_FOCUS notification,
- * which informs the application whether focusing was successful. The camera instance
- * only sends this message once and it is up to the application to call autoFocus()
- * again if refocusing is desired.
- *
- * CameraService calls takePicture() to request the camera instance take a
- * picture. At this point, if a shutter, postview, raw, and/or compressed
- * callback is desired, the corresponding message must be enabled. Any memory
- * provided in a data callback must be copied if it's needed after returning.
- */
-
-class CameraHardwareInterface :
- public virtual RefBase,
- public virtual hardware::camera::device::V1_0::ICameraDeviceCallback,
- public virtual hardware::camera::device::V1_0::ICameraDevicePreviewCallback {
-
-public:
- explicit CameraHardwareInterface(const char *name):
- mHidlDevice(nullptr),
- mName(name),
- mPreviewScalingMode(NOT_SET),
- mPreviewTransform(NOT_SET),
- mPreviewWidth(NOT_SET),
- mPreviewHeight(NOT_SET),
- mPreviewFormat(NOT_SET),
- mPreviewUsage(0),
- mPreviewSwapInterval(NOT_SET),
- mPreviewCrop{NOT_SET,NOT_SET,NOT_SET,NOT_SET}
- {
- }
-
- ~CameraHardwareInterface();
-
- status_t initialize(sp<CameraProviderManager> manager);
-
- /** Set the ANativeWindow to which preview frames are sent */
- status_t setPreviewWindow(const sp<ANativeWindow>& buf);
-
- status_t setPreviewScalingMode(int scalingMode);
-
- status_t setPreviewTransform(int transform);
-
- /** Set the notification and data callbacks */
- void setCallbacks(notify_callback notify_cb,
- data_callback data_cb,
- data_callback_timestamp data_cb_timestamp,
- data_callback_timestamp_batch data_cb_timestamp_batch,
- void* user);
-
- /**
- * The following three functions all take a msgtype,
- * which is a bitmask of the messages defined in
- * include/ui/Camera.h
- */
-
- /**
- * Enable a message, or set of messages.
- */
- void enableMsgType(int32_t msgType);
-
- /**
- * Disable a message, or a set of messages.
- *
- * Once received a call to disableMsgType(CAMERA_MSG_VIDEO_FRAME), camera hal
- * should not rely on its client to call releaseRecordingFrame() to release
- * video recording frames sent out by the cameral hal before and after the
- * disableMsgType(CAMERA_MSG_VIDEO_FRAME) call. Camera hal clients must not
- * modify/access any video recording frame after calling
- * disableMsgType(CAMERA_MSG_VIDEO_FRAME).
- */
- void disableMsgType(int32_t msgType);
-
- /**
- * Query whether a message, or a set of messages, is enabled.
- * Note that this is operates as an AND, if any of the messages
- * queried are off, this will return false.
- */
- int msgTypeEnabled(int32_t msgType);
-
- /**
- * Start preview mode.
- */
- status_t startPreview();
-
- /**
- * Stop a previously started preview.
- */
- void stopPreview();
-
- /**
- * Returns true if preview is enabled.
- */
- int previewEnabled();
-
- /**
- * Request the camera hal to store meta data or real YUV data in
- * the video buffers send out via CAMERA_MSG_VIDEO_FRRAME for a
- * recording session. If it is not called, the default camera
- * hal behavior is to store real YUV data in the video buffers.
- *
- * This method should be called before startRecording() in order
- * to be effective.
- *
- * If meta data is stored in the video buffers, it is up to the
- * receiver of the video buffers to interpret the contents and
- * to find the actual frame data with the help of the meta data
- * in the buffer. How this is done is outside of the scope of
- * this method.
- *
- * Some camera hal may not support storing meta data in the video
- * buffers, but all camera hal should support storing real YUV data
- * in the video buffers. If the camera hal does not support storing
- * the meta data in the video buffers when it is requested to do
- * do, INVALID_OPERATION must be returned. It is very useful for
- * the camera hal to pass meta data rather than the actual frame
- * data directly to the video encoder, since the amount of the
- * uncompressed frame data can be very large if video size is large.
- *
- * @param enable if true to instruct the camera hal to store
- * meta data in the video buffers; false to instruct
- * the camera hal to store real YUV data in the video
- * buffers.
- *
- * @return OK on success.
- */
-
- status_t storeMetaDataInBuffers(int enable);
-
- /**
- * Start record mode. When a record image is available a CAMERA_MSG_VIDEO_FRAME
- * message is sent with the corresponding frame. Every record frame must be released
- * by a cameral hal client via releaseRecordingFrame() before the client calls
- * disableMsgType(CAMERA_MSG_VIDEO_FRAME). After the client calls
- * disableMsgType(CAMERA_MSG_VIDEO_FRAME), it is camera hal's responsibility
- * to manage the life-cycle of the video recording frames, and the client must
- * not modify/access any video recording frames.
- */
- status_t startRecording();
-
- /**
- * Stop a previously started recording.
- */
- void stopRecording();
-
- /**
- * Returns true if recording is enabled.
- */
- int recordingEnabled();
-
- /**
- * Release a record frame previously returned by CAMERA_MSG_VIDEO_FRAME.
- *
- * It is camera hal client's responsibility to release video recording
- * frames sent out by the camera hal before the camera hal receives
- * a call to disableMsgType(CAMERA_MSG_VIDEO_FRAME). After it receives
- * the call to disableMsgType(CAMERA_MSG_VIDEO_FRAME), it is camera hal's
- * responsibility of managing the life-cycle of the video recording
- * frames.
- */
- void releaseRecordingFrame(const sp<IMemory>& mem);
-
- /**
- * Release a batch of recording frames previously returned by
- * CAMERA_MSG_VIDEO_FRAME. This method only supports frames that are
- * stored as VideoNativeHandleMetadata.
- *
- * It is camera hal client's responsibility to release video recording
- * frames sent out by the camera hal before the camera hal receives
- * a call to disableMsgType(CAMERA_MSG_VIDEO_FRAME). After it receives
- * the call to disableMsgType(CAMERA_MSG_VIDEO_FRAME), it is camera hal's
- * responsibility of managing the life-cycle of the video recording
- * frames.
- */
- void releaseRecordingFrameBatch(const std::vector<sp<IMemory>>& frames);
-
- /**
- * Start auto focus, the notification callback routine is called
- * with CAMERA_MSG_FOCUS once when focusing is complete. autoFocus()
- * will be called again if another auto focus is needed.
- */
- status_t autoFocus();
-
- /**
- * Cancels auto-focus function. If the auto-focus is still in progress,
- * this function will cancel it. Whether the auto-focus is in progress
- * or not, this function will return the focus position to the default.
- * If the camera does not support auto-focus, this is a no-op.
- */
- status_t cancelAutoFocus();
-
- /**
- * Take a picture.
- */
- status_t takePicture();
-
- /**
- * Cancel a picture that was started with takePicture. Calling this
- * method when no picture is being taken is a no-op.
- */
- status_t cancelPicture();
-
- /**
- * Set the camera parameters. This returns BAD_VALUE if any parameter is
- * invalid or not supported. */
- status_t setParameters(const CameraParameters ¶ms);
-
- /** Return the camera parameters. */
- CameraParameters getParameters() const;
-
- /**
- * Send command to camera driver.
- */
- status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2);
-
- /**
- * Release the hardware resources owned by this object. Note that this is
- * *not* done in the destructor.
- */
- void release();
-
- /**
- * Dump state of the camera hardware
- */
- status_t dump(int fd, const Vector<String16>& /*args*/) const;
-
-private:
- sp<hardware::camera::device::V1_0::ICameraDevice> mHidlDevice;
- String8 mName;
-
- static void sNotifyCb(int32_t msg_type, int32_t ext1,
- int32_t ext2, void *user);
-
- static void sDataCb(int32_t msg_type,
- const camera_memory_t *data, unsigned int index,
- camera_frame_metadata_t *metadata,
- void *user);
-
- static void sDataCbTimestamp(nsecs_t timestamp, int32_t msg_type,
- const camera_memory_t *data, unsigned index,
- void *user);
-
- // This is a utility class that combines a MemoryHeapBase and a MemoryBase
- // in one. Since we tend to use them in a one-to-one relationship, this is
- // handy.
- class CameraHeapMemory : public RefBase {
- public:
- CameraHeapMemory(int fd, size_t buf_size, uint_t num_buffers = 1) :
- mBufSize(buf_size),
- mNumBufs(num_buffers)
- {
- mHeap = new MemoryHeapBase(fd, buf_size * num_buffers);
- commonInitialization();
- }
-
- explicit CameraHeapMemory(size_t buf_size, uint_t num_buffers = 1) :
- mBufSize(buf_size),
- mNumBufs(num_buffers)
- {
- mHeap = new MemoryHeapBase(buf_size * num_buffers);
- commonInitialization();
- }
-
- void commonInitialization()
- {
- handle.data = mHeap->base();
- handle.size = mBufSize * mNumBufs;
- handle.handle = this;
-
- mBuffers = new sp<MemoryBase>[mNumBufs];
- for (uint_t i = 0; i < mNumBufs; i++)
- mBuffers[i] = new MemoryBase(mHeap,
- i * mBufSize,
- mBufSize);
-
- handle.release = sPutMemory;
- }
-
- virtual ~CameraHeapMemory()
- {
- delete [] mBuffers;
- }
-
- size_t mBufSize;
- uint_t mNumBufs;
- sp<MemoryHeapBase> mHeap;
- sp<MemoryBase> *mBuffers;
-
- camera_memory_t handle;
- };
-
- static camera_memory_t* sGetMemory(int fd, size_t buf_size, uint_t num_bufs,
- void *user __attribute__((unused)));
-
- static void sPutMemory(camera_memory_t *data);
-
- std::pair<bool, uint64_t> getBufferId(ANativeWindowBuffer* anb);
- void cleanupCirculatingBuffers();
-
- /**
- * Implementation of android::hardware::camera::device::V1_0::ICameraDeviceCallback
- */
- hardware::Return<void> notifyCallback(
- hardware::camera::device::V1_0::NotifyCallbackMsg msgType,
- int32_t ext1, int32_t ext2) override;
- hardware::Return<uint32_t> registerMemory(
- const hardware::hidl_handle& descriptor,
- uint32_t bufferSize, uint32_t bufferCount) override;
- hardware::Return<void> unregisterMemory(uint32_t memId) override;
- hardware::Return<void> dataCallback(
- hardware::camera::device::V1_0::DataCallbackMsg msgType,
- uint32_t data, uint32_t bufferIndex,
- const hardware::camera::device::V1_0::CameraFrameMetadata& metadata) override;
- hardware::Return<void> dataCallbackTimestamp(
- hardware::camera::device::V1_0::DataCallbackMsg msgType,
- uint32_t data, uint32_t bufferIndex, int64_t timestamp) override;
- hardware::Return<void> handleCallbackTimestamp(
- hardware::camera::device::V1_0::DataCallbackMsg msgType,
- const hardware::hidl_handle& frameData, uint32_t data,
- uint32_t bufferIndex, int64_t timestamp) override;
- hardware::Return<void> handleCallbackTimestampBatch(
- hardware::camera::device::V1_0::DataCallbackMsg msgType,
- const hardware::hidl_vec<
- hardware::camera::device::V1_0::HandleTimestampMessage>&) override;
-
- /**
- * Implementation of android::hardware::camera::device::V1_0::ICameraDevicePreviewCallback
- */
- hardware::Return<void> dequeueBuffer(dequeueBuffer_cb _hidl_cb) override;
- hardware::Return<hardware::camera::common::V1_0::Status>
- enqueueBuffer(uint64_t bufferId) override;
- hardware::Return<hardware::camera::common::V1_0::Status>
- cancelBuffer(uint64_t bufferId) override;
- hardware::Return<hardware::camera::common::V1_0::Status>
- setBufferCount(uint32_t count) override;
- hardware::Return<hardware::camera::common::V1_0::Status>
- setBuffersGeometry(uint32_t w, uint32_t h,
- hardware::graphics::common::V1_0::PixelFormat format) override;
- hardware::Return<hardware::camera::common::V1_0::Status>
- setCrop(int32_t left, int32_t top, int32_t right, int32_t bottom) override;
- hardware::Return<hardware::camera::common::V1_0::Status>
- setUsage(hardware::graphics::common::V1_0::BufferUsage usage) override;
- hardware::Return<hardware::camera::common::V1_0::Status>
- setSwapInterval(int32_t interval) override;
- hardware::Return<void> getMinUndequeuedBufferCount(
- getMinUndequeuedBufferCount_cb _hidl_cb) override;
- hardware::Return<hardware::camera::common::V1_0::Status>
- setTimestamp(int64_t timestamp) override;
-
- sp<ANativeWindow> mPreviewWindow;
-
- notify_callback mNotifyCb;
- data_callback mDataCb;
- data_callback_timestamp mDataCbTimestamp;
- data_callback_timestamp_batch mDataCbTimestampBatch;
- void *mCbUser;
-
- // Cached values for preview stream parameters
- static const int NOT_SET = -1;
- int mPreviewScalingMode;
- int mPreviewTransform;
- int mPreviewWidth;
- int mPreviewHeight;
- int mPreviewFormat;
- uint64_t mPreviewUsage;
- int mPreviewSwapInterval;
- android_native_rect_t mPreviewCrop;
-
- struct BufferHasher {
- size_t operator()(const buffer_handle_t& buf) const {
- if (buf == nullptr)
- return 0;
-
- size_t result = 1;
- result = 31 * result + buf->numFds;
- result = 31 * result + buf->numInts;
- int length = buf->numFds + buf->numInts;
- for (int i = 0; i < length; i++) {
- result = 31 * result + buf->data[i];
- }
- return result;
- }
- };
-
- struct BufferComparator {
- bool operator()(const buffer_handle_t& buf1, const buffer_handle_t& buf2) const {
- if (buf1->numFds == buf2->numFds && buf1->numInts == buf2->numInts) {
- int length = buf1->numFds + buf1->numInts;
- for (int i = 0; i < length; i++) {
- if (buf1->data[i] != buf2->data[i]) {
- return false;
- }
- }
- return true;
- }
- return false;
- }
- };
-
- std::mutex mBufferIdMapLock; // protecting mBufferIdMap and mNextBufferId
- typedef std::unordered_map<const buffer_handle_t, uint64_t,
- BufferHasher, BufferComparator> BufferIdMap;
- // stream ID -> per stream buffer ID map
- BufferIdMap mBufferIdMap;
- std::unordered_map<uint64_t, ANativeWindowBuffer*> mReversedBufMap;
- uint64_t mNextBufferId = 1;
- static const uint64_t BUFFER_ID_NO_BUFFER = 0;
-
- std::mutex mHidlMemPoolMapLock; // protecting mHidlMemPoolMap
- std::unordered_map<int, camera_memory_t*> mHidlMemPoolMap;
-};
-
-}; // namespace android
-
-#endif
diff --git a/services/camera/libcameraservice/device3/BufferUtils.cpp b/services/camera/libcameraservice/device3/BufferUtils.cpp
index cc29390..f3adf20 100644
--- a/services/camera/libcameraservice/device3/BufferUtils.cpp
+++ b/services/camera/libcameraservice/device3/BufferUtils.cpp
@@ -28,14 +28,14 @@
namespace android {
namespace camera3 {
-camera3_buffer_status_t mapHidlBufferStatus(hardware::camera::device::V3_2::BufferStatus status) {
+camera_buffer_status_t mapHidlBufferStatus(hardware::camera::device::V3_2::BufferStatus status) {
using hardware::camera::device::V3_2::BufferStatus;
switch (status) {
- case BufferStatus::OK: return CAMERA3_BUFFER_STATUS_OK;
- case BufferStatus::ERROR: return CAMERA3_BUFFER_STATUS_ERROR;
+ case BufferStatus::OK: return CAMERA_BUFFER_STATUS_OK;
+ case BufferStatus::ERROR: return CAMERA_BUFFER_STATUS_ERROR;
}
- return CAMERA3_BUFFER_STATUS_ERROR;
+ return CAMERA_BUFFER_STATUS_ERROR;
}
void BufferRecords::takeInflightBufferMap(BufferRecords& other) {
diff --git a/services/camera/libcameraservice/device3/BufferUtils.h b/services/camera/libcameraservice/device3/BufferUtils.h
index 452a908..1e1cd60 100644
--- a/services/camera/libcameraservice/device3/BufferUtils.h
+++ b/services/camera/libcameraservice/device3/BufferUtils.h
@@ -25,9 +25,6 @@
#include <android/hardware/camera/device/3.2/ICameraDevice.h>
-// TODO: remove legacy camera3.h references
-#include "hardware/camera3.h"
-
#include <device3/Camera3OutputInterface.h>
namespace android {
@@ -158,7 +155,7 @@
static const uint64_t BUFFER_ID_NO_BUFFER = 0;
- camera3_buffer_status_t mapHidlBufferStatus(
+ camera_buffer_status_t mapHidlBufferStatus(
hardware::camera::device::V3_2::BufferStatus status);
} // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3BufferManager.cpp b/services/camera/libcameraservice/device3/Camera3BufferManager.cpp
index d6bf83e..a556200 100644
--- a/services/camera/libcameraservice/device3/Camera3BufferManager.cpp
+++ b/services/camera/libcameraservice/device3/Camera3BufferManager.cpp
@@ -40,16 +40,17 @@
ATRACE_CALL();
int streamId = streamInfo.streamId;
- int streamSetId = streamInfo.streamSetId;
+ StreamSetKey streamSetKey = {streamInfo.streamSetId, streamInfo.isMultiRes};
- if (streamId == CAMERA3_STREAM_ID_INVALID || streamSetId == CAMERA3_STREAM_SET_ID_INVALID) {
+ if (streamId == CAMERA3_STREAM_ID_INVALID ||
+ streamSetKey.id == CAMERA3_STREAM_SET_ID_INVALID) {
ALOGE("%s: Stream id (%d) or stream set id (%d) is invalid",
- __FUNCTION__, streamId, streamSetId);
+ __FUNCTION__, streamId, streamSetKey.id);
return BAD_VALUE;
}
if (streamInfo.totalBufferCount > kMaxBufferCount || streamInfo.totalBufferCount == 0) {
ALOGE("%s: Stream id (%d) with stream set id (%d) total buffer count %zu is invalid",
- __FUNCTION__, streamId, streamSetId, streamInfo.totalBufferCount);
+ __FUNCTION__, streamId, streamSetKey.id, streamInfo.totalBufferCount);
return BAD_VALUE;
}
if (!streamInfo.isConfigured) {
@@ -75,7 +76,8 @@
for (size_t i = 0; i < mStreamSetMap.size(); i++) {
ssize_t streamIdx = mStreamSetMap[i].streamInfoMap.indexOfKey(streamId);
if (streamIdx != NAME_NOT_FOUND &&
- mStreamSetMap[i].streamInfoMap[streamIdx].streamSetId != streamInfo.streamSetId) {
+ mStreamSetMap[i].streamInfoMap[streamIdx].streamSetId != streamInfo.streamSetId &&
+ mStreamSetMap[i].streamInfoMap[streamIdx].isMultiRes != streamInfo.isMultiRes) {
ALOGE("%s: It is illegal to register the same stream id with different stream set",
__FUNCTION__);
return BAD_VALUE;
@@ -83,20 +85,20 @@
}
// Check if there is an existing stream set registered; if not, create one; otherwise, add this
// stream info to the existing stream set entry.
- ssize_t setIdx = mStreamSetMap.indexOfKey(streamSetId);
+ ssize_t setIdx = mStreamSetMap.indexOfKey(streamSetKey);
if (setIdx == NAME_NOT_FOUND) {
- ALOGV("%s: stream set %d is not registered to stream set map yet, create it.",
- __FUNCTION__, streamSetId);
+ ALOGV("%s: stream set %d(%d) is not registered to stream set map yet, create it.",
+ __FUNCTION__, streamSetKey.id, streamSetKey.isMultiRes);
// Create stream info map, then add to mStreamsetMap.
StreamSet newStreamSet;
- setIdx = mStreamSetMap.add(streamSetId, newStreamSet);
+ setIdx = mStreamSetMap.add(streamSetKey, newStreamSet);
}
// Update stream set map and water mark.
StreamSet& currentStreamSet = mStreamSetMap.editValueAt(setIdx);
ssize_t streamIdx = currentStreamSet.streamInfoMap.indexOfKey(streamId);
if (streamIdx != NAME_NOT_FOUND) {
- ALOGW("%s: stream %d was already registered with stream set %d",
- __FUNCTION__, streamId, streamSetId);
+ ALOGW("%s: stream %d was already registered with stream set %d(%d)",
+ __FUNCTION__, streamId, streamSetKey.id, streamSetKey.isMultiRes);
return OK;
}
currentStreamSet.streamInfoMap.add(streamId, streamInfo);
@@ -113,21 +115,22 @@
return OK;
}
-status_t Camera3BufferManager::unregisterStream(int streamId, int streamSetId) {
+status_t Camera3BufferManager::unregisterStream(int streamId, int streamSetId, bool isMultiRes) {
ATRACE_CALL();
Mutex::Autolock l(mLock);
- ALOGV("%s: unregister stream %d with stream set %d", __FUNCTION__,
- streamId, streamSetId);
+ ALOGV("%s: unregister stream %d with stream set %d(%d)", __FUNCTION__,
+ streamId, streamSetId, isMultiRes);
- if (!checkIfStreamRegisteredLocked(streamId, streamSetId)){
- ALOGE("%s: stream %d with set id %d wasn't properly registered to this buffer manager!",
- __FUNCTION__, streamId, streamSetId);
+ StreamSetKey streamSetKey = {streamSetId, isMultiRes};
+ if (!checkIfStreamRegisteredLocked(streamId, streamSetKey)){
+ ALOGE("%s: stream %d with set %d(%d) wasn't properly registered to this"
+ " buffer manager!", __FUNCTION__, streamId, streamSetId, isMultiRes);
return BAD_VALUE;
}
// De-list all the buffers associated with this stream first.
- StreamSet& currentSet = mStreamSetMap.editValueFor(streamSetId);
+ StreamSet& currentSet = mStreamSetMap.editValueFor(streamSetKey);
BufferCountMap& handOutBufferCounts = currentSet.handoutBufferCountMap;
BufferCountMap& attachedBufferCounts = currentSet.attachedBufferCountMap;
InfoMap& infoMap = currentSet.streamInfoMap;
@@ -150,26 +153,28 @@
// Remove this stream set if all its streams have been removed.
if (handOutBufferCounts.size() == 0 && infoMap.size() == 0) {
- mStreamSetMap.removeItem(streamSetId);
+ mStreamSetMap.removeItem(streamSetKey);
}
return OK;
}
-void Camera3BufferManager::notifyBufferRemoved(int streamId, int streamSetId) {
+void Camera3BufferManager::notifyBufferRemoved(int streamId, int streamSetId, bool isMultiRes) {
Mutex::Autolock l(mLock);
- StreamSet &streamSet = mStreamSetMap.editValueFor(streamSetId);
+ StreamSetKey streamSetKey = {streamSetId, isMultiRes};
+ StreamSet &streamSet = mStreamSetMap.editValueFor(streamSetKey);
size_t& attachedBufferCount =
streamSet.attachedBufferCountMap.editValueFor(streamId);
attachedBufferCount--;
}
status_t Camera3BufferManager::checkAndFreeBufferOnOtherStreamsLocked(
- int streamId, int streamSetId) {
+ int streamId, StreamSetKey streamSetKey) {
StreamId firstOtherStreamId = CAMERA3_STREAM_ID_INVALID;
- StreamSet &streamSet = mStreamSetMap.editValueFor(streamSetId);
+ StreamSet &streamSet = mStreamSetMap.editValueFor(streamSetKey);
if (streamSet.streamInfoMap.size() == 1) {
- ALOGV("StreamSet %d has no other stream available to free", streamSetId);
+ ALOGV("StreamSet %d(%d) has no other stream available to free",
+ streamSetKey.id, streamSetKey.isMultiRes);
return OK;
}
@@ -190,7 +195,8 @@
firstOtherStreamId = CAMERA3_STREAM_ID_INVALID;
}
if (firstOtherStreamId == CAMERA3_STREAM_ID_INVALID || !freeBufferIsAttached) {
- ALOGV("StreamSet %d has no buffer available to free", streamSetId);
+ ALOGV("StreamSet %d(%d) has no buffer available to free",
+ streamSetKey.id, streamSetKey.isMultiRes);
return OK;
}
@@ -237,20 +243,21 @@
}
status_t Camera3BufferManager::getBufferForStream(int streamId, int streamSetId,
- sp<GraphicBuffer>* gb, int* fenceFd, bool noFreeBufferAtConsumer) {
+ bool isMultiRes, sp<GraphicBuffer>* gb, int* fenceFd, bool noFreeBufferAtConsumer) {
ATRACE_CALL();
Mutex::Autolock l(mLock);
- ALOGV("%s: get buffer for stream %d with stream set %d", __FUNCTION__,
- streamId, streamSetId);
+ ALOGV("%s: get buffer for stream %d with stream set %d(%d)", __FUNCTION__,
+ streamId, streamSetId, isMultiRes);
- if (!checkIfStreamRegisteredLocked(streamId, streamSetId)) {
- ALOGE("%s: stream %d is not registered with stream set %d yet!!!",
- __FUNCTION__, streamId, streamSetId);
+ StreamSetKey streamSetKey = {streamSetId, isMultiRes};
+ if (!checkIfStreamRegisteredLocked(streamId, streamSetKey)) {
+ ALOGE("%s: stream %d is not registered with stream set %d(%d) yet!!!",
+ __FUNCTION__, streamId, streamSetId, isMultiRes);
return BAD_VALUE;
}
- StreamSet &streamSet = mStreamSetMap.editValueFor(streamSetId);
+ StreamSet &streamSet = mStreamSetMap.editValueFor(streamSetKey);
BufferCountMap& handOutBufferCounts = streamSet.handoutBufferCountMap;
size_t& bufferCount = handOutBufferCounts.editValueFor(streamId);
BufferCountMap& attachedBufferCounts = streamSet.attachedBufferCountMap;
@@ -272,7 +279,8 @@
bufferCount++;
return ALREADY_EXISTS;
}
- ALOGV("Stream %d set %d: Get buffer for stream: Allocate new", streamId, streamSetId);
+ ALOGV("Stream %d set %d(%d): Get buffer for stream: Allocate new",
+ streamId, streamSetId, isMultiRes);
if (mGrallocVersion < HARDWARE_DEVICE_API_VERSION(1,0)) {
const StreamInfo& info = streamSet.streamInfoMap.valueFor(streamId);
@@ -313,13 +321,13 @@
// in returnBufferForStream() if we want to free buffer more quickly.
// TODO: probably should find out all the inactive stream IDs, and free the firstly found
// buffers for them.
- res = checkAndFreeBufferOnOtherStreamsLocked(streamId, streamSetId);
+ res = checkAndFreeBufferOnOtherStreamsLocked(streamId, streamSetKey);
if (res != OK) {
return res;
}
// Since we just allocated one new buffer above, try free one more buffer from other streams
// to prevent total buffer count from growing
- res = checkAndFreeBufferOnOtherStreamsLocked(streamId, streamSetId);
+ res = checkAndFreeBufferOnOtherStreamsLocked(streamId, streamSetKey);
if (res != OK) {
return res;
}
@@ -332,7 +340,7 @@
}
status_t Camera3BufferManager::onBufferReleased(
- int streamId, int streamSetId, bool* shouldFreeBuffer) {
+ int streamId, int streamSetId, bool isMultiRes, bool* shouldFreeBuffer) {
ATRACE_CALL();
if (shouldFreeBuffer == nullptr) {
@@ -341,22 +349,24 @@
}
Mutex::Autolock l(mLock);
- ALOGV("Stream %d set %d: Buffer released", streamId, streamSetId);
+ ALOGV("Stream %d set %d(%d): Buffer released", streamId, streamSetId, isMultiRes);
*shouldFreeBuffer = false;
- if (!checkIfStreamRegisteredLocked(streamId, streamSetId)){
+ StreamSetKey streamSetKey = {streamSetId, isMultiRes};
+ if (!checkIfStreamRegisteredLocked(streamId, streamSetKey)){
ALOGV("%s: signaling buffer release for an already unregistered stream "
- "(stream %d with set id %d)", __FUNCTION__, streamId, streamSetId);
+ "(stream %d with set id %d(%d))", __FUNCTION__, streamId, streamSetId,
+ isMultiRes);
return OK;
}
if (mGrallocVersion < HARDWARE_DEVICE_API_VERSION(1,0)) {
- StreamSet& streamSet = mStreamSetMap.editValueFor(streamSetId);
+ StreamSet& streamSet = mStreamSetMap.editValueFor(streamSetKey);
BufferCountMap& handOutBufferCounts = streamSet.handoutBufferCountMap;
size_t& bufferCount = handOutBufferCounts.editValueFor(streamId);
bufferCount--;
- ALOGV("%s: Stream %d set %d: Buffer count now %zu", __FUNCTION__, streamId, streamSetId,
- bufferCount);
+ ALOGV("%s: Stream %d set %d(%d): Buffer count now %zu", __FUNCTION__, streamId,
+ streamSetId, isMultiRes, bufferCount);
size_t totalAllocatedBufferCount = 0;
size_t totalHandOutBufferCount = 0;
@@ -371,8 +381,9 @@
// BufferManager got more than enough buffers, so decrease watermark
// to trigger more buffers free operation.
streamSet.allocatedBufferWaterMark = newWaterMark;
- ALOGV("%s: Stream %d set %d: watermark--; now %zu",
- __FUNCTION__, streamId, streamSetId, streamSet.allocatedBufferWaterMark);
+ ALOGV("%s: Stream %d set %d(%d): watermark--; now %zu",
+ __FUNCTION__, streamId, streamSetId, isMultiRes,
+ streamSet.allocatedBufferWaterMark);
}
size_t attachedBufferCount = streamSet.attachedBufferCountMap.valueFor(streamId);
@@ -395,20 +406,22 @@
return OK;
}
-status_t Camera3BufferManager::onBuffersRemoved(int streamId, int streamSetId, size_t count) {
+status_t Camera3BufferManager::onBuffersRemoved(int streamId, int streamSetId,
+ bool isMultiRes, size_t count) {
ATRACE_CALL();
Mutex::Autolock l(mLock);
- ALOGV("Stream %d set %d: Buffer removed", streamId, streamSetId);
+ ALOGV("Stream %d set %d(%d): Buffer removed", streamId, streamSetId, isMultiRes);
- if (!checkIfStreamRegisteredLocked(streamId, streamSetId)){
+ StreamSetKey streamSetKey = {streamSetId, isMultiRes};
+ if (!checkIfStreamRegisteredLocked(streamId, streamSetKey)){
ALOGV("%s: signaling buffer removal for an already unregistered stream "
- "(stream %d with set id %d)", __FUNCTION__, streamId, streamSetId);
+ "(stream %d with set id %d(%d))", __FUNCTION__, streamId, streamSetId, isMultiRes);
return OK;
}
if (mGrallocVersion < HARDWARE_DEVICE_API_VERSION(1,0)) {
- StreamSet& streamSet = mStreamSetMap.editValueFor(streamSetId);
+ StreamSet& streamSet = mStreamSetMap.editValueFor(streamSetKey);
BufferCountMap& handOutBufferCounts = streamSet.handoutBufferCountMap;
size_t& totalHandoutCount = handOutBufferCounts.editValueFor(streamId);
BufferCountMap& attachedBufferCounts = streamSet.attachedBufferCountMap;
@@ -427,8 +440,9 @@
totalHandoutCount -= count;
totalAttachedCount -= count;
- ALOGV("%s: Stream %d set %d: Buffer count now %zu, attached buffer count now %zu",
- __FUNCTION__, streamId, streamSetId, totalHandoutCount, totalAttachedCount);
+ ALOGV("%s: Stream %d set %d(%d): Buffer count now %zu, attached buffer count now %zu",
+ __FUNCTION__, streamId, streamSetId, isMultiRes, totalHandoutCount,
+ totalAttachedCount);
} else {
// TODO: implement gralloc V1 support
return BAD_VALUE;
@@ -444,7 +458,8 @@
String8 lines;
lines.appendFormat(" Total stream sets: %zu\n", mStreamSetMap.size());
for (size_t i = 0; i < mStreamSetMap.size(); i++) {
- lines.appendFormat(" Stream set %d has below streams:\n", mStreamSetMap.keyAt(i));
+ lines.appendFormat(" Stream set %d(%d) has below streams:\n",
+ mStreamSetMap.keyAt(i).id, mStreamSetMap.keyAt(i).isMultiRes);
for (size_t j = 0; j < mStreamSetMap[i].streamInfoMap.size(); j++) {
lines.appendFormat(" Stream %d\n", mStreamSetMap[i].streamInfoMap[j].streamId);
}
@@ -470,11 +485,12 @@
write(fd, lines.string(), lines.size());
}
-bool Camera3BufferManager::checkIfStreamRegisteredLocked(int streamId, int streamSetId) const {
- ssize_t setIdx = mStreamSetMap.indexOfKey(streamSetId);
+bool Camera3BufferManager::checkIfStreamRegisteredLocked(int streamId,
+ StreamSetKey streamSetKey) const {
+ ssize_t setIdx = mStreamSetMap.indexOfKey(streamSetKey);
if (setIdx == NAME_NOT_FOUND) {
- ALOGV("%s: stream set %d is not registered to stream set map yet!",
- __FUNCTION__, streamSetId);
+ ALOGV("%s: stream set %d(%d) is not registered to stream set map yet!",
+ __FUNCTION__, streamSetKey.id, streamSetKey.isMultiRes);
return false;
}
@@ -486,9 +502,10 @@
size_t bufferWaterMark = mStreamSetMap[setIdx].maxAllowedBufferCount;
if (bufferWaterMark == 0 || bufferWaterMark > kMaxBufferCount) {
- ALOGW("%s: stream %d with stream set %d is not registered correctly to stream set map,"
+ ALOGW("%s: stream %d with stream set %d(%d) is not registered correctly to stream set map,"
" as the water mark (%zu) is wrong!",
- __FUNCTION__, streamId, streamSetId, bufferWaterMark);
+ __FUNCTION__, streamId, streamSetKey.id, streamSetKey.isMultiRes,
+ bufferWaterMark);
return false;
}
diff --git a/services/camera/libcameraservice/device3/Camera3BufferManager.h b/services/camera/libcameraservice/device3/Camera3BufferManager.h
index f0de1c1..64aaa230 100644
--- a/services/camera/libcameraservice/device3/Camera3BufferManager.h
+++ b/services/camera/libcameraservice/device3/Camera3BufferManager.h
@@ -99,7 +99,7 @@
* combination doesn't match what was registered, or this stream wasn't registered
* to this buffer manager before.
*/
- status_t unregisterStream(int streamId, int streamSetId);
+ status_t unregisterStream(int streamId, int streamSetId, bool isMultiRes);
/**
* This method obtains a buffer for a stream from this buffer manager.
@@ -127,8 +127,8 @@
* NO_MEMORY: Unable to allocate a buffer for this stream at this time.
*/
status_t getBufferForStream(
- int streamId, int streamSetId, sp<GraphicBuffer>* gb, int* fenceFd,
- bool noFreeBufferAtConsumer = false);
+ int streamId, int streamSetId, bool isMultiRes, sp<GraphicBuffer>* gb,
+ int* fenceFd, bool noFreeBufferAtConsumer = false);
/**
* This method notifies the manager that a buffer has been released by the consumer.
@@ -153,7 +153,8 @@
* combination doesn't match what was registered, or this stream wasn't registered
* to this buffer manager before, or shouldFreeBuffer is null/
*/
- status_t onBufferReleased(int streamId, int streamSetId, /*out*/bool* shouldFreeBuffer);
+ status_t onBufferReleased(int streamId, int streamSetId, bool isMultiRes,
+ /*out*/bool* shouldFreeBuffer);
/**
* This method notifies the manager that certain buffers has been removed from the
@@ -171,13 +172,13 @@
* to this buffer manager before, or the removed buffer count is larger than
* current total handoutCount or attachedCount.
*/
- status_t onBuffersRemoved(int streamId, int streamSetId, size_t count);
+ status_t onBuffersRemoved(int streamId, int streamSetId, bool isMultiRes, size_t count);
/**
* This method notifiers the manager that a buffer is freed from the buffer queue, usually
* because onBufferReleased signals the caller to free a buffer via the shouldFreeBuffer flag.
*/
- void notifyBufferRemoved(int streamId, int streamSetId);
+ void notifyBufferRemoved(int streamId, int streamSetId, bool isMultiRes);
/**
* Dump the buffer manager statistics.
@@ -292,8 +293,20 @@
/**
* Stream set map managed by this buffer manager.
*/
- typedef int StreamSetId;
- KeyedVector<StreamSetId, StreamSet> mStreamSetMap;
+ struct StreamSetKey {
+ // The stream set ID
+ int id;
+ // Whether this stream set is for multi-resolution output streams. It's
+ // valid for 2 stream sets to have the same stream set ID if: one is for
+ // multi-resolution output stream, and the other one is not.
+ bool isMultiRes;
+
+ inline bool operator<(const StreamSetKey& other) const {
+ return (isMultiRes < other.isMultiRes) ||
+ ((isMultiRes == other.isMultiRes) && (id < other.id));
+ }
+ };
+ KeyedVector<StreamSetKey, StreamSet> mStreamSetMap;
KeyedVector<StreamId, wp<Camera3OutputStream>> mStreamMap;
// TODO: There is no easy way to query the Gralloc version in this code yet, we have different
@@ -304,13 +317,13 @@
* Check if this stream was successfully registered already. This method needs to be called with
* mLock held.
*/
- bool checkIfStreamRegisteredLocked(int streamId, int streamSetId) const;
+ bool checkIfStreamRegisteredLocked(int streamId, StreamSetKey streamSetKey) const;
/**
* Check if other streams in the stream set has extra buffer available to be freed, and
* free one if so.
*/
- status_t checkAndFreeBufferOnOtherStreamsLocked(int streamId, int streamSetId);
+ status_t checkAndFreeBufferOnOtherStreamsLocked(int streamId, StreamSetKey streamSetKey);
};
} // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index ea6d59e..d05a2e1 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -49,6 +49,7 @@
#include <utils/Timers.h>
#include <cutils/properties.h>
+#include <android/hardware/camera/device/3.7/ICameraInjectionSession.h>
#include <android/hardware/camera2/ICameraDeviceUser.h>
#include "utils/CameraTraces.h"
@@ -56,11 +57,13 @@
#include "device3/Camera3Device.h"
#include "device3/Camera3OutputStream.h"
#include "device3/Camera3InputStream.h"
-#include "device3/Camera3DummyStream.h"
+#include "device3/Camera3FakeStream.h"
#include "device3/Camera3SharedOutputStream.h"
#include "CameraService.h"
#include "utils/CameraThreadState.h"
+#include "utils/SessionConfigurationUtils.h"
#include "utils/TraceHFR.h"
+#include "utils/CameraServiceProxyWrapper.h"
#include <algorithm>
#include <tuple>
@@ -68,6 +71,7 @@
using namespace android::camera3;
using namespace android::hardware::camera;
using namespace android::hardware::camera::device::V3_2;
+using android::hardware::camera::metadata::V3_6::CameraMetadataEnumAndroidSensorPixelMode;
namespace android {
@@ -270,7 +274,7 @@
}
/** Register in-flight map to the status tracker */
- mInFlightStatusId = mStatusTracker->addComponent();
+ mInFlightStatusId = mStatusTracker->addComponent("InflightRequests");
if (mUseHalBufManager) {
res = mRequestBufferSM.initialize(mStatusTracker);
@@ -309,9 +313,10 @@
internalUpdateStatusLocked(STATUS_UNCONFIGURED);
mNextStreamId = 0;
- mDummyStreamId = NO_STREAM;
+ mFakeStreamId = NO_STREAM;
mNeedConfig = true;
mPauseStateNotify = false;
+ mIsInputStreamMultiResolution = false;
// Measure the clock domain offset between camera and video/hw_composer
camera_metadata_entry timestampSource =
@@ -329,17 +334,6 @@
mUsePartialResult = (mNumPartialResults > 1);
}
- camera_metadata_entry configs =
- mDeviceInfo.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
- for (uint32_t i = 0; i < configs.count; i += 4) {
- if (configs.data.i32[i] == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED &&
- configs.data.i32[i + 3] ==
- ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT) {
- mSupportedOpaqueInputSizes.add(Size(configs.data.i32[i + 1],
- configs.data.i32[i + 2]));
- }
- }
-
bool usePrecorrectArray = DistortionMapper::isDistortionSupported(mDeviceInfo);
if (usePrecorrectArray) {
res = mDistortionMappers[mId.c_str()].setupStaticInfo(mDeviceInfo);
@@ -356,6 +350,17 @@
mRotateAndCropMappers.emplace(mId.c_str(), &mDeviceInfo);
}
+ camera_metadata_entry_t availableTestPatternModes = mDeviceInfo.find(
+ ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES);
+ for (size_t i = 0; i < availableTestPatternModes.count; i++) {
+ if (availableTestPatternModes.data.i32[i] == ANDROID_SENSOR_TEST_PATTERN_MODE_SOLID_COLOR) {
+ mSupportCameraMute = true;
+ break;
+ }
+ }
+
+ mInjectionMethods = new Camera3DeviceInjectionMethods(this);
+
return OK;
}
@@ -429,6 +434,10 @@
mStatusTracker->join();
}
+ if (mInjectionMethods->isInjecting()) {
+ mInjectionMethods->stopInjection();
+ }
+
HalInterface* interface;
{
Mutex::Autolock l(mLock);
@@ -482,18 +491,23 @@
return gotLock;
}
-Camera3Device::Size Camera3Device::getMaxJpegResolution() const {
+camera3::Size Camera3Device::getMaxJpegResolution() const {
int32_t maxJpegWidth = 0, maxJpegHeight = 0;
const int STREAM_CONFIGURATION_SIZE = 4;
const int STREAM_FORMAT_OFFSET = 0;
const int STREAM_WIDTH_OFFSET = 1;
const int STREAM_HEIGHT_OFFSET = 2;
const int STREAM_IS_INPUT_OFFSET = 3;
+ bool isHighResolutionSensor =
+ camera3::SessionConfigurationUtils::isUltraHighResolutionSensor(mDeviceInfo);
+ int32_t scalerSizesTag = isHighResolutionSensor ?
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION :
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS;
camera_metadata_ro_entry_t availableStreamConfigs =
- mDeviceInfo.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
+ mDeviceInfo.find(scalerSizesTag);
if (availableStreamConfigs.count == 0 ||
availableStreamConfigs.count % STREAM_CONFIGURATION_SIZE != 0) {
- return Size(0, 0);
+ return camera3::Size(0, 0);
}
// Get max jpeg size (area-wise).
@@ -510,7 +524,7 @@
}
}
- return Size(maxJpegWidth, maxJpegHeight);
+ return camera3::Size(maxJpegWidth, maxJpegHeight);
}
nsecs_t Camera3Device::getMonoToBoottimeOffset() {
@@ -546,15 +560,15 @@
return usage;
}
-StreamRotation Camera3Device::mapToStreamRotation(camera3_stream_rotation_t rotation) {
+StreamRotation Camera3Device::mapToStreamRotation(camera_stream_rotation_t rotation) {
switch (rotation) {
- case CAMERA3_STREAM_ROTATION_0:
+ case CAMERA_STREAM_ROTATION_0:
return StreamRotation::ROTATION_0;
- case CAMERA3_STREAM_ROTATION_90:
+ case CAMERA_STREAM_ROTATION_90:
return StreamRotation::ROTATION_90;
- case CAMERA3_STREAM_ROTATION_180:
+ case CAMERA_STREAM_ROTATION_180:
return StreamRotation::ROTATION_180;
- case CAMERA3_STREAM_ROTATION_270:
+ case CAMERA_STREAM_ROTATION_270:
return StreamRotation::ROTATION_270;
}
ALOGE("%s: Unknown stream rotation %d", __FUNCTION__, rotation);
@@ -562,14 +576,14 @@
}
status_t Camera3Device::mapToStreamConfigurationMode(
- camera3_stream_configuration_mode_t operationMode, StreamConfigurationMode *mode) {
+ camera_stream_configuration_mode_t operationMode, StreamConfigurationMode *mode) {
if (mode == nullptr) return BAD_VALUE;
- if (operationMode < CAMERA3_VENDOR_STREAM_CONFIGURATION_MODE_START) {
+ if (operationMode < CAMERA_VENDOR_STREAM_CONFIGURATION_MODE_START) {
switch(operationMode) {
- case CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE:
+ case CAMERA_STREAM_CONFIGURATION_NORMAL_MODE:
*mode = StreamConfigurationMode::NORMAL_MODE;
break;
- case CAMERA3_STREAM_CONFIGURATION_CONSTRAINED_HIGH_SPEED_MODE:
+ case CAMERA_STREAM_CONFIGURATION_CONSTRAINED_HIGH_SPEED_MODE:
*mode = StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE;
break;
default:
@@ -604,7 +618,7 @@
ssize_t Camera3Device::getJpegBufferSize(uint32_t width, uint32_t height) const {
// Get max jpeg size (area-wise).
- Size maxJpegResolution = getMaxJpegResolution();
+ camera3::Size maxJpegResolution = getMaxJpegResolution();
if (maxJpegResolution.width == 0) {
ALOGE("%s: Camera %s: Can't find valid available jpeg sizes in static metadata!",
__FUNCTION__, mId.string());
@@ -628,6 +642,8 @@
ssize_t jpegBufferSize = scaleFactor * (maxJpegBufferSize - kMinJpegBufferSize) +
kMinJpegBufferSize;
if (jpegBufferSize > maxJpegBufferSize) {
+ ALOGI("%s: jpeg buffer size calculated is > maxJpeg bufferSize(%zd), clamping",
+ __FUNCTION__, maxJpegBufferSize);
jpegBufferSize = maxJpegBufferSize;
}
@@ -647,13 +663,17 @@
return maxBytesForPointCloud;
}
-ssize_t Camera3Device::getRawOpaqueBufferSize(int32_t width, int32_t height) const {
+ssize_t Camera3Device::getRawOpaqueBufferSize(int32_t width, int32_t height,
+ bool maxResolution) const {
const int PER_CONFIGURATION_SIZE = 3;
const int WIDTH_OFFSET = 0;
const int HEIGHT_OFFSET = 1;
const int SIZE_OFFSET = 2;
camera_metadata_ro_entry rawOpaqueSizes =
- mDeviceInfo.find(ANDROID_SENSOR_OPAQUE_RAW_SIZE);
+ mDeviceInfo.find(
+ camera3::SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_SENSOR_OPAQUE_RAW_SIZE,
+ maxResolution));
size_t count = rawOpaqueSizes.count;
if (count == 0 || (count % PER_CONFIGURATION_SIZE)) {
ALOGE("%s: Camera %s: bad opaque RAW size static metadata length(%zu)!",
@@ -778,7 +798,7 @@
}
if (dumpTemplates) {
- const char *templateNames[CAMERA3_TEMPLATE_COUNT] = {
+ const char *templateNames[CAMERA_TEMPLATE_COUNT] = {
"TEMPLATE_PREVIEW",
"TEMPLATE_STILL_CAPTURE",
"TEMPLATE_VIDEO_RECORD",
@@ -787,10 +807,10 @@
"TEMPLATE_MANUAL",
};
- for (int i = 1; i < CAMERA3_TEMPLATE_COUNT; i++) {
+ for (int i = 1; i < CAMERA_TEMPLATE_COUNT; i++) {
camera_metadata_t *templateRequest = nullptr;
mInterface->constructDefaultRequestSettings(
- (camera3_request_template_t) i, &templateRequest);
+ (camera_request_template_t) i, &templateRequest);
lines = String8::format(" HAL Request %s:\n", templateNames[i-1]);
if (templateRequest == nullptr) {
lines.append(" Not supported\n");
@@ -867,7 +887,7 @@
status_t Camera3Device::convertMetadataListToRequestListLocked(
const List<const PhysicalCameraSettingsList> &metadataList,
const std::list<const SurfaceMap> &surfaceMaps,
- bool repeating,
+ bool repeating, nsecs_t requestTimeNs,
RequestList *requestList) {
if (requestList == NULL) {
CLOGE("requestList cannot be NULL.");
@@ -886,6 +906,7 @@
}
newRequest->mRepeating = repeating;
+ newRequest->mRequestTimeNs = requestTimeNs;
// Setup burst Id and request Id
newRequest->mResultExtras.burstId = burstId++;
@@ -911,6 +932,7 @@
for (auto& outputStream : (*firstRequest)->mOutputStreams) {
if (outputStream->isVideoStream()) {
(*firstRequest)->mBatchSize = requestList->size();
+ outputStream->setBatchSize(requestList->size());
break;
}
}
@@ -953,6 +975,8 @@
/*out*/
int64_t *lastFrameNumber) {
ATRACE_CALL();
+ nsecs_t requestTimeNs = systemTime();
+
Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
@@ -965,7 +989,7 @@
RequestList requestList;
res = convertMetadataListToRequestListLocked(requests, surfaceMaps,
- repeating, /*out*/&requestList);
+ repeating, requestTimeNs, /*out*/&requestList);
if (res != OK) {
// error logged by previous call
return res;
@@ -997,7 +1021,7 @@
const hardware::hidl_vec<hardware::camera::device::V3_5::BufferRequest>& bufReqs,
requestStreamBuffers_cb _hidl_cb) {
RequestBufferStates states {
- mId, mRequestBufferInterfaceLock, mUseHalBufManager, mOutputStreams,
+ mId, mRequestBufferInterfaceLock, mUseHalBufManager, mOutputStreams, mSessionStatsBuilder,
*this, *mInterface, *this};
camera3::requestStreamBuffers(states, bufReqs, _hidl_cb);
return hardware::Void();
@@ -1006,7 +1030,7 @@
hardware::Return<void> Camera3Device::returnStreamBuffers(
const hardware::hidl_vec<hardware::camera::device::V3_2::StreamBuffer>& buffers) {
ReturnBufferStates states {
- mId, mUseHalBufManager, mOutputStreams, *mInterface};
+ mId, mUseHalBufManager, mOutputStreams, mSessionStatsBuilder, *mInterface};
camera3::returnStreamBuffers(states, buffers);
return hardware::Void();
}
@@ -1054,7 +1078,8 @@
mUseHalBufManager, mUsePartialResult, mNeedFixupMonochromeTags,
mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
mResultMetadataQueue, mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
- mTagMonitor, mInputStream, mOutputStreams, listener, *this, *this, *mInterface
+ mTagMonitor, mInputStream, mOutputStreams, mSessionStatsBuilder, listener, *this, *this,
+ *mInterface
};
for (const auto& result : results) {
@@ -1112,7 +1137,8 @@
mUseHalBufManager, mUsePartialResult, mNeedFixupMonochromeTags,
mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
mResultMetadataQueue, mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
- mTagMonitor, mInputStream, mOutputStreams, listener, *this, *this, *mInterface
+ mTagMonitor, mInputStream, mOutputStreams, mSessionStatsBuilder, listener, *this, *this,
+ *mInterface
};
for (const auto& result : results) {
@@ -1152,7 +1178,8 @@
mUseHalBufManager, mUsePartialResult, mNeedFixupMonochromeTags,
mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
mResultMetadataQueue, mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
- mTagMonitor, mInputStream, mOutputStreams, listener, *this, *this, *mInterface
+ mTagMonitor, mInputStream, mOutputStreams, mSessionStatsBuilder, listener, *this, *this,
+ *mInterface
};
for (const auto& msg : msgs) {
camera3::notify(states, msg);
@@ -1196,7 +1223,7 @@
// This point should only be reached via API1 (API2 must explicitly call configureStreams)
// so unilaterally select normal operating mode.
res = filterParamsAndConfigureLocked(request.begin()->metadata,
- CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE);
+ CAMERA_STREAM_CONFIGURATION_NORMAL_MODE);
// Stream configuration failed. Client might try other configuraitons.
if (res != OK) {
CLOGE("Can't set up streams: %s (%d)", strerror(-res), res);
@@ -1246,7 +1273,7 @@
}
status_t Camera3Device::createInputStream(
- uint32_t width, uint32_t height, int format, int *id) {
+ uint32_t width, uint32_t height, int format, bool isMultiResolution, int *id) {
ATRACE_CALL();
Mutex::Autolock il(mInterfaceLock);
nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
@@ -1293,6 +1320,7 @@
newStream->setStatusTracker(mStatusTracker);
mInputStream = newStream;
+ mIsInputStreamMultiResolution = isMultiResolution;
*id = mNextStreamId++;
@@ -1315,9 +1343,11 @@
status_t Camera3Device::createStream(sp<Surface> consumer,
uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
+ android_dataspace dataSpace, camera_stream_rotation_t rotation, int *id,
const String8& physicalCameraId,
- std::vector<int> *surfaceIds, int streamSetId, bool isShared, uint64_t consumerUsage) {
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ std::vector<int> *surfaceIds, int streamSetId, bool isShared, bool isMultiResolution,
+ uint64_t consumerUsage) {
ATRACE_CALL();
if (consumer == nullptr) {
@@ -1329,24 +1359,37 @@
consumers.push_back(consumer);
return createStream(consumers, /*hasDeferredConsumer*/ false, width, height,
- format, dataSpace, rotation, id, physicalCameraId, surfaceIds, streamSetId,
- isShared, consumerUsage);
+ format, dataSpace, rotation, id, physicalCameraId, sensorPixelModesUsed, surfaceIds,
+ streamSetId, isShared, isMultiResolution, consumerUsage);
+}
+
+static bool isRawFormat(int format) {
+ switch (format) {
+ case HAL_PIXEL_FORMAT_RAW16:
+ case HAL_PIXEL_FORMAT_RAW12:
+ case HAL_PIXEL_FORMAT_RAW10:
+ case HAL_PIXEL_FORMAT_RAW_OPAQUE:
+ return true;
+ default:
+ return false;
+ }
}
status_t Camera3Device::createStream(const std::vector<sp<Surface>>& consumers,
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
- const String8& physicalCameraId,
- std::vector<int> *surfaceIds, int streamSetId, bool isShared, uint64_t consumerUsage) {
+ android_dataspace dataSpace, camera_stream_rotation_t rotation, int *id,
+ const String8& physicalCameraId, const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ std::vector<int> *surfaceIds, int streamSetId, bool isShared, bool isMultiResolution,
+ uint64_t consumerUsage) {
ATRACE_CALL();
Mutex::Autolock il(mInterfaceLock);
nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
Mutex::Autolock l(mLock);
ALOGV("Camera %s: Creating new stream %d: %d x %d, format %d, dataspace %d rotation %d"
- " consumer usage %" PRIu64 ", isShared %d, physicalCameraId %s", mId.string(),
- mNextStreamId, width, height, format, dataSpace, rotation, consumerUsage, isShared,
- physicalCameraId.string());
+ " consumer usage %" PRIu64 ", isShared %d, physicalCameraId %s, isMultiResolution %d",
+ mId.string(), mNextStreamId, width, height, format, dataSpace, rotation,
+ consumerUsage, isShared, physicalCameraId.string(), isMultiResolution);
status_t res;
bool wasActive = false;
@@ -1389,6 +1432,12 @@
return BAD_VALUE;
}
+ if (isRawFormat(format) && sensorPixelModesUsed.size() > 1) {
+ // We can't use one stream with a raw format in both sensor pixel modes since its going to
+ // be found in only one sensor pixel mode.
+ ALOGE("%s: RAW opaque stream cannot be used with > 1 sensor pixel modes", __FUNCTION__);
+ return BAD_VALUE;
+ }
if (format == HAL_PIXEL_FORMAT_BLOB) {
ssize_t blobBufferSize;
if (dataSpace == HAL_DATASPACE_DEPTH) {
@@ -1408,29 +1457,36 @@
}
newStream = new Camera3OutputStream(mNextStreamId, consumers[0],
width, height, blobBufferSize, format, dataSpace, rotation,
- mTimestampOffset, physicalCameraId, streamSetId);
+ mTimestampOffset, physicalCameraId, sensorPixelModesUsed, streamSetId,
+ isMultiResolution);
} else if (format == HAL_PIXEL_FORMAT_RAW_OPAQUE) {
- ssize_t rawOpaqueBufferSize = getRawOpaqueBufferSize(width, height);
+ bool maxResolution =
+ sensorPixelModesUsed.find(ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) !=
+ sensorPixelModesUsed.end();
+ ssize_t rawOpaqueBufferSize = getRawOpaqueBufferSize(width, height, maxResolution);
if (rawOpaqueBufferSize <= 0) {
SET_ERR_L("Invalid RAW opaque buffer size %zd", rawOpaqueBufferSize);
return BAD_VALUE;
}
newStream = new Camera3OutputStream(mNextStreamId, consumers[0],
width, height, rawOpaqueBufferSize, format, dataSpace, rotation,
- mTimestampOffset, physicalCameraId, streamSetId);
+ mTimestampOffset, physicalCameraId, sensorPixelModesUsed, streamSetId,
+ isMultiResolution);
} else if (isShared) {
newStream = new Camera3SharedOutputStream(mNextStreamId, consumers,
width, height, format, consumerUsage, dataSpace, rotation,
- mTimestampOffset, physicalCameraId, streamSetId,
+ mTimestampOffset, physicalCameraId, sensorPixelModesUsed, streamSetId,
mUseHalBufManager);
} else if (consumers.size() == 0 && hasDeferredConsumer) {
newStream = new Camera3OutputStream(mNextStreamId,
width, height, format, consumerUsage, dataSpace, rotation,
- mTimestampOffset, physicalCameraId, streamSetId);
+ mTimestampOffset, physicalCameraId, sensorPixelModesUsed, streamSetId,
+ isMultiResolution);
} else {
newStream = new Camera3OutputStream(mNextStreamId, consumers[0],
width, height, format, dataSpace, rotation,
- mTimestampOffset, physicalCameraId, streamSetId);
+ mTimestampOffset, physicalCameraId, sensorPixelModesUsed, streamSetId,
+ isMultiResolution);
}
size_t consumerCount = consumers.size();
@@ -1449,12 +1505,16 @@
newStream->setBufferManager(mBufferManager);
+ newStream->setImageDumpMask(mImageDumpMask);
+
res = mOutputStreams.add(mNextStreamId, newStream);
if (res < 0) {
SET_ERR_L("Can't add new stream to set: %s (%d)", strerror(-res), res);
return res;
}
+ mSessionStatsBuilder.addStream(mNextStreamId);
+
*id = mNextStreamId++;
mNeedConfig = true;
@@ -1578,6 +1638,7 @@
CLOGE("Stream %d does not exist", id);
return BAD_VALUE;
}
+ mSessionStatsBuilder.removeStream(id);
}
// Delete output stream or the output part of a bi-directional stream.
@@ -1609,7 +1670,7 @@
// speculative configuration using the values from the last cached
// default request.
if (sessionParams.isEmpty() &&
- ((mLastTemplateId > 0) && (mLastTemplateId < CAMERA3_TEMPLATE_COUNT)) &&
+ ((mLastTemplateId > 0) && (mLastTemplateId < CAMERA_TEMPLATE_COUNT)) &&
(!mRequestTemplateCache[mLastTemplateId].isEmpty())) {
ALOGV("%s: Speculative session param configuration with template id: %d", __func__,
mLastTemplateId);
@@ -1659,12 +1720,12 @@
return mInputStream->getInputBufferProducer(producer);
}
-status_t Camera3Device::createDefaultRequest(int templateId,
+status_t Camera3Device::createDefaultRequest(camera_request_template_t templateId,
CameraMetadata *request) {
ATRACE_CALL();
ALOGV("%s: for template %d", __FUNCTION__, templateId);
- if (templateId <= 0 || templateId >= CAMERA3_TEMPLATE_COUNT) {
+ if (templateId <= 0 || templateId >= CAMERA_TEMPLATE_COUNT) {
android_errorWriteWithInfoLog(CameraService::SN_EVENT_LOG_ID, "26866110",
CameraThreadState::getCallingUid(), nullptr, 0);
return BAD_VALUE;
@@ -1700,7 +1761,7 @@
camera_metadata_t *rawRequest;
status_t res = mInterface->constructDefaultRequestSettings(
- (camera3_request_template_t) templateId, &rawRequest);
+ (camera_request_template_t) templateId, &rawRequest);
{
Mutex::Autolock l(mLock);
@@ -1768,13 +1829,13 @@
maxExpectedDuration);
status_t res = waitUntilStateThenRelock(/*active*/ false, maxExpectedDuration);
if (res != OK) {
+ mStatusTracker->dumpActiveComponents();
SET_ERR_L("Error waiting for HAL to drain: %s (%d)", strerror(-res),
res);
}
return res;
}
-
void Camera3Device::internalUpdateStatusLocked(Status status) {
mStatus = status;
mRecentStatusUpdates.add(mStatus);
@@ -2009,6 +2070,9 @@
}
mRequestThread->clear(/*out*/frameNumber);
+
+ // Stop session and stream counter
+ mSessionStatsBuilder.stopCounter();
}
return mRequestThread->flush();
@@ -2086,6 +2150,9 @@
void Camera3Device::notifyStatus(bool idle) {
ATRACE_CALL();
+ std::vector<int> streamIds;
+ std::vector<hardware::CameraStreamStats> streamStats;
+
{
// Need mLock to safely update state and synchronize to current
// state of methods in flight.
@@ -2103,6 +2170,24 @@
// Skip notifying listener if we're doing some user-transparent
// state changes
if (mPauseStateNotify) return;
+
+ // Populate stream statistics in case of Idle
+ if (idle) {
+ for (size_t i = 0; i < mOutputStreams.size(); i++) {
+ auto stream = mOutputStreams[i];
+ if (stream.get() == nullptr) continue;
+ streamIds.push_back(stream->getId());
+ Camera3Stream* camera3Stream = Camera3Stream::cast(stream->asHalStream());
+ int64_t usage = 0LL;
+ if (camera3Stream != nullptr) {
+ usage = camera3Stream->getUsage();
+ }
+ streamStats.emplace_back(stream->getWidth(), stream->getHeight(),
+ stream->getFormat(), stream->getDataSpace(), usage,
+ stream->getMaxHalBuffers(),
+ stream->getMaxTotalBuffers() - stream->getMaxHalBuffers());
+ }
+ }
}
sp<NotificationListener> listener;
@@ -2110,8 +2195,40 @@
std::lock_guard<std::mutex> l(mOutputLock);
listener = mListener.promote();
}
- if (idle && listener != NULL) {
- listener->notifyIdle();
+ status_t res = OK;
+ if (listener != nullptr) {
+ if (idle) {
+ // Get session stats from the builder, and notify the listener.
+ int64_t requestCount, resultErrorCount;
+ bool deviceError;
+ std::map<int, StreamStats> streamStatsMap;
+ mSessionStatsBuilder.buildAndReset(&requestCount, &resultErrorCount,
+ &deviceError, &streamStatsMap);
+ for (size_t i = 0; i < streamIds.size(); i++) {
+ int streamId = streamIds[i];
+ auto stats = streamStatsMap.find(streamId);
+ if (stats != streamStatsMap.end()) {
+ streamStats[i].mRequestCount = stats->second.mRequestedFrameCount;
+ streamStats[i].mErrorCount = stats->second.mDroppedFrameCount;
+ streamStats[i].mStartLatencyMs = stats->second.mStartLatencyMs;
+ streamStats[i].mHistogramType =
+ hardware::CameraStreamStats::HISTOGRAM_TYPE_CAPTURE_LATENCY;
+ streamStats[i].mHistogramBins.assign(
+ stats->second.mCaptureLatencyBins.begin(),
+ stats->second.mCaptureLatencyBins.end());
+ streamStats[i].mHistogramCounts.assign(
+ stats->second.mCaptureLatencyHistogram.begin(),
+ stats->second.mCaptureLatencyHistogram.end());
+ }
+ }
+ listener->notifyIdle(requestCount, resultErrorCount, deviceError, streamStats);
+ } else {
+ res = listener->notifyActive();
+ }
+ }
+ if (res != OK) {
+ SET_ERR("Camera access permission lost mid-operation: %s (%d)",
+ strerror(-res), res);
}
}
@@ -2221,6 +2338,12 @@
ALOGE("%s: Stream %d is not found.", __FUNCTION__, streamId);
return BAD_VALUE;
}
+
+ if (dropping) {
+ mSessionStatsBuilder.stopCounter(streamId);
+ } else {
+ mSessionStatsBuilder.startCounter(streamId);
+ }
return stream->dropBuffers(dropping);
}
@@ -2321,6 +2444,26 @@
newRequest->mZoomRatioIs1x = false;
}
+ if (mSupportCameraMute) {
+ auto testPatternModeEntry =
+ newRequest->mSettingsList.begin()->metadata.find(ANDROID_SENSOR_TEST_PATTERN_MODE);
+ newRequest->mOriginalTestPatternMode = testPatternModeEntry.count > 0 ?
+ testPatternModeEntry.data.i32[0] :
+ ANDROID_SENSOR_TEST_PATTERN_MODE_OFF;
+
+ auto testPatternDataEntry =
+ newRequest->mSettingsList.begin()->metadata.find(ANDROID_SENSOR_TEST_PATTERN_DATA);
+ if (testPatternDataEntry.count > 0) {
+ memcpy(newRequest->mOriginalTestPatternData, testPatternModeEntry.data.i32,
+ sizeof(newRequest->mOriginalTestPatternData));
+ } else {
+ newRequest->mOriginalTestPatternData[0] = 0;
+ newRequest->mOriginalTestPatternData[1] = 0;
+ newRequest->mOriginalTestPatternData[2] = 0;
+ newRequest->mOriginalTestPatternData[3] = 0;
+ }
+ }
+
return newRequest;
}
@@ -2375,6 +2518,8 @@
ATRACE_CALL();
bool ret = false;
+ nsecs_t startTime = systemTime();
+
Mutex::Autolock il(mInterfaceLock);
nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
@@ -2422,6 +2567,9 @@
ALOGE("%s: Failed to pause streaming: %d", __FUNCTION__, rc);
}
+ CameraServiceProxyWrapper::logStreamConfigured(mId, mOperatingMode, true /*internalReconfig*/,
+ ns2ms(systemTime() - startTime));
+
if (markClientActive) {
mStatusTracker->markComponentActive(clientStatusId);
}
@@ -2458,15 +2606,16 @@
// any pending requests.
if (mInputStream != NULL && notifyRequestThread) {
while (true) {
- camera3_stream_buffer_t inputBuffer;
+ camera_stream_buffer_t inputBuffer;
+ camera3::Size inputBufferSize;
status_t res = mInputStream->getInputBuffer(&inputBuffer,
- /*respectHalLimit*/ false);
+ &inputBufferSize, /*respectHalLimit*/ false);
if (res != OK) {
// Exhausted acquiring all input buffers.
break;
}
- inputBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
+ inputBuffer.status = CAMERA_BUFFER_STATUS_ERROR;
res = mInputStream->returnInputBuffer(inputBuffer);
if (res != OK) {
ALOGE("%s: %d: couldn't return input buffer while clearing input queue: "
@@ -2481,12 +2630,12 @@
}
// Workaround for device HALv3.2 or older spec bug - zero streams requires
- // adding a dummy stream instead.
+ // adding a fake stream instead.
// TODO: Bug: 17321404 for fixing the HAL spec and removing this workaround.
if (mOutputStreams.size() == 0) {
- addDummyStreamLocked();
+ addFakeStreamLocked();
} else {
- tryRemoveDummyStreamLocked();
+ tryRemoveFakeStreamLocked();
}
// Start configuring the streams
@@ -2494,17 +2643,18 @@
mPreparerThread->pause();
- camera3_stream_configuration config;
+ camera_stream_configuration config;
config.operation_mode = mOperatingMode;
config.num_streams = (mInputStream != NULL) + mOutputStreams.size();
+ config.input_is_multi_resolution = false;
- Vector<camera3_stream_t*> streams;
+ Vector<camera3::camera_stream_t*> streams;
streams.setCapacity(config.num_streams);
std::vector<uint32_t> bufferSizes(config.num_streams, 0);
if (mInputStream != NULL) {
- camera3_stream_t *inputStream;
+ camera3::camera_stream_t *inputStream;
inputStream = mInputStream->startConfiguration();
if (inputStream == NULL) {
CLOGE("Can't start input stream configuration");
@@ -2512,8 +2662,11 @@
return INVALID_OPERATION;
}
streams.add(inputStream);
+
+ config.input_is_multi_resolution = mIsInputStreamMultiResolution;
}
+ mGroupIdPhysicalCameraMap.clear();
for (size_t i = 0; i < mOutputStreams.size(); i++) {
// Don't configure bidi streams twice, nor add them twice to the list
@@ -2524,7 +2677,7 @@
continue;
}
- camera3_stream_t *outputStream;
+ camera3::camera_stream_t *outputStream;
outputStream = mOutputStreams[i]->startConfiguration();
if (outputStream == NULL) {
CLOGE("Can't start output stream configuration");
@@ -2547,6 +2700,12 @@
__FUNCTION__, outputStream->data_space);
}
}
+
+ if (mOutputStreams[i]->isMultiResolution()) {
+ int32_t streamGroupId = mOutputStreams[i]->getHalStreamGroupId();
+ const String8& physicalCameraId = mOutputStreams[i]->getPhysicalCameraId();
+ mGroupIdPhysicalCameraMap[streamGroupId].insert(physicalCameraId);
+ }
}
config.streams = streams.editArray();
@@ -2617,7 +2776,8 @@
// Request thread needs to know to avoid using repeat-last-settings protocol
// across configure_streams() calls
if (notifyRequestThread) {
- mRequestThread->configurationComplete(mIsConstrainedHighSpeedConfiguration, sessionParams);
+ mRequestThread->configurationComplete(mIsConstrainedHighSpeedConfiguration,
+ sessionParams, mGroupIdPhysicalCameraMap);
}
char value[PROPERTY_VALUE_MAX];
@@ -2648,7 +2808,7 @@
mNeedConfig = false;
- internalUpdateStatusLocked((mDummyStreamId == NO_STREAM) ?
+ internalUpdateStatusLocked((mFakeStreamId == NO_STREAM) ?
STATUS_CONFIGURED : STATUS_UNCONFIGURED);
ALOGV("%s: Camera %s: Stream configuration complete", __FUNCTION__, mId.string());
@@ -2662,69 +2822,82 @@
return rc;
}
- if (mDummyStreamId == NO_STREAM) {
+ if (mFakeStreamId == NO_STREAM) {
mRequestBufferSM.onStreamsConfigured();
}
+ // Since the streams configuration of the injection camera is based on the internal camera, we
+ // must wait until the internal camera configure streams before calling injectCamera() to
+ // configure the injection streams.
+ if (mInjectionMethods->isInjecting()) {
+ ALOGV("%s: Injection camera %s: Start to configure streams.",
+ __FUNCTION__, mInjectionMethods->getInjectedCamId().string());
+ res = mInjectionMethods->injectCamera(config, bufferSizes);
+ if (res != OK) {
+ ALOGE("Can't finish inject camera process!");
+ return res;
+ }
+ }
+
return OK;
}
-status_t Camera3Device::addDummyStreamLocked() {
+status_t Camera3Device::addFakeStreamLocked() {
ATRACE_CALL();
status_t res;
- if (mDummyStreamId != NO_STREAM) {
- // Should never be adding a second dummy stream when one is already
+ if (mFakeStreamId != NO_STREAM) {
+ // Should never be adding a second fake stream when one is already
// active
- SET_ERR_L("%s: Camera %s: A dummy stream already exists!",
+ SET_ERR_L("%s: Camera %s: A fake stream already exists!",
__FUNCTION__, mId.string());
return INVALID_OPERATION;
}
- ALOGV("%s: Camera %s: Adding a dummy stream", __FUNCTION__, mId.string());
+ ALOGV("%s: Camera %s: Adding a fake stream", __FUNCTION__, mId.string());
- sp<Camera3OutputStreamInterface> dummyStream =
- new Camera3DummyStream(mNextStreamId);
+ sp<Camera3OutputStreamInterface> fakeStream =
+ new Camera3FakeStream(mNextStreamId);
- res = mOutputStreams.add(mNextStreamId, dummyStream);
+ res = mOutputStreams.add(mNextStreamId, fakeStream);
if (res < 0) {
- SET_ERR_L("Can't add dummy stream to set: %s (%d)", strerror(-res), res);
+ SET_ERR_L("Can't add fake stream to set: %s (%d)", strerror(-res), res);
return res;
}
- mDummyStreamId = mNextStreamId;
+ mFakeStreamId = mNextStreamId;
mNextStreamId++;
return OK;
}
-status_t Camera3Device::tryRemoveDummyStreamLocked() {
+status_t Camera3Device::tryRemoveFakeStreamLocked() {
ATRACE_CALL();
status_t res;
- if (mDummyStreamId == NO_STREAM) return OK;
+ if (mFakeStreamId == NO_STREAM) return OK;
if (mOutputStreams.size() == 1) return OK;
- ALOGV("%s: Camera %s: Removing the dummy stream", __FUNCTION__, mId.string());
+ ALOGV("%s: Camera %s: Removing the fake stream", __FUNCTION__, mId.string());
- // Ok, have a dummy stream and there's at least one other output stream,
- // so remove the dummy
+ // Ok, have a fake stream and there's at least one other output stream,
+ // so remove the fake
- sp<Camera3StreamInterface> deletedStream = mOutputStreams.get(mDummyStreamId);
+ sp<Camera3StreamInterface> deletedStream = mOutputStreams.get(mFakeStreamId);
if (deletedStream == nullptr) {
- SET_ERR_L("Dummy stream %d does not appear to exist", mDummyStreamId);
+ SET_ERR_L("Fake stream %d does not appear to exist", mFakeStreamId);
return INVALID_OPERATION;
}
- mOutputStreams.remove(mDummyStreamId);
+ mOutputStreams.remove(mFakeStreamId);
// Free up the stream endpoint so that it can be used by some other stream
res = deletedStream->disconnect();
if (res != OK) {
- SET_ERR_L("Can't disconnect deleted dummy stream %d", mDummyStreamId);
+ SET_ERR_L("Can't disconnect deleted fake stream %d", mFakeStreamId);
// fall through since we want to still list the stream as deleted.
}
mDeletedStreams.add(deletedStream);
- mDummyStreamId = NO_STREAM;
+ mFakeStreamId = NO_STREAM;
return res;
}
@@ -2775,6 +2948,7 @@
if (listener != NULL) {
listener->notifyError(hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE,
CaptureResultExtras());
+ mSessionStatsBuilder.onDeviceError();
}
// Save stack trace. View by dumping it later.
@@ -2789,16 +2963,17 @@
status_t Camera3Device::registerInFlight(uint32_t frameNumber,
int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
bool hasAppCallback, nsecs_t maxExpectedDuration,
- std::set<String8>& physicalCameraIds, bool isStillCapture,
- bool isZslCapture, bool rotateAndCropAuto, const std::set<std::string>& cameraIdsWithZoom,
- const SurfaceMap& outputSurfaces) {
+ const std::set<std::set<String8>>& physicalCameraIds,
+ bool isStillCapture, bool isZslCapture, bool rotateAndCropAuto,
+ const std::set<std::string>& cameraIdsWithZoom,
+ const SurfaceMap& outputSurfaces, nsecs_t requestTimeNs) {
ATRACE_CALL();
std::lock_guard<std::mutex> l(mInFlightLock);
ssize_t res;
res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers, resultExtras, hasInput,
hasAppCallback, maxExpectedDuration, physicalCameraIds, isStillCapture, isZslCapture,
- rotateAndCropAuto, cameraIdsWithZoom, outputSurfaces));
+ rotateAndCropAuto, cameraIdsWithZoom, requestTimeNs, outputSurfaces));
if (res < 0) return res;
if (mInFlightMap.size() == 1) {
@@ -2829,7 +3004,7 @@
}
void Camera3Device::checkInflightMapLengthLocked() {
- // Sanity check - if we have too many in-flight frames with long total inflight duration,
+ // Validation check - if we have too many in-flight frames with long total inflight duration,
// something has likely gone wrong. This might still be legit only if application send in
// a long burst of long exposure requests.
if (mExpectedInflightDuration > kMinWarnInflightDuration) {
@@ -2868,7 +3043,7 @@
FlushInflightReqStates states {
mId, mInFlightLock, mInFlightMap, mUseHalBufManager,
- listener, *this, *mInterface, *this};
+ listener, *this, *mInterface, *this, mSessionStatsBuilder};
camera3::flushInflightRequests(states);
}
@@ -2908,6 +3083,10 @@
mSupportOfflineProcessing(supportOfflineProcessing) {
// Check with hardware service manager if we can downcast these interfaces
// Somewhat expensive, so cache the results at startup
+ auto castResult_3_7 = device::V3_7::ICameraDeviceSession::castFrom(mHidlSession);
+ if (castResult_3_7.isOk()) {
+ mHidlSession_3_7 = castResult_3_7;
+ }
auto castResult_3_6 = device::V3_6::ICameraDeviceSession::castFrom(mHidlSession);
if (castResult_3_6.isOk()) {
mHidlSession_3_6 = castResult_3_6;
@@ -2941,6 +3120,7 @@
}
void Camera3Device::HalInterface::clear() {
+ mHidlSession_3_7.clear();
mHidlSession_3_6.clear();
mHidlSession_3_5.clear();
mHidlSession_3_4.clear();
@@ -2949,7 +3129,7 @@
}
status_t Camera3Device::HalInterface::constructDefaultRequestSettings(
- camera3_request_template_t templateId,
+ camera_request_template_t templateId,
/*out*/ camera_metadata_t **requestTemplate) {
ATRACE_NAME("CameraHal::constructDefaultRequestSettings");
if (!valid()) return INVALID_OPERATION;
@@ -2981,22 +3161,22 @@
hardware::Return<void> err;
RequestTemplate id;
switch (templateId) {
- case CAMERA3_TEMPLATE_PREVIEW:
+ case CAMERA_TEMPLATE_PREVIEW:
id = RequestTemplate::PREVIEW;
break;
- case CAMERA3_TEMPLATE_STILL_CAPTURE:
+ case CAMERA_TEMPLATE_STILL_CAPTURE:
id = RequestTemplate::STILL_CAPTURE;
break;
- case CAMERA3_TEMPLATE_VIDEO_RECORD:
+ case CAMERA_TEMPLATE_VIDEO_RECORD:
id = RequestTemplate::VIDEO_RECORD;
break;
- case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
+ case CAMERA_TEMPLATE_VIDEO_SNAPSHOT:
id = RequestTemplate::VIDEO_SNAPSHOT;
break;
- case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
+ case CAMERA_TEMPLATE_ZERO_SHUTTER_LAG:
id = RequestTemplate::ZERO_SHUTTER_LAG;
break;
- case CAMERA3_TEMPLATE_MANUAL:
+ case CAMERA_TEMPLATE_MANUAL:
id = RequestTemplate::MANUAL;
break;
default:
@@ -3062,31 +3242,39 @@
}
status_t Camera3Device::HalInterface::configureStreams(const camera_metadata_t *sessionParams,
- camera3_stream_configuration *config, const std::vector<uint32_t>& bufferSizes) {
+ camera_stream_configuration *config, const std::vector<uint32_t>& bufferSizes) {
ATRACE_NAME("CameraHal::configureStreams");
if (!valid()) return INVALID_OPERATION;
status_t res = OK;
+ if (config->input_is_multi_resolution && mHidlSession_3_7 == nullptr) {
+ ALOGE("%s: Camera device doesn't support multi-resolution input stream", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
// Convert stream config to HIDL
std::set<int> activeStreams;
device::V3_2::StreamConfiguration requestedConfiguration3_2;
device::V3_4::StreamConfiguration requestedConfiguration3_4;
+ device::V3_7::StreamConfiguration requestedConfiguration3_7;
requestedConfiguration3_2.streams.resize(config->num_streams);
requestedConfiguration3_4.streams.resize(config->num_streams);
+ requestedConfiguration3_7.streams.resize(config->num_streams);
for (size_t i = 0; i < config->num_streams; i++) {
device::V3_2::Stream &dst3_2 = requestedConfiguration3_2.streams[i];
device::V3_4::Stream &dst3_4 = requestedConfiguration3_4.streams[i];
- camera3_stream_t *src = config->streams[i];
+ device::V3_7::Stream &dst3_7 = requestedConfiguration3_7.streams[i];
+ camera3::camera_stream_t *src = config->streams[i];
Camera3Stream* cam3stream = Camera3Stream::cast(src);
cam3stream->setBufferFreedListener(this);
int streamId = cam3stream->getId();
StreamType streamType;
switch (src->stream_type) {
- case CAMERA3_STREAM_OUTPUT:
+ case CAMERA_STREAM_OUTPUT:
streamType = StreamType::OUTPUT;
break;
- case CAMERA3_STREAM_INPUT:
+ case CAMERA_STREAM_INPUT:
streamType = StreamType::INPUT;
break;
default:
@@ -3099,9 +3287,9 @@
dst3_2.width = src->width;
dst3_2.height = src->height;
dst3_2.usage = mapToConsumerUsage(cam3stream->getUsage());
- dst3_2.rotation = mapToStreamRotation((camera3_stream_rotation_t) src->rotation);
+ dst3_2.rotation = mapToStreamRotation((camera_stream_rotation_t) src->rotation);
// For HidlSession version 3.5 or newer, the format and dataSpace sent
- // to HAL are original, not the overriden ones.
+ // to HAL are original, not the overridden ones.
if (mHidlSession_3_5 != nullptr) {
dst3_2.format = mapToPixelFormat(cam3stream->isFormatOverridden() ?
cam3stream->getOriginalFormat() : src->format);
@@ -3116,7 +3304,14 @@
if (src->physical_camera_id != nullptr) {
dst3_4.physicalCameraId = src->physical_camera_id;
}
-
+ dst3_7.v3_4 = dst3_4;
+ dst3_7.groupId = cam3stream->getHalStreamGroupId();
+ dst3_7.sensorPixelModesUsed.resize(src->sensor_pixel_modes_used.size());
+ size_t j = 0;
+ for (int mode : src->sensor_pixel_modes_used) {
+ dst3_7.sensorPixelModesUsed[j++] =
+ static_cast<CameraMetadataEnumAndroidSensorPixelMode>(mode);
+ }
activeStreams.insert(streamId);
// Create Buffer ID map if necessary
mBufferRecords.tryCreateBufferCache(streamId);
@@ -3126,16 +3321,22 @@
StreamConfigurationMode operationMode;
res = mapToStreamConfigurationMode(
- (camera3_stream_configuration_mode_t) config->operation_mode,
+ (camera_stream_configuration_mode_t) config->operation_mode,
/*out*/ &operationMode);
if (res != OK) {
return res;
}
requestedConfiguration3_2.operationMode = operationMode;
requestedConfiguration3_4.operationMode = operationMode;
+ requestedConfiguration3_7.operationMode = operationMode;
+ size_t sessionParamSize = get_camera_metadata_size(sessionParams);
requestedConfiguration3_4.sessionParams.setToExternal(
reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(sessionParams)),
- get_camera_metadata_size(sessionParams));
+ sessionParamSize);
+ requestedConfiguration3_7.operationMode = operationMode;
+ requestedConfiguration3_7.sessionParams.setToExternal(
+ reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(sessionParams)),
+ sessionParamSize);
// Invoke configureStreams
device::V3_3::HalStreamConfiguration finalConfiguration;
@@ -3182,7 +3383,17 @@
};
// See which version of HAL we have
- if (mHidlSession_3_6 != nullptr) {
+ if (mHidlSession_3_7 != nullptr) {
+ ALOGV("%s: v3.7 device found", __FUNCTION__);
+ requestedConfiguration3_7.streamConfigCounter = mNextStreamConfigCounter++;
+ requestedConfiguration3_7.multiResolutionInputImage = config->input_is_multi_resolution;
+ auto err = mHidlSession_3_7->configureStreams_3_7(
+ requestedConfiguration3_7, configStream36Cb);
+ res = postprocConfigStream36(err);
+ if (res != OK) {
+ return res;
+ }
+ } else if (mHidlSession_3_6 != nullptr) {
ALOGV("%s: v3.6 device found", __FUNCTION__);
device::V3_5::StreamConfiguration requestedConfiguration3_5;
requestedConfiguration3_5.v3_4 = requestedConfiguration3_4;
@@ -3255,7 +3466,7 @@
// And convert output stream configuration from HIDL
for (size_t i = 0; i < config->num_streams; i++) {
- camera3_stream_t *dst = config->streams[i];
+ camera3::camera_stream_t *dst = config->streams[i];
int streamId = Camera3Stream::cast(dst)->getId();
// Start scan at i, with the assumption that the stream order matches
@@ -3308,7 +3519,7 @@
dst->data_space = overrideDataSpace;
}
- if (dst->stream_type == CAMERA3_STREAM_INPUT) {
+ if (dst->stream_type == CAMERA_STREAM_INPUT) {
if (src.v3_2.producerUsage != 0) {
ALOGE("%s: Stream %d: INPUT streams must have 0 for producer usage",
__FUNCTION__, streamId);
@@ -3332,7 +3543,147 @@
return res;
}
-status_t Camera3Device::HalInterface::wrapAsHidlRequest(camera3_capture_request_t* request,
+status_t Camera3Device::HalInterface::configureInjectedStreams(
+ const camera_metadata_t* sessionParams, camera_stream_configuration* config,
+ const std::vector<uint32_t>& bufferSizes,
+ const CameraMetadata& cameraCharacteristics) {
+ ATRACE_NAME("InjectionCameraHal::configureStreams");
+ if (!valid()) return INVALID_OPERATION;
+ status_t res = OK;
+
+ if (config->input_is_multi_resolution) {
+ ALOGE("%s: Injection camera device doesn't support multi-resolution input "
+ "stream", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ // Convert stream config to HIDL
+ std::set<int> activeStreams;
+ device::V3_2::StreamConfiguration requestedConfiguration3_2;
+ device::V3_4::StreamConfiguration requestedConfiguration3_4;
+ device::V3_7::StreamConfiguration requestedConfiguration3_7;
+ requestedConfiguration3_2.streams.resize(config->num_streams);
+ requestedConfiguration3_4.streams.resize(config->num_streams);
+ requestedConfiguration3_7.streams.resize(config->num_streams);
+ for (size_t i = 0; i < config->num_streams; i++) {
+ device::V3_2::Stream& dst3_2 = requestedConfiguration3_2.streams[i];
+ device::V3_4::Stream& dst3_4 = requestedConfiguration3_4.streams[i];
+ device::V3_7::Stream& dst3_7 = requestedConfiguration3_7.streams[i];
+ camera3::camera_stream_t* src = config->streams[i];
+
+ Camera3Stream* cam3stream = Camera3Stream::cast(src);
+ cam3stream->setBufferFreedListener(this);
+ int streamId = cam3stream->getId();
+ StreamType streamType;
+ switch (src->stream_type) {
+ case CAMERA_STREAM_OUTPUT:
+ streamType = StreamType::OUTPUT;
+ break;
+ case CAMERA_STREAM_INPUT:
+ streamType = StreamType::INPUT;
+ break;
+ default:
+ ALOGE("%s: Stream %d: Unsupported stream type %d", __FUNCTION__,
+ streamId, config->streams[i]->stream_type);
+ return BAD_VALUE;
+ }
+ dst3_2.id = streamId;
+ dst3_2.streamType = streamType;
+ dst3_2.width = src->width;
+ dst3_2.height = src->height;
+ dst3_2.usage = mapToConsumerUsage(cam3stream->getUsage());
+ dst3_2.rotation =
+ mapToStreamRotation((camera_stream_rotation_t)src->rotation);
+ // For HidlSession version 3.5 or newer, the format and dataSpace sent
+ // to HAL are original, not the overridden ones.
+ if (mHidlSession_3_5 != nullptr) {
+ dst3_2.format = mapToPixelFormat(cam3stream->isFormatOverridden()
+ ? cam3stream->getOriginalFormat()
+ : src->format);
+ dst3_2.dataSpace =
+ mapToHidlDataspace(cam3stream->isDataSpaceOverridden()
+ ? cam3stream->getOriginalDataSpace()
+ : src->data_space);
+ } else {
+ dst3_2.format = mapToPixelFormat(src->format);
+ dst3_2.dataSpace = mapToHidlDataspace(src->data_space);
+ }
+ dst3_4.v3_2 = dst3_2;
+ dst3_4.bufferSize = bufferSizes[i];
+ if (src->physical_camera_id != nullptr) {
+ dst3_4.physicalCameraId = src->physical_camera_id;
+ }
+ dst3_7.v3_4 = dst3_4;
+ dst3_7.groupId = cam3stream->getHalStreamGroupId();
+ dst3_7.sensorPixelModesUsed.resize(src->sensor_pixel_modes_used.size());
+ size_t j = 0;
+ for (int mode : src->sensor_pixel_modes_used) {
+ dst3_7.sensorPixelModesUsed[j++] =
+ static_cast<CameraMetadataEnumAndroidSensorPixelMode>(mode);
+ }
+ activeStreams.insert(streamId);
+ // Create Buffer ID map if necessary
+ mBufferRecords.tryCreateBufferCache(streamId);
+ }
+ // remove BufferIdMap for deleted streams
+ mBufferRecords.removeInactiveBufferCaches(activeStreams);
+
+ StreamConfigurationMode operationMode;
+ res = mapToStreamConfigurationMode(
+ (camera_stream_configuration_mode_t)config->operation_mode,
+ /*out*/ &operationMode);
+ if (res != OK) {
+ return res;
+ }
+ requestedConfiguration3_7.operationMode = operationMode;
+ size_t sessionParamSize = get_camera_metadata_size(sessionParams);
+ requestedConfiguration3_7.operationMode = operationMode;
+ requestedConfiguration3_7.sessionParams.setToExternal(
+ reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(sessionParams)),
+ sessionParamSize);
+
+ // See which version of HAL we have
+ if (mHidlSession_3_7 != nullptr) {
+ requestedConfiguration3_7.streamConfigCounter = mNextStreamConfigCounter++;
+ requestedConfiguration3_7.multiResolutionInputImage =
+ config->input_is_multi_resolution;
+
+ const camera_metadata_t* rawMetadata = cameraCharacteristics.getAndLock();
+ ::android::hardware::camera::device::V3_2::CameraMetadata hidlChars = {};
+ hidlChars.setToExternal(
+ reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(rawMetadata)),
+ get_camera_metadata_size(rawMetadata));
+ cameraCharacteristics.unlock(rawMetadata);
+
+ sp<hardware::camera::device::V3_7::ICameraInjectionSession>
+ hidlInjectionSession_3_7;
+ auto castInjectionResult_3_7 =
+ device::V3_7::ICameraInjectionSession::castFrom(mHidlSession_3_7);
+ if (castInjectionResult_3_7.isOk()) {
+ hidlInjectionSession_3_7 = castInjectionResult_3_7;
+ } else {
+ ALOGE("%s: Transaction error: %s", __FUNCTION__,
+ castInjectionResult_3_7.description().c_str());
+ return DEAD_OBJECT;
+ }
+
+ auto err = hidlInjectionSession_3_7->configureInjectionStreams(
+ requestedConfiguration3_7, hidlChars);
+ if (!err.isOk()) {
+ ALOGE("%s: Transaction error: %s", __FUNCTION__,
+ err.description().c_str());
+ return DEAD_OBJECT;
+ }
+ } else {
+ ALOGE("%s: mHidlSession_3_7 does not exist, the lowest version of injection "
+ "session is 3.7", __FUNCTION__);
+ return DEAD_OBJECT;
+ }
+
+ return res;
+}
+
+status_t Camera3Device::HalInterface::wrapAsHidlRequest(camera_capture_request_t* request,
/*out*/device::V3_2::CaptureRequest* captureRequest,
/*out*/std::vector<native_handle_t*>* handlesCreated,
/*out*/std::vector<std::pair<int32_t, int32_t>>* inflightBuffers) {
@@ -3377,7 +3728,7 @@
captureRequest->outputBuffers.resize(request->num_output_buffers);
for (size_t i = 0; i < request->num_output_buffers; i++) {
- const camera3_stream_buffer_t *src = request->output_buffers + i;
+ const camera_stream_buffer_t *src = request->output_buffers + i;
StreamBuffer &dst = captureRequest->outputBuffers[i];
int32_t streamId = Camera3Stream::cast(src->stream)->getId();
if (src->buffer != nullptr) {
@@ -3435,11 +3786,16 @@
}
status_t Camera3Device::HalInterface::processBatchCaptureRequests(
- std::vector<camera3_capture_request_t*>& requests,/*out*/uint32_t* numRequestProcessed) {
+ std::vector<camera_capture_request_t*>& requests,/*out*/uint32_t* numRequestProcessed) {
ATRACE_NAME("CameraHal::processBatchCaptureRequests");
if (!valid()) return INVALID_OPERATION;
sp<device::V3_4::ICameraDeviceSession> hidlSession_3_4;
+ sp<device::V3_7::ICameraDeviceSession> hidlSession_3_7;
+ auto castResult_3_7 = device::V3_7::ICameraDeviceSession::castFrom(mHidlSession);
+ if (castResult_3_7.isOk()) {
+ hidlSession_3_7 = castResult_3_7;
+ }
auto castResult_3_4 = device::V3_4::ICameraDeviceSession::castFrom(mHidlSession);
if (castResult_3_4.isOk()) {
hidlSession_3_4 = castResult_3_4;
@@ -3447,8 +3803,11 @@
hardware::hidl_vec<device::V3_2::CaptureRequest> captureRequests;
hardware::hidl_vec<device::V3_4::CaptureRequest> captureRequests_3_4;
+ hardware::hidl_vec<device::V3_7::CaptureRequest> captureRequests_3_7;
size_t batchSize = requests.size();
- if (hidlSession_3_4 != nullptr) {
+ if (hidlSession_3_7 != nullptr) {
+ captureRequests_3_7.resize(batchSize);
+ } else if (hidlSession_3_4 != nullptr) {
captureRequests_3_4.resize(batchSize);
} else {
captureRequests.resize(batchSize);
@@ -3458,7 +3817,10 @@
status_t res = OK;
for (size_t i = 0; i < batchSize; i++) {
- if (hidlSession_3_4 != nullptr) {
+ if (hidlSession_3_7 != nullptr) {
+ res = wrapAsHidlRequest(requests[i], /*out*/&captureRequests_3_7[i].v3_4.v3_2,
+ /*out*/&handlesCreated, /*out*/&inflightBuffers);
+ } else if (hidlSession_3_4 != nullptr) {
res = wrapAsHidlRequest(requests[i], /*out*/&captureRequests_3_4[i].v3_2,
/*out*/&handlesCreated, /*out*/&inflightBuffers);
} else {
@@ -3489,9 +3851,11 @@
// Write metadata to FMQ.
for (size_t i = 0; i < batchSize; i++) {
- camera3_capture_request_t* request = requests[i];
+ camera_capture_request_t* request = requests[i];
device::V3_2::CaptureRequest* captureRequest;
- if (hidlSession_3_4 != nullptr) {
+ if (hidlSession_3_7 != nullptr) {
+ captureRequest = &captureRequests_3_7[i].v3_4.v3_2;
+ } else if (hidlSession_3_4 != nullptr) {
captureRequest = &captureRequests_3_4[i].v3_2;
} else {
captureRequest = &captureRequests[i];
@@ -3518,33 +3882,42 @@
captureRequest->fmqSettingsSize = 0u;
}
- if (hidlSession_3_4 != nullptr) {
- captureRequests_3_4[i].physicalCameraSettings.resize(request->num_physcam_settings);
+ // hidl session 3.7 specific handling.
+ if (hidlSession_3_7 != nullptr) {
+ captureRequests_3_7[i].inputWidth = request->input_width;
+ captureRequests_3_7[i].inputHeight = request->input_height;
+ }
+
+ // hidl session 3.7 and 3.4 specific handling.
+ if (hidlSession_3_7 != nullptr || hidlSession_3_4 != nullptr) {
+ hardware::hidl_vec<device::V3_4::PhysicalCameraSetting>& physicalCameraSettings =
+ (hidlSession_3_7 != nullptr) ?
+ captureRequests_3_7[i].v3_4.physicalCameraSettings :
+ captureRequests_3_4[i].physicalCameraSettings;
+ physicalCameraSettings.resize(request->num_physcam_settings);
for (size_t j = 0; j < request->num_physcam_settings; j++) {
if (request->physcam_settings != nullptr) {
size_t settingsSize = get_camera_metadata_size(request->physcam_settings[j]);
if (mRequestMetadataQueue != nullptr && mRequestMetadataQueue->write(
reinterpret_cast<const uint8_t*>(request->physcam_settings[j]),
settingsSize)) {
- captureRequests_3_4[i].physicalCameraSettings[j].settings.resize(0);
- captureRequests_3_4[i].physicalCameraSettings[j].fmqSettingsSize =
- settingsSize;
+ physicalCameraSettings[j].settings.resize(0);
+ physicalCameraSettings[j].fmqSettingsSize = settingsSize;
} else {
if (mRequestMetadataQueue != nullptr) {
ALOGW("%s: couldn't utilize fmq, fallback to hwbinder", __FUNCTION__);
}
- captureRequests_3_4[i].physicalCameraSettings[j].settings.setToExternal(
+ physicalCameraSettings[j].settings.setToExternal(
reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(
request->physcam_settings[j])),
get_camera_metadata_size(request->physcam_settings[j]));
- captureRequests_3_4[i].physicalCameraSettings[j].fmqSettingsSize = 0u;
+ physicalCameraSettings[j].fmqSettingsSize = 0u;
}
} else {
- captureRequests_3_4[i].physicalCameraSettings[j].fmqSettingsSize = 0u;
- captureRequests_3_4[i].physicalCameraSettings[j].settings.resize(0);
+ physicalCameraSettings[j].fmqSettingsSize = 0u;
+ physicalCameraSettings[j].settings.resize(0);
}
- captureRequests_3_4[i].physicalCameraSettings[j].physicalCameraId =
- request->physcam_id[j];
+ physicalCameraSettings[j].physicalCameraId = request->physcam_id[j];
}
}
}
@@ -3555,7 +3928,10 @@
status = s;
*numRequestProcessed = n;
};
- if (hidlSession_3_4 != nullptr) {
+ if (hidlSession_3_7 != nullptr) {
+ err = hidlSession_3_7->processCaptureRequest_3_7(captureRequests_3_7, cachesToRemove,
+ resultCallback);
+ } else if (hidlSession_3_4 != nullptr) {
err = hidlSession_3_4->processCaptureRequest_3_4(captureRequests_3_4, cachesToRemove,
resultCallback);
} else {
@@ -3777,6 +4153,7 @@
mInterface(interface),
mListener(nullptr),
mId(getId(parent)),
+ mFirstRepeating(false),
mReconfigured(false),
mDoPause(false),
mPaused(true),
@@ -3786,6 +4163,8 @@
mCurrentAfTriggerId(0),
mCurrentPreCaptureTriggerId(0),
mRotateAndCropOverride(ANDROID_SCALER_ROTATE_AND_CROP_NONE),
+ mCameraMute(false),
+ mCameraMuteChanged(false),
mRepeatingLastFrameNumber(
hardware::camera2::ICameraDeviceUser::NO_IN_FLIGHT_REPEATING_FRAMES),
mPrepareVideoStream(false),
@@ -3794,7 +4173,7 @@
mSessionParamKeys(sessionParamKeys),
mLatestSessionParams(sessionParamKeys.size()),
mUseHalBufManager(useHalBufManager) {
- mStatusId = statusTracker->addComponent();
+ mStatusId = statusTracker->addComponent("RequestThread");
}
Camera3Device::RequestThread::~RequestThread() {}
@@ -3807,11 +4186,13 @@
}
void Camera3Device::RequestThread::configurationComplete(bool isConstrainedHighSpeed,
- const CameraMetadata& sessionParams) {
+ const CameraMetadata& sessionParams,
+ const std::map<int32_t, std::set<String8>>& groupIdPhysicalCameraMap) {
ATRACE_CALL();
Mutex::Autolock l(mRequestLock);
mReconfigured = true;
mLatestSessionParams = sessionParams;
+ mGroupIdPhysicalCameraMap = groupIdPhysicalCameraMap;
// Prepare video stream for high speed recording.
mPrepareVideoStream = isConstrainedHighSpeed;
mConstrainedMode = isConstrainedHighSpeed;
@@ -3907,6 +4288,7 @@
*lastFrameNumber = mRepeatingLastFrameNumber;
}
mRepeatingRequests.clear();
+ mFirstRepeating = true;
mRepeatingRequests.insert(mRepeatingRequests.begin(),
requests.begin(), requests.end());
@@ -3959,14 +4341,15 @@
it != mRequestQueue.end(); ++it) {
// Abort the input buffers for reprocess requests.
if ((*it)->mInputStream != NULL) {
- camera3_stream_buffer_t inputBuffer;
+ camera_stream_buffer_t inputBuffer;
+ camera3::Size inputBufferSize;
status_t res = (*it)->mInputStream->getInputBuffer(&inputBuffer,
- /*respectHalLimit*/ false);
+ &inputBufferSize, /*respectHalLimit*/ false);
if (res != OK) {
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;
+ inputBuffer.status = CAMERA_BUFFER_STATUS_ERROR;
res = (*it)->mInputStream->returnInputBuffer(inputBuffer);
if (res != OK) {
ALOGE("%s: %d: couldn't return input buffer while clearing the request "
@@ -4070,7 +4453,7 @@
ATRACE_CALL();
status_t res;
size_t batchSize = mNextRequests.size();
- std::vector<camera3_capture_request_t*> requests(batchSize);
+ std::vector<camera_capture_request_t*> requests(batchSize);
uint32_t numRequestProcessed = 0;
for (size_t i = 0; i < batchSize; i++) {
requests[i] = &mNextRequests.editItemAt(i).halRequest;
@@ -4167,33 +4550,34 @@
void Camera3Device::RequestThread::updateNextRequest(NextRequest& nextRequest) {
// Update the latest request sent to HAL
- if (nextRequest.halRequest.settings != NULL) { // Don't update if they were unchanged
+ camera_capture_request_t& halRequest = nextRequest.halRequest;
+ if (halRequest.settings != NULL) { // Don't update if they were unchanged
Mutex::Autolock al(mLatestRequestMutex);
- camera_metadata_t* cloned = clone_camera_metadata(nextRequest.halRequest.settings);
+ camera_metadata_t* cloned = clone_camera_metadata(halRequest.settings);
mLatestRequest.acquire(cloned);
mLatestPhysicalRequest.clear();
- for (uint32_t i = 0; i < nextRequest.halRequest.num_physcam_settings; i++) {
- cloned = clone_camera_metadata(nextRequest.halRequest.physcam_settings[i]);
- mLatestPhysicalRequest.emplace(nextRequest.halRequest.physcam_id[i],
+ for (uint32_t i = 0; i < halRequest.num_physcam_settings; i++) {
+ cloned = clone_camera_metadata(halRequest.physcam_settings[i]);
+ mLatestPhysicalRequest.emplace(halRequest.physcam_id[i],
CameraMetadata(cloned));
}
sp<Camera3Device> parent = mParent.promote();
if (parent != NULL) {
parent->monitorMetadata(TagMonitor::REQUEST,
- nextRequest.halRequest.frame_number,
+ halRequest.frame_number,
0, mLatestRequest, mLatestPhysicalRequest);
}
}
- if (nextRequest.halRequest.settings != NULL) {
+ if (halRequest.settings != NULL) {
nextRequest.captureRequest->mSettingsList.begin()->metadata.unlock(
- nextRequest.halRequest.settings);
+ halRequest.settings);
}
- cleanupPhysicalSettings(nextRequest.captureRequest, &nextRequest.halRequest);
+ cleanupPhysicalSettings(nextRequest.captureRequest, &halRequest);
}
bool Camera3Device::RequestThread::updateSessionParameters(const CameraMetadata& settings) {
@@ -4389,8 +4773,8 @@
for (size_t i = 0; i < mNextRequests.size(); i++) {
auto& nextRequest = mNextRequests.editItemAt(i);
sp<CaptureRequest> captureRequest = nextRequest.captureRequest;
- camera3_capture_request_t* halRequest = &nextRequest.halRequest;
- Vector<camera3_stream_buffer_t>* outputBuffers = &nextRequest.outputBuffers;
+ camera_capture_request_t* halRequest = &nextRequest.halRequest;
+ Vector<camera_stream_buffer_t>* outputBuffers = &nextRequest.outputBuffers;
// Prepare a request to HAL
halRequest->frame_number = captureRequest->mResultExtras.frameNumber;
@@ -4409,10 +4793,13 @@
mPrevTriggers = triggerCount;
bool rotateAndCropChanged = overrideAutoRotateAndCrop(captureRequest);
+ bool testPatternChanged = overrideTestPattern(captureRequest);
- // If the request is the same as last, or we had triggers last time
+ // If the request is the same as last, or we had triggers now or last time or
+ // changing overrides this time
bool newRequest =
- (mPrevRequest != captureRequest || triggersMixedIn || rotateAndCropChanged) &&
+ (mPrevRequest != captureRequest || triggersMixedIn ||
+ rotateAndCropChanged || testPatternChanged) &&
// Request settings are all the same within one batch, so only treat the first
// request in a batch as new
!(batchedRequest && i > 0);
@@ -4420,11 +4807,11 @@
std::set<std::string> cameraIdsWithZoom;
/**
* HAL workaround:
- * Insert a dummy trigger ID if a trigger is set but no trigger ID is
+ * Insert a fake trigger ID if a trigger is set but no trigger ID is
*/
- res = addDummyTriggerIds(captureRequest);
+ res = addFakeTriggerIds(captureRequest);
if (res != OK) {
- SET_ERR("RequestThread: Unable to insert dummy trigger IDs "
+ SET_ERR("RequestThread: Unable to insert fake trigger IDs "
"(capture request %d, HAL device: %s (%d)",
halRequest->frame_number, strerror(-res), res);
return INVALID_OPERATION;
@@ -4553,15 +4940,18 @@
// Fill in buffers
if (captureRequest->mInputStream != NULL) {
halRequest->input_buffer = &captureRequest->mInputBuffer;
+
+ halRequest->input_width = captureRequest->mInputBufferSize.width;
+ halRequest->input_height = captureRequest->mInputBufferSize.height;
totalNumBuffers += 1;
} else {
halRequest->input_buffer = NULL;
}
- outputBuffers->insertAt(camera3_stream_buffer_t(), 0,
+ outputBuffers->insertAt(camera_stream_buffer_t(), 0,
captureRequest->mOutputStreams.size());
halRequest->output_buffers = outputBuffers->array();
- std::set<String8> requestedPhysicalCameras;
+ std::set<std::set<String8>> requestedPhysicalCameras;
sp<Camera3Device> parent = mParent.promote();
if (parent == NULL) {
@@ -4614,12 +5004,16 @@
return TIMED_OUT;
}
// HAL will request buffer through requestStreamBuffer API
- camera3_stream_buffer_t& buffer = outputBuffers->editItemAt(j);
+ camera_stream_buffer_t& buffer = outputBuffers->editItemAt(j);
buffer.stream = outputStream->asHalStream();
buffer.buffer = nullptr;
- buffer.status = CAMERA3_BUFFER_STATUS_OK;
+ buffer.status = CAMERA_BUFFER_STATUS_OK;
buffer.acquire_fence = -1;
buffer.release_fence = -1;
+ // Mark the output stream as unpreparable to block clients from calling
+ // 'prepare' after this request reaches CameraHal and before the respective
+ // buffers are requested.
+ outputStream->markUnpreparable();
} else {
res = outputStream->getBuffer(&outputBuffers->editItemAt(j),
waitDuration,
@@ -4652,14 +5046,11 @@
}
String8 physicalCameraId = outputStream->getPhysicalCameraId();
-
- if (!physicalCameraId.isEmpty()) {
- // Physical stream isn't supported for input request.
- if (halRequest->input_buffer) {
- CLOGE("Physical stream is not supported for input request");
- return INVALID_OPERATION;
- }
- requestedPhysicalCameras.insert(physicalCameraId);
+ int32_t streamGroupId = outputStream->getHalStreamGroupId();
+ if (streamGroupId != -1 && mGroupIdPhysicalCameraMap.count(streamGroupId) == 1) {
+ requestedPhysicalCameras.insert(mGroupIdPhysicalCameraMap[streamGroupId]);
+ } else if (!physicalCameraId.isEmpty()) {
+ requestedPhysicalCameras.insert(std::set<String8>({physicalCameraId}));
}
halRequest->num_output_buffers++;
}
@@ -4689,6 +5080,7 @@
ATRACE_ASYNC_BEGIN("still capture", mNextRequests[i].halRequest.frame_number);
}
+ e = camera_metadata_ro_entry_t();
find_camera_metadata_ro_entry(settings, ANDROID_CONTROL_ENABLE_ZSL, &e);
if ((e.count > 0) && (e.data.u8[0] == ANDROID_CONTROL_ENABLE_ZSL_TRUE)) {
isZslCapture = true;
@@ -4702,7 +5094,7 @@
requestedPhysicalCameras, isStillCapture, isZslCapture,
captureRequest->mRotateAndCropAuto, mPrevCameraIdsWithZoom,
(mUseHalBufManager) ? uniqueSurfaceIdMap :
- SurfaceMap{});
+ SurfaceMap{}, captureRequest->mRequestTimeNs);
ALOGVV("%s: registered in flight requestId = %" PRId32 ", frameNumber = %" PRId64
", burstId = %" PRId32 ".",
__FUNCTION__,
@@ -4873,6 +5265,16 @@
return OK;
}
+status_t Camera3Device::RequestThread::setCameraMute(bool enabled) {
+ ATRACE_CALL();
+ Mutex::Autolock l(mTriggerMutex);
+ if (enabled != mCameraMute) {
+ mCameraMute = enabled;
+ mCameraMuteChanged = true;
+ }
+ return OK;
+}
+
nsecs_t Camera3Device::getExpectedInFlightDuration() {
ATRACE_CALL();
std::lock_guard<std::mutex> l(mInFlightLock);
@@ -4881,7 +5283,7 @@
}
void Camera3Device::RequestThread::cleanupPhysicalSettings(sp<CaptureRequest> request,
- camera3_capture_request_t *halRequest) {
+ camera_capture_request_t *halRequest) {
if ((request == nullptr) || (halRequest == nullptr)) {
ALOGE("%s: Invalid request!", __FUNCTION__);
return;
@@ -4916,8 +5318,8 @@
}
sp<CaptureRequest> captureRequest = nextRequest.captureRequest;
- camera3_capture_request_t* halRequest = &nextRequest.halRequest;
- Vector<camera3_stream_buffer_t>* outputBuffers = &nextRequest.outputBuffers;
+ camera_capture_request_t* halRequest = &nextRequest.halRequest;
+ Vector<camera_stream_buffer_t>* outputBuffers = &nextRequest.outputBuffers;
if (halRequest->settings != NULL) {
captureRequest->mSettingsList.begin()->metadata.unlock(halRequest->settings);
@@ -4926,7 +5328,7 @@
cleanupPhysicalSettings(captureRequest, halRequest);
if (captureRequest->mInputStream != NULL) {
- captureRequest->mInputBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
+ captureRequest->mInputBuffer.status = CAMERA_BUFFER_STATUS_ERROR;
captureRequest->mInputStream->returnInputBuffer(captureRequest->mInputBuffer);
}
@@ -4940,7 +5342,7 @@
close(acquireFence);
outputBuffers->editItemAt(i).acquire_fence = -1;
}
- outputBuffers->editItemAt(i).status = CAMERA3_BUFFER_STATUS_ERROR;
+ outputBuffers->editItemAt(i).status = CAMERA_BUFFER_STATUS_ERROR;
captureRequest->mOutputStreams.editItemAt(i)->returnBuffer((*outputBuffers)[i], 0,
/*timestampIncreasing*/true, std::vector<size_t> (),
captureRequest->mResultExtras.frameNumber);
@@ -4990,7 +5392,7 @@
return;
}
- nextRequest.halRequest = camera3_capture_request_t();
+ nextRequest.halRequest = camera_capture_request_t();
nextRequest.submitted = false;
mNextRequests.add(nextRequest);
@@ -5004,7 +5406,7 @@
break;
}
- additionalRequest.halRequest = camera3_capture_request_t();
+ additionalRequest.halRequest = camera_capture_request_t();
additionalRequest.submitted = false;
mNextRequests.add(additionalRequest);
}
@@ -5029,6 +5431,17 @@
// list. Guarantees a complete in-sequence set of captures to
// application.
const RequestList &requests = mRepeatingRequests;
+ if (mFirstRepeating) {
+ mFirstRepeating = false;
+ } else {
+ for (auto& request : requests) {
+ // For repeating requests, override timestamp request using
+ // the time a request is inserted into the request queue,
+ // because the original repeating request will have an old
+ // fixed timestamp.
+ request->mRequestTimeNs = systemTime();
+ }
+ }
RequestList::const_iterator firstRequest =
requests.begin();
nextRequest = *firstRequest;
@@ -5113,7 +5526,8 @@
// Since RequestThread::clear() removes buffers from the input stream,
// get the right buffer here before unlocking mRequestLock
if (nextRequest->mInputStream != NULL) {
- res = nextRequest->mInputStream->getInputBuffer(&nextRequest->mInputBuffer);
+ res = nextRequest->mInputStream->getInputBuffer(&nextRequest->mInputBuffer,
+ &nextRequest->mInputBufferSize);
if (res != OK) {
// Can't get input buffer from gralloc queue - this could be due to
// disconnected queue or other producer misbehavior, so not a fatal
@@ -5353,26 +5767,26 @@
return OK;
}
-status_t Camera3Device::RequestThread::addDummyTriggerIds(
+status_t Camera3Device::RequestThread::addFakeTriggerIds(
const sp<CaptureRequest> &request) {
// Trigger ID 0 had special meaning in the HAL2 spec, so avoid it here
- static const int32_t dummyTriggerId = 1;
+ static const int32_t fakeTriggerId = 1;
status_t res;
CameraMetadata &metadata = request->mSettingsList.begin()->metadata;
- // If AF trigger is active, insert a dummy AF trigger ID if none already
+ // If AF trigger is active, insert a fake AF trigger ID if none already
// exists
camera_metadata_entry afTrigger = metadata.find(ANDROID_CONTROL_AF_TRIGGER);
camera_metadata_entry afId = metadata.find(ANDROID_CONTROL_AF_TRIGGER_ID);
if (afTrigger.count > 0 &&
afTrigger.data.u8[0] != ANDROID_CONTROL_AF_TRIGGER_IDLE &&
afId.count == 0) {
- res = metadata.update(ANDROID_CONTROL_AF_TRIGGER_ID, &dummyTriggerId, 1);
+ res = metadata.update(ANDROID_CONTROL_AF_TRIGGER_ID, &fakeTriggerId, 1);
if (res != OK) return res;
}
- // If AE precapture trigger is active, insert a dummy precapture trigger ID
+ // If AE precapture trigger is active, insert a fake precapture trigger ID
// if none already exists
camera_metadata_entry pcTrigger =
metadata.find(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER);
@@ -5381,7 +5795,7 @@
pcTrigger.data.u8[0] != ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE &&
pcId.count == 0) {
res = metadata.update(ANDROID_CONTROL_AE_PRECAPTURE_ID,
- &dummyTriggerId, 1);
+ &fakeTriggerId, 1);
if (res != OK) return res;
}
@@ -5414,6 +5828,73 @@
return false;
}
+bool Camera3Device::RequestThread::overrideTestPattern(
+ const sp<CaptureRequest> &request) {
+ ATRACE_CALL();
+
+ Mutex::Autolock l(mTriggerMutex);
+
+ bool changed = false;
+
+ int32_t testPatternMode = request->mOriginalTestPatternMode;
+ int32_t testPatternData[4] = {
+ request->mOriginalTestPatternData[0],
+ request->mOriginalTestPatternData[1],
+ request->mOriginalTestPatternData[2],
+ request->mOriginalTestPatternData[3]
+ };
+
+ if (mCameraMute) {
+ testPatternMode = ANDROID_SENSOR_TEST_PATTERN_MODE_SOLID_COLOR;
+ testPatternData[0] = 0;
+ testPatternData[1] = 0;
+ testPatternData[2] = 0;
+ testPatternData[3] = 0;
+ }
+
+ CameraMetadata &metadata = request->mSettingsList.begin()->metadata;
+
+ auto testPatternEntry = metadata.find(ANDROID_SENSOR_TEST_PATTERN_MODE);
+ if (testPatternEntry.count > 0) {
+ if (testPatternEntry.data.i32[0] != testPatternMode) {
+ testPatternEntry.data.i32[0] = testPatternMode;
+ changed = true;
+ }
+ } else {
+ metadata.update(ANDROID_SENSOR_TEST_PATTERN_MODE,
+ &testPatternMode, 1);
+ changed = true;
+ }
+
+ auto testPatternColor = metadata.find(ANDROID_SENSOR_TEST_PATTERN_DATA);
+ if (testPatternColor.count > 0) {
+ for (size_t i = 0; i < 4; i++) {
+ if (testPatternColor.data.i32[i] != (int32_t)testPatternData[i]) {
+ testPatternColor.data.i32[i] = testPatternData[i];
+ changed = true;
+ }
+ }
+ } else {
+ metadata.update(ANDROID_SENSOR_TEST_PATTERN_DATA,
+ (int32_t*)testPatternData, 4);
+ changed = true;
+ }
+
+ return changed;
+}
+
+status_t Camera3Device::RequestThread::setHalInterface(
+ sp<HalInterface> newHalInterface) {
+ if (newHalInterface.get() == nullptr) {
+ ALOGE("%s: The newHalInterface does not exist!", __FUNCTION__);
+ return DEAD_OBJECT;
+ }
+
+ mInterface = newHalInterface;
+
+ return OK;
+}
+
/**
* PreparerThread inner class methods
*/
@@ -5647,7 +6128,7 @@
std::lock_guard<std::mutex> lock(mLock);
mStatusTracker = statusTracker;
- mRequestBufferStatusId = statusTracker->addComponent();
+ mRequestBufferStatusId = statusTracker->addComponent("BufferRequestSM");
return OK;
}
@@ -6039,4 +6520,76 @@
return mRequestThread->setRotateAndCropAutoBehavior(rotateAndCropValue);
}
+bool Camera3Device::supportsCameraMute() {
+ Mutex::Autolock il(mInterfaceLock);
+ Mutex::Autolock l(mLock);
+
+ return mSupportCameraMute;
+}
+
+status_t Camera3Device::setCameraMute(bool enabled) {
+ ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
+ Mutex::Autolock l(mLock);
+
+ if (mRequestThread == nullptr || !mSupportCameraMute) {
+ return INVALID_OPERATION;
+ }
+ return mRequestThread->setCameraMute(enabled);
+}
+
+status_t Camera3Device::injectCamera(const String8& injectedCamId,
+ sp<CameraProviderManager> manager) {
+ ALOGI("%s Injection camera: injectedCamId = %s", __FUNCTION__, injectedCamId.string());
+ ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
+
+ status_t res = NO_ERROR;
+ if (mInjectionMethods->isInjecting()) {
+ if (injectedCamId == mInjectionMethods->getInjectedCamId()) {
+ return OK;
+ } else {
+ res = mInjectionMethods->stopInjection();
+ if (res != OK) {
+ ALOGE("%s: Failed to stop the injection camera! ret != NO_ERROR: %d",
+ __FUNCTION__, res);
+ return res;
+ }
+ }
+ }
+
+ res = mInjectionMethods->injectionInitialize(injectedCamId, manager, this);
+ if (res != OK) {
+ ALOGE("%s: Failed to initialize the injection camera! ret != NO_ERROR: %d",
+ __FUNCTION__, res);
+ return res;
+ }
+
+ camera3::camera_stream_configuration injectionConfig;
+ std::vector<uint32_t> injectionBufferSizes;
+ mInjectionMethods->getInjectionConfig(&injectionConfig, &injectionBufferSizes);
+ // When the second display of android is cast to the remote device, and the opened camera is
+ // also cast to the second display, in this case, because the camera has configured the streams
+ // at this time, we can directly call injectCamera() to replace the internal camera with
+ // injection camera.
+ if (mOperatingMode >= 0 && injectionConfig.num_streams > 0
+ && injectionBufferSizes.size() > 0) {
+ ALOGV("%s: The opened camera is directly cast to the remote device.", __FUNCTION__);
+ res = mInjectionMethods->injectCamera(
+ injectionConfig, injectionBufferSizes);
+ if (res != OK) {
+ ALOGE("Can't finish inject camera process!");
+ return res;
+ }
+ }
+
+ return OK;
+}
+
+status_t Camera3Device::stopInjection() {
+ ALOGI("%s: Injection camera: stopInjection", __FUNCTION__);
+ Mutex::Autolock il(mInterfaceLock);
+ return mInjectionMethods->stopInjection();
+}
+
}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index e10da2c..f962c78 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -35,6 +35,7 @@
#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.7/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>
@@ -52,10 +53,18 @@
#include "device3/InFlightRequest.h"
#include "device3/Camera3OutputInterface.h"
#include "device3/Camera3OfflineSession.h"
+#include "device3/Camera3StreamInterface.h"
#include "utils/TagMonitor.h"
#include "utils/LatencyHistogram.h"
#include <camera_metadata_hidden.h>
+using android::camera3::camera_capture_request_t;
+using android::camera3::camera_jpeg_blob_t;
+using android::camera3::camera_request_template;
+using android::camera3::camera_stream_buffer_t;
+using android::camera3::camera_stream_configuration_t;
+using android::camera3::camera_stream_configuration_mode_t;
+using android::camera3::CAMERA_TEMPLATE_COUNT;
using android::camera3::OutputStreamInfo;
namespace android {
@@ -64,7 +73,6 @@
class Camera3Stream;
class Camera3ZslStream;
-class Camera3OutputStreamInterface;
class Camera3StreamInterface;
} // namespace camera3
@@ -122,21 +130,26 @@
// and finish the stream configuration before starting output streaming.
status_t createStream(sp<Surface> consumer,
uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
+ android_dataspace dataSpace, camera_stream_rotation_t rotation, int *id,
const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
std::vector<int> *surfaceIds = nullptr,
int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
- bool isShared = false, uint64_t consumerUsage = 0) override;
+ bool isShared = false, bool isMultiResolution = false,
+ uint64_t consumerUsage = 0) override;
+
status_t createStream(const std::vector<sp<Surface>>& consumers,
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
+ android_dataspace dataSpace, camera_stream_rotation_t rotation, int *id,
const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
std::vector<int> *surfaceIds = nullptr,
int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
- bool isShared = false, uint64_t consumerUsage = 0) override;
+ bool isShared = false, bool isMultiResolution = false,
+ uint64_t consumerUsage = 0) override;
status_t createInputStream(
- uint32_t width, uint32_t height, int format,
+ uint32_t width, uint32_t height, int format, bool isMultiResolution,
int *id) override;
status_t getStreamInfo(int id, StreamInfo *streamInfo) override;
@@ -146,14 +159,14 @@
status_t configureStreams(const CameraMetadata& sessionParams,
int operatingMode =
- static_cast<int>(hardware::camera::device::V3_2::StreamConfigurationMode::NORMAL_MODE))
- override;
+ camera_stream_configuration_mode_t::CAMERA_STREAM_CONFIGURATION_NORMAL_MODE) override;
status_t getInputBufferProducer(
sp<IGraphicBufferProducer> *producer) override;
void getOfflineStreamIds(std::vector<int> *offlineStreamIds) override;
- status_t createDefaultRequest(int templateId, CameraMetadata *request) override;
+ status_t createDefaultRequest(camera_request_template_t templateId,
+ CameraMetadata *request) override;
// Transitions to the idle state on success
status_t waitUntilDrained() override;
@@ -180,7 +193,7 @@
ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const override;
ssize_t getPointCloudBufferSize() const;
- ssize_t getRawOpaqueBufferSize(int32_t width, int32_t height) const;
+ ssize_t getRawOpaqueBufferSize(int32_t width, int32_t height, bool maxResolution) const;
// Methods called by subclasses
void notifyStatus(bool idle); // updates from StatusTracker
@@ -231,10 +244,37 @@
status_t setRotateAndCropAutoBehavior(
camera_metadata_enum_android_scaler_rotate_and_crop_t rotateAndCropValue);
+ /**
+ * Whether camera muting (producing black-only output) is supported.
+ *
+ * Calling setCameraMute(true) when this returns false will return an
+ * INVALID_OPERATION error.
+ */
+ bool supportsCameraMute();
+
+ /**
+ * Mute the camera.
+ *
+ * When muted, black image data is output on all output streams.
+ */
+ status_t setCameraMute(bool enabled);
+
// Get the status trackeer for the camera device
wp<camera3::StatusTracker> getStatusTracker() { return mStatusTracker; }
/**
+ * The injection camera session to replace the internal camera
+ * session.
+ */
+ status_t injectCamera(const String8& injectedCamId,
+ sp<CameraProviderManager> manager);
+
+ /**
+ * Stop the injection camera and restore to internal camera session.
+ */
+ status_t stopInjection();
+
+ /**
* Helper functions to map between framework and HIDL values
*/
static hardware::graphics::common::V1_0::PixelFormat mapToPixelFormat(int frameworkFormat);
@@ -242,9 +282,9 @@
android_dataspace dataSpace);
static hardware::camera::device::V3_2::BufferUsageFlags mapToConsumerUsage(uint64_t usage);
static hardware::camera::device::V3_2::StreamRotation mapToStreamRotation(
- camera3_stream_rotation_t rotation);
+ camera_stream_rotation_t rotation);
// Returns a negative error code if the passed-in operation mode is not valid.
- static status_t mapToStreamConfigurationMode(camera3_stream_configuration_mode_t operationMode,
+ static status_t mapToStreamConfigurationMode(camera_stream_configuration_mode_t operationMode,
/*out*/ hardware::camera::device::V3_2::StreamConfigurationMode *mode);
static int mapToFrameworkFormat(hardware::graphics::common::V1_0::PixelFormat pixelFormat);
static android_dataspace mapToFrameworkDataspace(
@@ -255,7 +295,6 @@
hardware::camera::device::V3_2::BufferUsageFlags usage);
private:
-
status_t disconnectImpl();
// internal typedefs
@@ -275,7 +314,7 @@
struct RequestTrigger;
// minimal jpeg buffer size: 256KB + blob header
- static const ssize_t kMinJpegBufferSize = 256 * 1024 + sizeof(camera3_jpeg_blob);
+ static const ssize_t kMinJpegBufferSize = 256 * 1024 + sizeof(camera_jpeg_blob_t);
// Constant to use for stream ID when one doesn't exist
static const int NO_STREAM = -1;
@@ -330,18 +369,25 @@
// Calls into the HAL interface
// Caller takes ownership of requestTemplate
- status_t constructDefaultRequestSettings(camera3_request_template_t templateId,
+ status_t constructDefaultRequestSettings(camera_request_template templateId,
/*out*/ camera_metadata_t **requestTemplate);
status_t configureStreams(const camera_metadata_t *sessionParams,
- /*inout*/ camera3_stream_configuration *config,
+ /*inout*/ camera_stream_configuration_t *config,
const std::vector<uint32_t>& bufferSizes);
+ // The injection camera configures the streams to hal.
+ status_t configureInjectedStreams(
+ const camera_metadata_t* sessionParams,
+ /*inout*/ camera_stream_configuration_t* config,
+ const std::vector<uint32_t>& bufferSizes,
+ const CameraMetadata& cameraCharacteristics);
+
// When the call succeeds, the ownership of acquire fences in requests is transferred to
// HalInterface. More specifically, the current implementation will send the fence to
// HAL process and close the FD in cameraserver process. When the call fails, the ownership
// of the acquire fence still belongs to the caller.
status_t processBatchCaptureRequests(
- std::vector<camera3_capture_request_t*>& requests,
+ std::vector<camera_capture_request_t*>& requests,
/*out*/uint32_t* numRequestProcessed);
status_t flush();
status_t dump(int fd);
@@ -397,12 +443,14 @@
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;
+ // Valid if ICameraDeviceSession is @3.7 or newer
+ sp<hardware::camera::device::V3_7::ICameraDeviceSession> mHidlSession_3_7;
std::shared_ptr<RequestMetadataQueue> mRequestMetadataQueue;
- // The output HIDL request still depends on input camera3_capture_request_t
- // Do not free input camera3_capture_request_t before output HIDL request
- status_t wrapAsHidlRequest(camera3_capture_request_t* in,
+ // The output HIDL request still depends on input camera_capture_request_t
+ // Do not free input camera_capture_request_t before output HIDL request
+ status_t wrapAsHidlRequest(camera_capture_request_t* in,
/*out*/hardware::camera::device::V3_2::CaptureRequest* out,
/*out*/std::vector<native_handle_t*>* handlesCreated,
/*out*/std::vector<std::pair<int32_t, int32_t>>* inflightBuffers);
@@ -442,15 +490,13 @@
bool mSupportNativeZoomRatio;
std::unordered_map<std::string, CameraMetadata> mPhysicalDeviceInfoMap;
- CameraMetadata mRequestTemplateCache[CAMERA3_TEMPLATE_COUNT];
+ CameraMetadata mRequestTemplateCache[CAMERA_TEMPLATE_COUNT];
struct Size {
uint32_t width;
uint32_t height;
explicit Size(uint32_t w = 0, uint32_t h = 0) : width(w), height(h){}
};
- // Map from format to size.
- Vector<Size> mSupportedOpaqueInputSizes;
enum Status {
STATUS_ERROR,
@@ -471,10 +517,15 @@
camera3::StreamSet mOutputStreams;
sp<camera3::Camera3Stream> mInputStream;
+ bool mIsInputStreamMultiResolution;
+ SessionStatsBuilder mSessionStatsBuilder;
+ // Map from stream group ID to physical cameras backing the stream group
+ std::map<int32_t, std::set<String8>> mGroupIdPhysicalCameraMap;
+
int mNextStreamId;
bool mNeedConfig;
- int mDummyStreamId;
+ int mFakeStreamId;
// Whether to send state updates upstream
// Pause when doing transparent reconfiguration
@@ -501,7 +552,8 @@
public:
PhysicalCameraSettingsList mSettingsList;
sp<camera3::Camera3Stream> mInputStream;
- camera3_stream_buffer_t mInputBuffer;
+ camera_stream_buffer_t mInputBuffer;
+ camera3::Size mInputBufferSize;
Vector<sp<camera3::Camera3OutputStreamInterface> >
mOutputStreams;
SurfaceMap mOutputSurfaces;
@@ -517,9 +569,16 @@
// overriding of ROTATE_AND_CROP value and adjustment of coordinates
// in several other controls in both the request and the result
bool mRotateAndCropAuto;
+ // Original value of TEST_PATTERN_MODE and DATA so that they can be
+ // restored when sensor muting is turned off
+ int32_t mOriginalTestPatternMode;
+ int32_t mOriginalTestPatternData[4];
+
// Whether this capture request has its zoom ratio set to 1.0x before
// the framework overrides it for camera HAL consumption.
bool mZoomRatioIs1x;
+ // The systemTime timestamp when the request is created.
+ nsecs_t mRequestTimeNs;
// Whether this capture request's distortion correction update has
@@ -538,7 +597,7 @@
status_t convertMetadataListToRequestListLocked(
const List<const PhysicalCameraSettingsList> &metadataList,
const std::list<const SurfaceMap> &surfaceMaps,
- bool repeating,
+ bool repeating, nsecs_t requestTimeNs,
/*out*/
RequestList *requestList);
@@ -681,15 +740,15 @@
void cancelStreamsConfigurationLocked();
/**
- * Add a dummy stream to the current stream set as a workaround for
+ * Add a fake stream to the current stream set as a workaround for
* not allowing 0 streams in the camera HAL spec.
*/
- status_t addDummyStreamLocked();
+ status_t addFakeStreamLocked();
/**
- * Remove a dummy stream if the current config includes real streams.
+ * Remove a fake stream if the current config includes real streams.
*/
- status_t tryRemoveDummyStreamLocked();
+ status_t tryRemoveFakeStreamLocked();
/**
* Set device into an error state due to some fatal failure, and set an
@@ -720,7 +779,7 @@
* Helper function to get the largest Jpeg resolution (in area)
* Return Size(0, 0) if static metatdata is invalid
*/
- Size getMaxJpegResolution() const;
+ camera3::Size getMaxJpegResolution() const;
/**
* Helper function to get the offset between MONOTONIC and BOOTTIME
@@ -765,7 +824,8 @@
* Call after stream (re)-configuration is completed.
*/
void configurationComplete(bool isConstrainedHighSpeed,
- const CameraMetadata& sessionParams);
+ const CameraMetadata& sessionParams,
+ const std::map<int32_t, std::set<String8>>& groupIdPhysicalCameraMap);
/**
* Set or clear the list of repeating requests. Does not block
@@ -858,6 +918,10 @@
status_t setRotateAndCropAutoBehavior(
camera_metadata_enum_android_scaler_rotate_and_crop_t rotateAndCropValue);
+ status_t setCameraMute(bool enabled);
+
+ status_t setHalInterface(sp<HalInterface> newHalInterface);
+
protected:
virtual bool threadLoop();
@@ -874,11 +938,15 @@
// HAL workaround: Make sure a trigger ID always exists if
// a trigger does
- status_t addDummyTriggerIds(const sp<CaptureRequest> &request);
+ status_t addFakeTriggerIds(const sp<CaptureRequest> &request);
// Override rotate_and_crop control if needed; returns true if the current value was changed
bool overrideAutoRotateAndCrop(const sp<CaptureRequest> &request);
+ // Override test_pattern control if needed for camera mute; returns true
+ // if the current value was changed
+ bool overrideTestPattern(const sp<CaptureRequest> &request);
+
static const nsecs_t kRequestTimeout = 50e6; // 50 ms
// TODO: does this need to be adjusted for long exposure requests?
@@ -887,8 +955,8 @@
// Used to prepare a batch of requests.
struct NextRequest {
sp<CaptureRequest> captureRequest;
- camera3_capture_request_t halRequest;
- Vector<camera3_stream_buffer_t> outputBuffers;
+ camera_capture_request_t halRequest;
+ Vector<camera_stream_buffer_t> outputBuffers;
bool submitted;
};
@@ -914,7 +982,7 @@
// Release physical camera settings and camera id resources.
void cleanupPhysicalSettings(sp<CaptureRequest> request,
- /*out*/camera3_capture_request_t *halRequest);
+ /*out*/camera_capture_request_t *halRequest);
// Pause handling
bool waitIfPaused();
@@ -961,6 +1029,7 @@
Condition mRequestSubmittedSignal;
RequestList mRequestQueue;
RequestList mRepeatingRequests;
+ bool mFirstRepeating;
// The next batch of requests being prepped for submission to the HAL, no longer
// on the request queue. Read-only even with mRequestLock held, outside
// of threadLoop
@@ -1000,6 +1069,8 @@
uint32_t mCurrentAfTriggerId;
uint32_t mCurrentPreCaptureTriggerId;
camera_metadata_enum_android_scaler_rotate_and_crop_t mRotateAndCropOverride;
+ bool mCameraMute;
+ bool mCameraMuteChanged;
int64_t mRepeatingLastFrameNumber;
@@ -1014,6 +1085,8 @@
Vector<int32_t> mSessionParamKeys;
CameraMetadata mLatestSessionParams;
+ std::map<int32_t, std::set<String8>> mGroupIdPhysicalCameraMap;
+
const bool mUseHalBufManager;
};
sp<RequestThread> mRequestThread;
@@ -1033,9 +1106,11 @@
status_t registerInFlight(uint32_t frameNumber,
int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
- bool callback, nsecs_t maxExpectedDuration, std::set<String8>& physicalCameraIds,
+ bool callback, nsecs_t maxExpectedDuration,
+ const std::set<std::set<String8>>& physicalCameraIds,
bool isStillCapture, bool isZslCapture, bool rotateAndCropAuto,
- const std::set<std::string>& cameraIdsWithZoom, const SurfaceMap& outputSurfaces);
+ const std::set<std::string>& cameraIdsWithZoom, const SurfaceMap& outputSurfaces,
+ nsecs_t requestTimeNs);
/**
* Tracking for idle detection
@@ -1264,6 +1339,79 @@
// Whether HAL supports offline processing capability.
bool mSupportOfflineProcessing = false;
+
+ // Whether the HAL supports camera muting via test pattern
+ bool mSupportCameraMute = false;
+
+ // Injection camera related methods.
+ class Camera3DeviceInjectionMethods : public virtual RefBase {
+ public:
+ Camera3DeviceInjectionMethods(wp<Camera3Device> parent);
+
+ ~Camera3DeviceInjectionMethods();
+
+ // Initialize the injection camera and generate an hal interface.
+ status_t injectionInitialize(
+ const String8& injectedCamId, sp<CameraProviderManager> manager,
+ const sp<
+ android::hardware::camera::device::V3_2 ::ICameraDeviceCallback>&
+ callback);
+
+ // Injection camera will replace the internal camera and configure streams
+ // when device is IDLE and request thread is paused.
+ status_t injectCamera(
+ camera3::camera_stream_configuration& injectionConfig,
+ std::vector<uint32_t>& injectionBufferSizes);
+
+ // Stop the injection camera and switch back to backup hal interface.
+ status_t stopInjection();
+
+ bool isInjecting();
+
+ const String8& getInjectedCamId() const;
+
+ void getInjectionConfig(/*out*/ camera3::camera_stream_configuration* injectionConfig,
+ /*out*/ std::vector<uint32_t>* injectionBufferSizes);
+
+ private:
+ // Configure the streams of injection camera, it need wait until the
+ // output streams are created and configured to the original camera before
+ // proceeding.
+ status_t injectionConfigureStreams(
+ camera3::camera_stream_configuration& injectionConfig,
+ std::vector<uint32_t>& injectionBufferSizes);
+
+ // Disconnect the injection camera and delete the hal interface.
+ void injectionDisconnectImpl();
+
+ // Use injection camera hal interface to replace and backup original
+ // camera hal interface.
+ status_t replaceHalInterface(sp<HalInterface> newHalInterface,
+ bool keepBackup);
+
+ wp<Camera3Device> mParent;
+
+ // Backup of the original camera hal interface.
+ sp<HalInterface> mBackupHalInterface;
+
+ // Generated injection camera hal interface.
+ sp<HalInterface> mInjectedCamHalInterface;
+
+ // Copy the configuration of the internal camera.
+ camera3::camera_stream_configuration mInjectionConfig;
+
+ // Copy the bufferSizes of the output streams of the internal camera.
+ std::vector<uint32_t> mInjectionBufferSizes;
+
+ // Synchronizes access to injection camera between initialize and
+ // disconnect.
+ Mutex mInjectionLock;
+
+ // The injection camera ID.
+ String8 mInjectedCamId;
+ };
+ sp<Camera3DeviceInjectionMethods> mInjectionMethods;
+
}; // class Camera3Device
}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3DeviceInjectionMethods.cpp b/services/camera/libcameraservice/device3/Camera3DeviceInjectionMethods.cpp
new file mode 100644
index 0000000..f145dac
--- /dev/null
+++ b/services/camera/libcameraservice/device3/Camera3DeviceInjectionMethods.cpp
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Camera3DeviceInjectionMethods"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include "common/CameraProviderManager.h"
+#include "device3/Camera3Device.h"
+
+namespace android {
+
+using hardware::camera::device::V3_2::ICameraDeviceSession;
+
+Camera3Device::Camera3DeviceInjectionMethods::Camera3DeviceInjectionMethods(
+ wp<Camera3Device> parent)
+ : mParent(parent) {
+ ALOGV("%s: Created injection camera methods", __FUNCTION__);
+}
+
+Camera3Device::Camera3DeviceInjectionMethods::~Camera3DeviceInjectionMethods() {
+ ALOGV("%s: Removed injection camera methods", __FUNCTION__);
+ injectionDisconnectImpl();
+}
+
+status_t Camera3Device::Camera3DeviceInjectionMethods::injectionInitialize(
+ const String8& injectedCamId, sp<CameraProviderManager> manager,
+ const sp<android::hardware::camera::device::V3_2::ICameraDeviceCallback>&
+ callback) {
+ ATRACE_CALL();
+ Mutex::Autolock lock(mInjectionLock);
+
+ if (manager == nullptr) {
+ ALOGE("%s: manager does not exist!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ sp<Camera3Device> parent = mParent.promote();
+ if (parent == nullptr) {
+ ALOGE("%s: parent does not exist!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ mInjectedCamId = injectedCamId;
+ sp<ICameraDeviceSession> session;
+ ATRACE_BEGIN("Injection CameraHal::openSession");
+ status_t res = manager->openSession(injectedCamId.string(), callback,
+ /*out*/ &session);
+ ATRACE_END();
+ if (res != OK) {
+ ALOGE("Injection camera could not open camera session: %s (%d)",
+ strerror(-res), res);
+ return res;
+ }
+
+ std::shared_ptr<RequestMetadataQueue> queue;
+ auto requestQueueRet =
+ session->getCaptureRequestMetadataQueue([&queue](const auto& descriptor) {
+ queue = std::make_shared<RequestMetadataQueue>(descriptor);
+ if (!queue->isValid() || queue->availableToWrite() <= 0) {
+ ALOGE("Injection camera HAL returns empty request metadata fmq, not "
+ "use it");
+ queue = nullptr;
+ // don't use the queue onwards.
+ }
+ });
+ if (!requestQueueRet.isOk()) {
+ ALOGE("Injection camera transaction error when getting request metadata fmq: "
+ "%s, not use it", requestQueueRet.description().c_str());
+ return DEAD_OBJECT;
+ }
+
+ std::unique_ptr<ResultMetadataQueue>& resQueue = parent->mResultMetadataQueue;
+ auto resultQueueRet = session->getCaptureResultMetadataQueue(
+ [&resQueue](const auto& descriptor) {
+ resQueue = std::make_unique<ResultMetadataQueue>(descriptor);
+ if (!resQueue->isValid() || resQueue->availableToWrite() <= 0) {
+ ALOGE("Injection camera HAL returns empty result metadata fmq, not use "
+ "it");
+ resQueue = nullptr;
+ // Don't use the resQueue onwards.
+ }
+ });
+ if (!resultQueueRet.isOk()) {
+ ALOGE("Injection camera transaction error when getting result metadata queue "
+ "from camera session: %s", resultQueueRet.description().c_str());
+ return DEAD_OBJECT;
+ }
+ IF_ALOGV() {
+ session->interfaceChain(
+ [](::android::hardware::hidl_vec<::android::hardware::hidl_string>
+ interfaceChain) {
+ ALOGV("Injection camera session interface chain:");
+ for (const auto& iface : interfaceChain) {
+ ALOGV(" %s", iface.c_str());
+ }
+ });
+ }
+
+ ALOGV("%s: Injection camera interface = new HalInterface()", __FUNCTION__);
+ mInjectedCamHalInterface =
+ new HalInterface(session, queue, parent->mUseHalBufManager,
+ parent->mSupportOfflineProcessing);
+ if (mInjectedCamHalInterface == nullptr) {
+ ALOGE("%s: mInjectedCamHalInterface does not exist!", __FUNCTION__);
+ return DEAD_OBJECT;
+ }
+
+ return OK;
+}
+
+status_t Camera3Device::Camera3DeviceInjectionMethods::injectCamera(
+ camera3::camera_stream_configuration& injectionConfig,
+ std::vector<uint32_t>& injectionBufferSizes) {
+ status_t res = NO_ERROR;
+ mInjectionConfig = injectionConfig;
+ mInjectionBufferSizes = injectionBufferSizes;
+
+ if (mInjectedCamHalInterface == nullptr) {
+ ALOGE("%s: mInjectedCamHalInterface does not exist!", __FUNCTION__);
+ return DEAD_OBJECT;
+ }
+
+ sp<Camera3Device> parent = mParent.promote();
+ if (parent == nullptr) {
+ ALOGE("%s: parent does not exist!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ nsecs_t maxExpectedDuration = parent->getExpectedInFlightDuration();
+ bool wasActive = false;
+ if (parent->mStatus == STATUS_ACTIVE) {
+ ALOGV("%s: Let the device be IDLE and the request thread is paused",
+ __FUNCTION__);
+ parent->mPauseStateNotify = true;
+ res = parent->internalPauseAndWaitLocked(maxExpectedDuration);
+ if (res != OK) {
+ ALOGE("%s: Can't pause captures to inject camera!", __FUNCTION__);
+ return res;
+ }
+ wasActive = true;
+ }
+
+ ALOGV("%s: Injection camera: replaceHalInterface", __FUNCTION__);
+ res = replaceHalInterface(mInjectedCamHalInterface, true);
+ if (res != OK) {
+ ALOGE("%s: Failed to replace the new HalInterface!", __FUNCTION__);
+ injectionDisconnectImpl();
+ return res;
+ }
+
+ res = parent->mRequestThread->setHalInterface(mInjectedCamHalInterface);
+ if (res != OK) {
+ ALOGE("%s: Failed to set new HalInterface in RequestThread!", __FUNCTION__);
+ replaceHalInterface(mBackupHalInterface, false);
+ injectionDisconnectImpl();
+ return res;
+ }
+
+ parent->mNeedConfig = true;
+ res = injectionConfigureStreams(injectionConfig, injectionBufferSizes);
+ parent->mNeedConfig = false;
+ if (res != OK) {
+ ALOGE("Can't injectionConfigureStreams device for streams: %d: %s "
+ "(%d)", parent->mNextStreamId, strerror(-res), res);
+ replaceHalInterface(mBackupHalInterface, false);
+ injectionDisconnectImpl();
+ return res;
+ }
+
+ if (wasActive) {
+ ALOGV("%s: Restarting activity to inject camera", __FUNCTION__);
+ // Reuse current operating mode and session parameters for new stream
+ // config.
+ parent->internalUpdateStatusLocked(STATUS_ACTIVE);
+ }
+
+ return OK;
+}
+
+status_t Camera3Device::Camera3DeviceInjectionMethods::stopInjection() {
+ status_t res = NO_ERROR;
+
+ sp<Camera3Device> parent = mParent.promote();
+ if (parent == nullptr) {
+ ALOGE("%s: parent does not exist!", __FUNCTION__);
+ return DEAD_OBJECT;
+ }
+
+ nsecs_t maxExpectedDuration = parent->getExpectedInFlightDuration();
+ bool wasActive = false;
+ if (parent->mStatus == STATUS_ACTIVE) {
+ ALOGV("%s: Let the device be IDLE and the request thread is paused",
+ __FUNCTION__);
+ parent->mPauseStateNotify = true;
+ res = parent->internalPauseAndWaitLocked(maxExpectedDuration);
+ if (res != OK) {
+ ALOGE("%s: Can't pause captures to stop injection!", __FUNCTION__);
+ return res;
+ }
+ wasActive = true;
+ }
+
+ res = replaceHalInterface(mBackupHalInterface, false);
+ if (res != OK) {
+ ALOGE("%s: Failed to restore the backup HalInterface!", __FUNCTION__);
+ injectionDisconnectImpl();
+ return res;
+ }
+ injectionDisconnectImpl();
+
+ if (wasActive) {
+ ALOGV("%s: Restarting activity to stop injection", __FUNCTION__);
+ // Reuse current operating mode and session parameters for new stream
+ // config.
+ parent->internalUpdateStatusLocked(STATUS_ACTIVE);
+ }
+
+ return OK;
+}
+
+bool Camera3Device::Camera3DeviceInjectionMethods::isInjecting() {
+ if (mInjectedCamHalInterface == nullptr) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+const String8& Camera3Device::Camera3DeviceInjectionMethods::getInjectedCamId()
+ const {
+ return mInjectedCamId;
+}
+
+void Camera3Device::Camera3DeviceInjectionMethods::getInjectionConfig(
+ /*out*/ camera3::camera_stream_configuration* injectionConfig,
+ /*out*/ std::vector<uint32_t>* injectionBufferSizes) {
+ if (injectionConfig == nullptr || injectionBufferSizes == nullptr) {
+ ALOGE("%s: Injection configuration arguments must not be null!", __FUNCTION__);
+ return;
+ }
+
+ *injectionConfig = mInjectionConfig;
+ *injectionBufferSizes = mInjectionBufferSizes;
+}
+
+
+status_t Camera3Device::Camera3DeviceInjectionMethods::injectionConfigureStreams(
+ camera3::camera_stream_configuration& injectionConfig,
+ std::vector<uint32_t>& injectionBufferSizes) {
+ ATRACE_CALL();
+ status_t res = NO_ERROR;
+
+ sp<Camera3Device> parent = mParent.promote();
+ if (parent == nullptr) {
+ ALOGE("%s: parent does not exist!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ if (parent->mOperatingMode < 0) {
+ ALOGE("Invalid operating mode: %d", parent->mOperatingMode);
+ return BAD_VALUE;
+ }
+
+ // Start configuring the streams
+ ALOGV("%s: Injection camera %s: Starting stream configuration", __FUNCTION__,
+ mInjectedCamId.string());
+
+ parent->mPreparerThread->pause();
+
+ // Do the HAL configuration; will potentially touch stream
+ // max_buffers, usage, and priv fields, as well as data_space and format
+ // fields for IMPLEMENTATION_DEFINED formats.
+
+ const camera_metadata_t* sessionBuffer = parent->mSessionParams.getAndLock();
+ res = mInjectedCamHalInterface->configureInjectedStreams(
+ sessionBuffer, &injectionConfig, injectionBufferSizes,
+ parent->mDeviceInfo);
+ parent->mSessionParams.unlock(sessionBuffer);
+
+ if (res == BAD_VALUE) {
+ // HAL rejected this set of streams as unsupported, clean up config
+ // attempt and return to unconfigured state
+ ALOGE("Set of requested outputs not supported by HAL");
+ parent->cancelStreamsConfigurationLocked();
+ return BAD_VALUE;
+ } else if (res != OK) {
+ // Some other kind of error from configure_streams - this is not
+ // expected
+ ALOGE("Unable to configure streams with HAL: %s (%d)", strerror(-res),
+ res);
+ return res;
+ }
+
+ for (size_t i = 0; i < parent->mOutputStreams.size(); i++) {
+ sp<camera3::Camera3OutputStreamInterface> outputStream =
+ parent->mOutputStreams[i];
+ mInjectedCamHalInterface->onStreamReConfigured(outputStream->getId());
+ }
+
+ // Request thread needs to know to avoid using repeat-last-settings protocol
+ // across configure_streams() calls
+ parent->mRequestThread->configurationComplete(
+ parent->mIsConstrainedHighSpeedConfiguration, parent->mSessionParams,
+ parent->mGroupIdPhysicalCameraMap);
+
+ parent->internalUpdateStatusLocked(STATUS_CONFIGURED);
+
+ ALOGV("%s: Injection camera %s: Stream configuration complete", __FUNCTION__,
+ mInjectedCamId.string());
+
+ auto rc = parent->mPreparerThread->resume();
+
+ if (rc != OK) {
+ ALOGE("%s: Injection camera %s: Preparer thread failed to resume!",
+ __FUNCTION__, mInjectedCamId.string());
+ return rc;
+ }
+
+ return OK;
+}
+
+void Camera3Device::Camera3DeviceInjectionMethods::injectionDisconnectImpl() {
+ ATRACE_CALL();
+ ALOGI("%s: Injection camera disconnect", __FUNCTION__);
+
+ mBackupHalInterface = nullptr;
+ HalInterface* interface = nullptr;
+ {
+ Mutex::Autolock lock(mInjectionLock);
+ if (mInjectedCamHalInterface != nullptr) {
+ interface = mInjectedCamHalInterface.get();
+ // Call close without internal mutex held, as the HAL close may need
+ // to wait on assorted callbacks,etc, to complete before it can
+ // return.
+ }
+ }
+
+ if (interface != nullptr) {
+ interface->close();
+ }
+
+ {
+ Mutex::Autolock lock(mInjectionLock);
+ if (mInjectedCamHalInterface != nullptr) {
+ mInjectedCamHalInterface->clear();
+ mInjectedCamHalInterface = nullptr;
+ }
+ }
+}
+
+status_t Camera3Device::Camera3DeviceInjectionMethods::replaceHalInterface(
+ sp<HalInterface> newHalInterface, bool keepBackup) {
+ Mutex::Autolock lock(mInjectionLock);
+ if (newHalInterface.get() == nullptr) {
+ ALOGE("%s: The newHalInterface does not exist, to stop replacing.",
+ __FUNCTION__);
+ return DEAD_OBJECT;
+ }
+
+ sp<Camera3Device> parent = mParent.promote();
+ if (parent == nullptr) {
+ ALOGE("%s: parent does not exist!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ if (keepBackup && mBackupHalInterface == nullptr) {
+ mBackupHalInterface = parent->mInterface;
+ } else if (!keepBackup) {
+ mBackupHalInterface = nullptr;
+ }
+ parent->mInterface = newHalInterface;
+
+ return OK;
+}
+
+}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
deleted file mode 100644
index b637160..0000000
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2014-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 "Camera3-DummyStream"
-#define ATRACE_TAG ATRACE_TAG_CAMERA
-//#define LOG_NDEBUG 0
-
-#include <utils/Log.h>
-#include <utils/Trace.h>
-#include "Camera3DummyStream.h"
-
-namespace android {
-
-namespace camera3 {
-
-const String8 Camera3DummyStream::DUMMY_ID;
-
-Camera3DummyStream::Camera3DummyStream(int id) :
- Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, DUMMY_WIDTH, DUMMY_HEIGHT,
- /*maxSize*/0, DUMMY_FORMAT, DUMMY_DATASPACE, DUMMY_ROTATION,
- DUMMY_ID) {
-
-}
-
-Camera3DummyStream::~Camera3DummyStream() {
-
-}
-
-status_t Camera3DummyStream::getBufferLocked(camera3_stream_buffer *,
- const std::vector<size_t>&) {
- ATRACE_CALL();
- ALOGE("%s: Stream %d: Dummy stream cannot produce buffers!", __FUNCTION__, mId);
- return INVALID_OPERATION;
-}
-
-status_t Camera3DummyStream::returnBufferLocked(
- const camera3_stream_buffer &,
- nsecs_t, const std::vector<size_t>&) {
- ATRACE_CALL();
- ALOGE("%s: Stream %d: Dummy stream cannot return buffers!", __FUNCTION__, mId);
- return INVALID_OPERATION;
-}
-
-status_t Camera3DummyStream::returnBufferCheckedLocked(
- const camera3_stream_buffer &,
- nsecs_t,
- bool,
- const std::vector<size_t>&,
- /*out*/
- sp<Fence>*) {
- ATRACE_CALL();
- ALOGE("%s: Stream %d: Dummy stream cannot return buffers!", __FUNCTION__, mId);
- return INVALID_OPERATION;
-}
-
-void Camera3DummyStream::dump(int fd, const Vector<String16> &args) const {
- (void) args;
- String8 lines;
- lines.appendFormat(" Stream[%d]: Dummy\n", mId);
- write(fd, lines.string(), lines.size());
-
- Camera3IOStreamBase::dump(fd, args);
-}
-
-status_t Camera3DummyStream::setTransform(int) {
- ATRACE_CALL();
- // Do nothing
- return OK;
-}
-
-status_t Camera3DummyStream::detachBuffer(sp<GraphicBuffer>* buffer, int* fenceFd) {
- (void) buffer;
- (void) fenceFd;
- // Do nothing
- return OK;
-}
-
-status_t Camera3DummyStream::configureQueueLocked() {
- // Do nothing
- return OK;
-}
-
-status_t Camera3DummyStream::disconnectLocked() {
- mState = (mState == STATE_IN_RECONFIG) ? STATE_IN_CONFIG
- : STATE_CONSTRUCTED;
- return OK;
-}
-
-status_t Camera3DummyStream::getEndpointUsage(uint64_t *usage) const {
- *usage = DUMMY_USAGE;
- return OK;
-}
-
-bool Camera3DummyStream::isVideoStream() const {
- return false;
-}
-
-bool Camera3DummyStream::isConsumerConfigurationDeferred(size_t /*surface_id*/) const {
- return false;
-}
-
-status_t Camera3DummyStream::dropBuffers(bool /*dropping*/) {
- return OK;
-}
-
-const String8& Camera3DummyStream::getPhysicalCameraId() const {
- return DUMMY_ID;
-}
-
-status_t Camera3DummyStream::setConsumers(const std::vector<sp<Surface>>& /*consumers*/) {
- ALOGE("%s: Stream %d: Dummy stream doesn't support set consumer surface!",
- __FUNCTION__, mId);
- return INVALID_OPERATION;
-}
-
-status_t Camera3DummyStream::updateStream(const std::vector<sp<Surface>> &/*outputSurfaces*/,
- const std::vector<OutputStreamInfo> &/*outputInfo*/,
- const std::vector<size_t> &/*removedSurfaceIds*/,
- KeyedVector<sp<Surface>, size_t> * /*outputMap*/) {
- ALOGE("%s: this method is not supported!", __FUNCTION__);
- return INVALID_OPERATION;
-}
-
-}; // namespace camera3
-
-}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h
deleted file mode 100644
index 4b67ea5..0000000
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2014-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 ANDROID_SERVERS_CAMERA3_DUMMY_STREAM_H
-#define ANDROID_SERVERS_CAMERA3_DUMMY_STREAM_H
-
-#include <utils/RefBase.h>
-#include <gui/Surface.h>
-
-#include "Camera3Stream.h"
-#include "Camera3IOStreamBase.h"
-#include "Camera3OutputStreamInterface.h"
-
-namespace android {
-namespace camera3 {
-
-/**
- * A dummy output stream class, to be used as a placeholder when no valid
- * streams are configured by the client.
- * This is necessary because camera HAL v3.2 or older disallow configuring
- * 0 output streams, while the public camera2 API allows for it.
- */
-class Camera3DummyStream :
- public Camera3IOStreamBase,
- public Camera3OutputStreamInterface {
-
- public:
- /**
- * Set up a dummy stream; doesn't actually connect to anything, and uses
- * a default dummy format and size.
- */
- explicit Camera3DummyStream(int id);
-
- virtual ~Camera3DummyStream();
-
- /**
- * Camera3Stream interface
- */
-
- virtual void dump(int fd, const Vector<String16> &args) const;
-
- status_t setTransform(int transform);
-
- virtual status_t detachBuffer(sp<GraphicBuffer>* buffer, int* fenceFd);
-
- /**
- * Drop buffers for stream of streamId if dropping is true. If dropping is false, do not
- * drop buffers for stream of streamId.
- */
- virtual status_t dropBuffers(bool /*dropping*/) override;
-
- /**
- * Query the physical camera id for the output stream.
- */
- virtual const String8& getPhysicalCameraId() const override;
-
- /**
- * Return if this output stream is for video encoding.
- */
- bool isVideoStream() const;
-
- /**
- * Return if the consumer configuration of this stream is deferred.
- */
- virtual bool isConsumerConfigurationDeferred(size_t surface_id) const;
-
- /**
- * Set the consumer surfaces to the output stream.
- */
- virtual status_t setConsumers(const std::vector<sp<Surface>>& consumers);
-
- /**
- * Query the output surface id.
- */
- virtual ssize_t getSurfaceId(const sp<Surface> &/*surface*/) { return 0; }
-
- virtual status_t getUniqueSurfaceIds(const std::vector<size_t>&,
- /*out*/std::vector<size_t>*) { return INVALID_OPERATION; };
-
- /**
- * Update the stream output surfaces.
- */
- virtual status_t updateStream(const std::vector<sp<Surface>> &outputSurfaces,
- const std::vector<OutputStreamInfo> &outputInfo,
- const std::vector<size_t> &removedSurfaceIds,
- KeyedVector<sp<Surface>, size_t> *outputMap/*out*/);
-
- protected:
-
- /**
- * Note that we release the lock briefly in this function
- */
- virtual status_t returnBufferCheckedLocked(
- const camera3_stream_buffer &buffer,
- nsecs_t timestamp,
- bool output,
- const std::vector<size_t>& surface_ids,
- /*out*/
- sp<Fence> *releaseFenceOut);
-
- virtual status_t disconnectLocked();
-
- private:
-
- // Default dummy parameters; 320x240 is a required size for all devices,
- // otherwise act like a SurfaceView would.
- static const int DUMMY_WIDTH = 320;
- static const int DUMMY_HEIGHT = 240;
- static const int DUMMY_FORMAT = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
- static const android_dataspace DUMMY_DATASPACE = HAL_DATASPACE_UNKNOWN;
- static const camera3_stream_rotation_t DUMMY_ROTATION = CAMERA3_STREAM_ROTATION_0;
- static const uint64_t DUMMY_USAGE = GRALLOC_USAGE_HW_COMPOSER;
- static const String8 DUMMY_ID;
-
- /**
- * Internal Camera3Stream interface
- */
- virtual status_t getBufferLocked(camera3_stream_buffer *buffer,
- const std::vector<size_t>& surface_ids = std::vector<size_t>());
- virtual status_t returnBufferLocked(
- const camera3_stream_buffer &buffer,
- nsecs_t timestamp, const std::vector<size_t>& surface_ids);
-
- virtual status_t configureQueueLocked();
-
- virtual status_t getEndpointUsage(uint64_t *usage) const;
-
-}; // class Camera3DummyStream
-
-} // namespace camera3
-
-} // namespace android
-
-#endif
diff --git a/services/camera/libcameraservice/device3/Camera3FakeStream.cpp b/services/camera/libcameraservice/device3/Camera3FakeStream.cpp
new file mode 100644
index 0000000..8cc6833
--- /dev/null
+++ b/services/camera/libcameraservice/device3/Camera3FakeStream.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2014-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 "Camera3-FakeStream"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include "Camera3FakeStream.h"
+
+namespace android {
+
+namespace camera3 {
+
+const String8 Camera3FakeStream::FAKE_ID;
+
+Camera3FakeStream::Camera3FakeStream(int id) :
+ Camera3IOStreamBase(id, CAMERA_STREAM_OUTPUT, FAKE_WIDTH, FAKE_HEIGHT,
+ /*maxSize*/0, FAKE_FORMAT, FAKE_DATASPACE, FAKE_ROTATION,
+ FAKE_ID, std::unordered_set<int32_t>{ANDROID_SENSOR_PIXEL_MODE_DEFAULT}) {
+
+}
+
+Camera3FakeStream::~Camera3FakeStream() {
+
+}
+
+status_t Camera3FakeStream::getBufferLocked(camera_stream_buffer *,
+ const std::vector<size_t>&) {
+ ATRACE_CALL();
+ ALOGE("%s: Stream %d: Fake stream cannot produce buffers!", __FUNCTION__, mId);
+ return INVALID_OPERATION;
+}
+
+status_t Camera3FakeStream::returnBufferLocked(
+ const camera_stream_buffer &,
+ nsecs_t, const std::vector<size_t>&) {
+ ATRACE_CALL();
+ ALOGE("%s: Stream %d: Fake stream cannot return buffers!", __FUNCTION__, mId);
+ return INVALID_OPERATION;
+}
+
+status_t Camera3FakeStream::returnBufferCheckedLocked(
+ const camera_stream_buffer &,
+ nsecs_t,
+ bool,
+ const std::vector<size_t>&,
+ /*out*/
+ sp<Fence>*) {
+ ATRACE_CALL();
+ ALOGE("%s: Stream %d: Fake stream cannot return buffers!", __FUNCTION__, mId);
+ return INVALID_OPERATION;
+}
+
+void Camera3FakeStream::dump(int fd, const Vector<String16> &args) const {
+ (void) args;
+ String8 lines;
+ lines.appendFormat(" Stream[%d]: Fake\n", mId);
+ write(fd, lines.string(), lines.size());
+
+ Camera3IOStreamBase::dump(fd, args);
+}
+
+status_t Camera3FakeStream::setTransform(int) {
+ ATRACE_CALL();
+ // Do nothing
+ return OK;
+}
+
+status_t Camera3FakeStream::detachBuffer(sp<GraphicBuffer>* buffer, int* fenceFd) {
+ (void) buffer;
+ (void) fenceFd;
+ // Do nothing
+ return OK;
+}
+
+status_t Camera3FakeStream::configureQueueLocked() {
+ // Do nothing
+ return OK;
+}
+
+status_t Camera3FakeStream::disconnectLocked() {
+ mState = (mState == STATE_IN_RECONFIG) ? STATE_IN_CONFIG
+ : STATE_CONSTRUCTED;
+ return OK;
+}
+
+status_t Camera3FakeStream::getEndpointUsage(uint64_t *usage) const {
+ *usage = FAKE_USAGE;
+ return OK;
+}
+
+bool Camera3FakeStream::isVideoStream() const {
+ return false;
+}
+
+bool Camera3FakeStream::isConsumerConfigurationDeferred(size_t /*surface_id*/) const {
+ return false;
+}
+
+status_t Camera3FakeStream::dropBuffers(bool /*dropping*/) {
+ return OK;
+}
+
+const String8& Camera3FakeStream::getPhysicalCameraId() const {
+ return FAKE_ID;
+}
+
+status_t Camera3FakeStream::setConsumers(const std::vector<sp<Surface>>& /*consumers*/) {
+ ALOGE("%s: Stream %d: Fake stream doesn't support set consumer surface!",
+ __FUNCTION__, mId);
+ return INVALID_OPERATION;
+}
+
+status_t Camera3FakeStream::updateStream(const std::vector<sp<Surface>> &/*outputSurfaces*/,
+ const std::vector<OutputStreamInfo> &/*outputInfo*/,
+ const std::vector<size_t> &/*removedSurfaceIds*/,
+ KeyedVector<sp<Surface>, size_t> * /*outputMap*/) {
+ ALOGE("%s: this method is not supported!", __FUNCTION__);
+ return INVALID_OPERATION;
+}
+
+status_t Camera3FakeStream::setBatchSize(size_t /*batchSize*/) {
+ ALOGE("%s: this method is not supported!", __FUNCTION__);
+ return INVALID_OPERATION;
+}
+
+}; // namespace camera3
+
+}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3FakeStream.h b/services/camera/libcameraservice/device3/Camera3FakeStream.h
new file mode 100644
index 0000000..914ccbf
--- /dev/null
+++ b/services/camera/libcameraservice/device3/Camera3FakeStream.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2014-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 ANDROID_SERVERS_CAMERA3_FAKE_STREAM_H
+#define ANDROID_SERVERS_CAMERA3_FAKE_STREAM_H
+
+#include <utils/RefBase.h>
+#include <gui/Surface.h>
+
+#include "Camera3Stream.h"
+#include "Camera3IOStreamBase.h"
+#include "Camera3OutputStreamInterface.h"
+
+namespace android {
+namespace camera3 {
+
+/**
+ * A fake output stream class, to be used as a placeholder when no valid
+ * streams are configured by the client.
+ * This is necessary because camera HAL v3.2 or older disallow configuring
+ * 0 output streams, while the public camera2 API allows for it.
+ */
+class Camera3FakeStream :
+ public Camera3IOStreamBase,
+ public Camera3OutputStreamInterface {
+
+ public:
+ /**
+ * Set up a fake stream; doesn't actually connect to anything, and uses
+ * a default fake format and size.
+ */
+ explicit Camera3FakeStream(int id);
+
+ virtual ~Camera3FakeStream();
+
+ /**
+ * Camera3Stream interface
+ */
+
+ virtual void dump(int fd, const Vector<String16> &args) const;
+
+ status_t setTransform(int transform);
+
+ virtual status_t detachBuffer(sp<GraphicBuffer>* buffer, int* fenceFd);
+
+ /**
+ * Drop buffers for stream of streamId if dropping is true. If dropping is false, do not
+ * drop buffers for stream of streamId.
+ */
+ virtual status_t dropBuffers(bool /*dropping*/) override;
+
+ /**
+ * Query the physical camera id for the output stream.
+ */
+ virtual const String8& getPhysicalCameraId() const override;
+
+ /**
+ * Return if this output stream is for video encoding.
+ */
+ bool isVideoStream() const;
+
+ /**
+ * Return if the consumer configuration of this stream is deferred.
+ */
+ virtual bool isConsumerConfigurationDeferred(size_t surface_id) const;
+
+ /**
+ * Set the consumer surfaces to the output stream.
+ */
+ virtual status_t setConsumers(const std::vector<sp<Surface>>& consumers);
+
+ /**
+ * Query the output surface id.
+ */
+ virtual ssize_t getSurfaceId(const sp<Surface> &/*surface*/) { return 0; }
+
+ virtual status_t getUniqueSurfaceIds(const std::vector<size_t>&,
+ /*out*/std::vector<size_t>*) { return INVALID_OPERATION; };
+
+ /**
+ * Update the stream output surfaces.
+ */
+ virtual status_t updateStream(const std::vector<sp<Surface>> &outputSurfaces,
+ const std::vector<OutputStreamInfo> &outputInfo,
+ const std::vector<size_t> &removedSurfaceIds,
+ KeyedVector<sp<Surface>, size_t> *outputMap/*out*/);
+
+ virtual status_t setBatchSize(size_t batchSize) override;
+
+ protected:
+
+ /**
+ * Note that we release the lock briefly in this function
+ */
+ virtual status_t returnBufferCheckedLocked(
+ const camera_stream_buffer &buffer,
+ nsecs_t timestamp,
+ bool output,
+ const std::vector<size_t>& surface_ids,
+ /*out*/
+ sp<Fence> *releaseFenceOut);
+
+ virtual status_t disconnectLocked();
+
+ private:
+
+ // Default fake parameters; 320x240 is a required size for all devices,
+ // otherwise act like a SurfaceView would.
+ static const int FAKE_WIDTH = 320;
+ static const int FAKE_HEIGHT = 240;
+ static const int FAKE_FORMAT = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
+ static const android_dataspace FAKE_DATASPACE = HAL_DATASPACE_UNKNOWN;
+ static const camera_stream_rotation_t FAKE_ROTATION = CAMERA_STREAM_ROTATION_0;
+ static const uint64_t FAKE_USAGE = GRALLOC_USAGE_HW_COMPOSER;
+ static const String8 FAKE_ID;
+
+ /**
+ * Internal Camera3Stream interface
+ */
+ virtual status_t getBufferLocked(camera_stream_buffer *buffer,
+ const std::vector<size_t>& surface_ids = std::vector<size_t>());
+ virtual status_t returnBufferLocked(
+ const camera_stream_buffer &buffer,
+ nsecs_t timestamp, const std::vector<size_t>& surface_ids);
+
+ virtual status_t configureQueueLocked();
+
+ virtual status_t getEndpointUsage(uint64_t *usage) const;
+
+}; // class Camera3FakeStream
+
+} // namespace camera3
+
+} // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index bda2961..0204d49 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -29,13 +29,15 @@
namespace camera3 {
-Camera3IOStreamBase::Camera3IOStreamBase(int id, camera3_stream_type_t type,
+Camera3IOStreamBase::Camera3IOStreamBase(int id, camera_stream_type_t type,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation,
- const String8& physicalCameraId, int setId) :
+ android_dataspace dataSpace, camera_stream_rotation_t rotation,
+ const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ int setId, bool isMultiResolution) :
Camera3Stream(id, type,
width, height, maxSize, format, dataSpace, rotation,
- physicalCameraId, setId),
+ physicalCameraId, sensorPixelModesUsed, setId, isMultiResolution),
mTotalBufferCount(0),
mHandoutTotalBufferCount(0),
mHandoutOutputBufferCount(0),
@@ -77,13 +79,13 @@
lines.appendFormat(" State: %d\n", mState);
lines.appendFormat(" Dims: %d x %d, format 0x%x, dataspace 0x%x\n",
- camera3_stream::width, camera3_stream::height,
- camera3_stream::format, camera3_stream::data_space);
+ camera_stream::width, camera_stream::height,
+ camera_stream::format, camera_stream::data_space);
lines.appendFormat(" Max size: %zu\n", mMaxSize);
lines.appendFormat(" Combined usage: %" PRIu64 ", max HAL buffers: %d\n",
- mUsage | consumerUsage, camera3_stream::max_buffers);
- if (strlen(camera3_stream::physical_camera_id) > 0) {
- lines.appendFormat(" Physical camera id: %s\n", camera3_stream::physical_camera_id);
+ mUsage | consumerUsage, camera_stream::max_buffers);
+ if (strlen(camera_stream::physical_camera_id) > 0) {
+ lines.appendFormat(" Physical camera id: %s\n", camera_stream::physical_camera_id);
}
lines.appendFormat(" Frames produced: %d, last timestamp: %" PRId64 " ns\n",
mFrameCount, mLastTimestamp);
@@ -150,11 +152,11 @@
return OK;
}
-void Camera3IOStreamBase::handoutBufferLocked(camera3_stream_buffer &buffer,
+void Camera3IOStreamBase::handoutBufferLocked(camera_stream_buffer &buffer,
buffer_handle_t *handle,
int acquireFence,
int releaseFence,
- camera3_buffer_status_t status,
+ camera_buffer_status_t status,
bool output) {
/**
* Note that all fences are now owned by HAL.
@@ -220,7 +222,7 @@
}
status_t Camera3IOStreamBase::returnAnyBufferLocked(
- const camera3_stream_buffer &buffer,
+ const camera_stream_buffer &buffer,
nsecs_t timestamp,
bool output,
const std::vector<size_t>& surface_ids) {
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
index 448379c..90c8a7b 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
@@ -32,11 +32,12 @@
class Camera3IOStreamBase :
public Camera3Stream {
protected:
- Camera3IOStreamBase(int id, camera3_stream_type_t type,
+ Camera3IOStreamBase(int id, camera_stream_type_t type,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation,
+ android_dataspace dataSpace, camera_stream_rotation_t rotation,
const String8& physicalCameraId,
- int setId = CAMERA3_STREAM_SET_ID_INVALID);
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ int setId = CAMERA3_STREAM_SET_ID_INVALID, bool isMultiResolution = false);
public:
@@ -48,6 +49,7 @@
virtual void dump(int fd, const Vector<String16> &args) const;
+ int getMaxTotalBuffers() const { return mTotalBufferCount; }
protected:
size_t mTotalBufferCount;
// sum of input and output buffers that are currently acquired by HAL
@@ -63,13 +65,13 @@
sp<Fence> mCombinedFence;
status_t returnAnyBufferLocked(
- const camera3_stream_buffer &buffer,
+ const camera_stream_buffer &buffer,
nsecs_t timestamp,
bool output,
const std::vector<size_t>& surface_ids = std::vector<size_t>());
virtual status_t returnBufferCheckedLocked(
- const camera3_stream_buffer &buffer,
+ const camera_stream_buffer &buffer,
nsecs_t timestamp,
bool output,
const std::vector<size_t>& surface_ids,
@@ -99,11 +101,11 @@
// Hand out the buffer to a native location,
// incrementing the internal refcount and dequeued buffer count
- void handoutBufferLocked(camera3_stream_buffer &buffer,
+ void handoutBufferLocked(camera_stream_buffer &buffer,
buffer_handle_t *handle,
int acquire_fence,
int release_fence,
- camera3_buffer_status_t status,
+ camera_buffer_status_t status,
bool output);
}; // class Camera3IOStreamBase
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index cb59a76..6d8317b 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -27,13 +27,14 @@
namespace camera3 {
-const String8 Camera3InputStream::DUMMY_ID;
+const String8 Camera3InputStream::FAKE_ID;
Camera3InputStream::Camera3InputStream(int id,
uint32_t width, uint32_t height, int format) :
- Camera3IOStreamBase(id, CAMERA3_STREAM_INPUT, width, height, /*maxSize*/0,
- format, HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0,
- DUMMY_ID) {
+ Camera3IOStreamBase(id, CAMERA_STREAM_INPUT, width, height, /*maxSize*/0,
+ format, HAL_DATASPACE_UNKNOWN, CAMERA_STREAM_ROTATION_0,
+ FAKE_ID,
+ std::unordered_set<int32_t>{ANDROID_SENSOR_PIXEL_MODE_DEFAULT}) {
if (format == HAL_PIXEL_FORMAT_BLOB) {
ALOGE("%s: Bad format, BLOB not supported", __FUNCTION__);
@@ -46,10 +47,14 @@
}
status_t Camera3InputStream::getInputBufferLocked(
- camera3_stream_buffer *buffer) {
+ camera_stream_buffer *buffer, Size *size) {
ATRACE_CALL();
status_t res;
+ if (size == nullptr) {
+ ALOGE("%s: size must not be null", __FUNCTION__);
+ return BAD_VALUE;
+ }
// FIXME: will not work in (re-)registration
if (mState == STATE_IN_CONFIG || mState == STATE_IN_RECONFIG) {
ALOGE("%s: Stream %d: Buffer registration for input streams"
@@ -77,16 +82,18 @@
return res;
}
+ size->width = bufferItem.mGraphicBuffer->getWidth();
+ size->height = bufferItem.mGraphicBuffer->getHeight();
+
anb = bufferItem.mGraphicBuffer->getNativeBuffer();
assert(anb != NULL);
fenceFd = bufferItem.mFence->dup();
-
/**
* FenceFD now owned by HAL except in case of error,
* in which case we reassign it to acquire_fence
*/
handoutBufferLocked(*buffer, &(anb->handle), /*acquireFence*/fenceFd,
- /*releaseFence*/-1, CAMERA3_BUFFER_STATUS_OK, /*output*/false);
+ /*releaseFence*/-1, CAMERA_BUFFER_STATUS_OK, /*output*/false);
mBuffersInFlight.push_back(bufferItem);
mFrameCount++;
@@ -96,7 +103,7 @@
}
status_t Camera3InputStream::returnBufferCheckedLocked(
- const camera3_stream_buffer &buffer,
+ const camera_stream_buffer &buffer,
nsecs_t timestamp,
bool output,
const std::vector<size_t>&,
@@ -134,7 +141,7 @@
return INVALID_OPERATION;
}
- if (buffer.status == CAMERA3_BUFFER_STATUS_ERROR) {
+ if (buffer.status == CAMERA_BUFFER_STATUS_ERROR) {
if (buffer.release_fence != -1) {
ALOGE("%s: Stream %d: HAL should not set release_fence(%d) when "
"there is an error", __FUNCTION__, mId, buffer.release_fence);
@@ -144,7 +151,7 @@
/**
* Reassign release fence as the acquire fence incase of error
*/
- const_cast<camera3_stream_buffer*>(&buffer)->release_fence =
+ const_cast<camera_stream_buffer*>(&buffer)->release_fence =
buffer.acquire_fence;
}
@@ -165,7 +172,7 @@
}
status_t Camera3InputStream::returnInputBufferLocked(
- const camera3_stream_buffer &buffer) {
+ const camera_stream_buffer &buffer) {
ATRACE_CALL();
return returnAnyBufferLocked(buffer, /*timestamp*/0, /*output*/false);
@@ -224,7 +231,7 @@
}
assert(mMaxSize == 0);
- assert(camera3_stream::format != HAL_PIXEL_FORMAT_BLOB);
+ assert(camera_stream::format != HAL_PIXEL_FORMAT_BLOB);
mHandoutTotalBufferCount = 0;
mFrameCount = 0;
@@ -244,14 +251,14 @@
}
size_t minBufs = static_cast<size_t>(minUndequeuedBuffers);
- if (camera3_stream::max_buffers == 0) {
+ if (camera_stream::max_buffers == 0) {
ALOGE("%s: %d: HAL sets max_buffer to 0. Must be at least 1.",
__FUNCTION__, __LINE__);
return INVALID_OPERATION;
}
/*
- * We promise never to 'acquire' more than camera3_stream::max_buffers
+ * We promise never to 'acquire' more than camera_stream::max_buffers
* at any one time.
*
* Boost the number up to meet the minimum required buffer count.
@@ -259,8 +266,8 @@
* (Note that this sets consumer-side buffer count only,
* and not the sum of producer+consumer side as in other camera streams).
*/
- mTotalBufferCount = camera3_stream::max_buffers > minBufs ?
- camera3_stream::max_buffers : minBufs;
+ mTotalBufferCount = camera_stream::max_buffers > minBufs ?
+ camera_stream::max_buffers : minBufs;
// TODO: somehow set the total buffer count when producer connects?
mConsumer = new BufferItemConsumer(consumer, mUsage,
@@ -272,17 +279,17 @@
mConsumer->setBufferFreedListener(this);
}
- res = mConsumer->setDefaultBufferSize(camera3_stream::width,
- camera3_stream::height);
+ res = mConsumer->setDefaultBufferSize(camera_stream::width,
+ camera_stream::height);
if (res != OK) {
ALOGE("%s: Stream %d: Could not set buffer dimensions %dx%d",
- __FUNCTION__, mId, camera3_stream::width, camera3_stream::height);
+ __FUNCTION__, mId, camera_stream::width, camera_stream::height);
return res;
}
- res = mConsumer->setDefaultBufferFormat(camera3_stream::format);
+ res = mConsumer->setDefaultBufferFormat(camera_stream::format);
if (res != OK) {
ALOGE("%s: Stream %d: Could not set buffer format %d",
- __FUNCTION__, mId, camera3_stream::format);
+ __FUNCTION__, mId, camera_stream::format);
return res;
}
@@ -298,8 +305,8 @@
void Camera3InputStream::onBufferFreed(const wp<GraphicBuffer>& gb) {
const sp<GraphicBuffer> buffer = gb.promote();
if (buffer != nullptr) {
- camera3_stream_buffer streamBuffer =
- {nullptr, &buffer->handle, 0, -1, -1};
+ camera_stream_buffer streamBuffer =
+ {nullptr, &buffer->handle, CAMERA_BUFFER_STATUS_OK, -1, -1};
// Check if this buffer is outstanding.
if (isOutstandingBuffer(streamBuffer)) {
ALOGV("%s: Stream %d: Trying to free a buffer that is still being "
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.h b/services/camera/libcameraservice/device3/Camera3InputStream.h
index 97a627a..46221d1 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.h
@@ -53,13 +53,13 @@
sp<IGraphicBufferProducer> mProducer;
Vector<BufferItem> mBuffersInFlight;
- static const String8 DUMMY_ID;
+ static const String8 FAKE_ID;
/**
* Camera3IOStreamBase
*/
virtual status_t returnBufferCheckedLocked(
- const camera3_stream_buffer &buffer,
+ const camera_stream_buffer &buffer,
nsecs_t timestamp,
bool output,
const std::vector<size_t>& surface_ids,
@@ -70,9 +70,9 @@
* Camera3Stream interface
*/
- virtual status_t getInputBufferLocked(camera3_stream_buffer *buffer);
+ virtual status_t getInputBufferLocked(camera_stream_buffer *buffer, Size *size);
virtual status_t returnInputBufferLocked(
- const camera3_stream_buffer &buffer);
+ const camera_stream_buffer &buffer);
virtual status_t getInputBufferProducerLocked(
sp<IGraphicBufferProducer> *producer);
virtual status_t disconnectLocked();
diff --git a/services/camera/libcameraservice/device3/Camera3OfflineSession.cpp b/services/camera/libcameraservice/device3/Camera3OfflineSession.cpp
index 95f9633..a7e64ce 100644
--- a/services/camera/libcameraservice/device3/Camera3OfflineSession.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OfflineSession.cpp
@@ -176,7 +176,7 @@
FlushInflightReqStates states {
mId, mOfflineReqsLock, mOfflineReqs, mUseHalBufManager,
- listener, *this, mBufferRecords, *this};
+ listener, *this, mBufferRecords, *this, mSessionStatsBuilder};
camera3::flushInflightRequests(states);
@@ -260,7 +260,8 @@
mUseHalBufManager, mUsePartialResult, mNeedFixupMonochromeTags,
mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
mResultMetadataQueue, mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
- mTagMonitor, mInputStream, mOutputStreams, listener, *this, *this, mBufferRecords
+ mTagMonitor, mInputStream, mOutputStreams, mSessionStatsBuilder, listener, *this, *this,
+ mBufferRecords
};
std::lock_guard<std::mutex> lock(mProcessCaptureResultLock);
@@ -299,7 +300,8 @@
mUseHalBufManager, mUsePartialResult, mNeedFixupMonochromeTags,
mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
mResultMetadataQueue, mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
- mTagMonitor, mInputStream, mOutputStreams, listener, *this, *this, mBufferRecords
+ mTagMonitor, mInputStream, mOutputStreams, mSessionStatsBuilder, listener, *this, *this,
+ mBufferRecords
};
std::lock_guard<std::mutex> lock(mProcessCaptureResultLock);
@@ -333,7 +335,8 @@
mUseHalBufManager, mUsePartialResult, mNeedFixupMonochromeTags,
mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
mResultMetadataQueue, mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
- mTagMonitor, mInputStream, mOutputStreams, listener, *this, *this, mBufferRecords
+ mTagMonitor, mInputStream, mOutputStreams, mSessionStatsBuilder, listener, *this, *this,
+ mBufferRecords
};
for (const auto& msg : msgs) {
camera3::notify(states, msg);
@@ -353,7 +356,7 @@
}
RequestBufferStates states {
- mId, mRequestBufferInterfaceLock, mUseHalBufManager, mOutputStreams,
+ mId, mRequestBufferInterfaceLock, mUseHalBufManager, mOutputStreams, mSessionStatsBuilder,
*this, mBufferRecords, *this};
camera3::requestStreamBuffers(states, bufReqs, _hidl_cb);
return hardware::Void();
@@ -370,7 +373,7 @@
}
ReturnBufferStates states {
- mId, mUseHalBufManager, mOutputStreams, mBufferRecords};
+ mId, mUseHalBufManager, mOutputStreams, mSessionStatsBuilder, mBufferRecords};
camera3::returnStreamBuffers(states, buffers);
return hardware::Void();
}
diff --git a/services/camera/libcameraservice/device3/Camera3OfflineSession.h b/services/camera/libcameraservice/device3/Camera3OfflineSession.h
index c4c7a85..5581964 100644
--- a/services/camera/libcameraservice/device3/Camera3OfflineSession.h
+++ b/services/camera/libcameraservice/device3/Camera3OfflineSession.h
@@ -36,7 +36,6 @@
#include "device3/RotateAndCropMapper.h"
#include "device3/ZoomRatioMapper.h"
#include "utils/TagMonitor.h"
-#include "utils/LatencyHistogram.h"
#include <camera_metadata_hidden.h>
namespace android {
@@ -209,6 +208,7 @@
sp<camera3::Camera3Stream> mInputStream;
camera3::StreamSet mOutputStreams;
camera3::BufferRecords mBufferRecords;
+ SessionStatsBuilder mSessionStatsBuilder;
std::mutex mOfflineReqsLock;
camera3::InFlightRequestMap mOfflineReqs;
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 01ca006..221bebb 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -18,8 +18,15 @@
#define ATRACE_TAG ATRACE_TAG_CAMERA
//#define LOG_NDEBUG 0
+#include <ctime>
+#include <fstream>
+
+#include <android-base/unique_fd.h>
+#include <ui/GraphicBuffer.h>
#include <utils/Log.h>
#include <utils/Trace.h>
+
+#include "api1/client2/JpegProcessor.h"
#include "Camera3OutputStream.h"
#include "utils/TraceHFR.h"
@@ -35,12 +42,13 @@
Camera3OutputStream::Camera3OutputStream(int id,
sp<Surface> consumer,
uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation,
+ android_dataspace dataSpace, camera_stream_rotation_t rotation,
nsecs_t timestampOffset, const String8& physicalCameraId,
- int setId) :
- Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ int setId, bool isMultiResolution) :
+ Camera3IOStreamBase(id, CAMERA_STREAM_OUTPUT, width, height,
/*maxSize*/0, format, dataSpace, rotation,
- physicalCameraId, setId),
+ physicalCameraId, sensorPixelModesUsed, setId, isMultiResolution),
mConsumer(consumer),
mTransform(0),
mTraceFirstBuffer(true),
@@ -62,10 +70,13 @@
Camera3OutputStream::Camera3OutputStream(int id,
sp<Surface> consumer,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation,
- nsecs_t timestampOffset, const String8& physicalCameraId, int setId) :
- Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height, maxSize,
- format, dataSpace, rotation, physicalCameraId, setId),
+ android_dataspace dataSpace, camera_stream_rotation_t rotation,
+ nsecs_t timestampOffset, const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ int setId, bool isMultiResolution) :
+ Camera3IOStreamBase(id, CAMERA_STREAM_OUTPUT, width, height, maxSize,
+ format, dataSpace, rotation, physicalCameraId, sensorPixelModesUsed,
+ setId, isMultiResolution),
mConsumer(consumer),
mTransform(0),
mTraceFirstBuffer(true),
@@ -94,11 +105,13 @@
Camera3OutputStream::Camera3OutputStream(int id,
uint32_t width, uint32_t height, int format,
uint64_t consumerUsage, android_dataspace dataSpace,
- camera3_stream_rotation_t rotation, nsecs_t timestampOffset,
- const String8& physicalCameraId, int setId) :
- Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height,
+ camera_stream_rotation_t rotation, nsecs_t timestampOffset,
+ const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ int setId, bool isMultiResolution) :
+ Camera3IOStreamBase(id, CAMERA_STREAM_OUTPUT, width, height,
/*maxSize*/0, format, dataSpace, rotation,
- physicalCameraId, setId),
+ physicalCameraId, sensorPixelModesUsed, setId, isMultiResolution),
mConsumer(nullptr),
mTransform(0),
mTraceFirstBuffer(true),
@@ -114,7 +127,7 @@
mState = STATE_ERROR;
}
- // Sanity check for the consumer usage flag.
+ // Validation check for the consumer usage flag.
if ((consumerUsage & GraphicBuffer::USAGE_HW_TEXTURE) == 0 &&
(consumerUsage & GraphicBuffer::USAGE_HW_COMPOSER) == 0) {
ALOGE("%s: Deferred consumer usage flag is illegal %" PRIu64 "!",
@@ -127,18 +140,19 @@
mBufferProducerListener = new BufferProducerListener(this, needsReleaseNotify);
}
-Camera3OutputStream::Camera3OutputStream(int id, camera3_stream_type_t type,
+Camera3OutputStream::Camera3OutputStream(int id, camera_stream_type_t type,
uint32_t width, uint32_t height,
int format,
android_dataspace dataSpace,
- camera3_stream_rotation_t rotation,
+ camera_stream_rotation_t rotation,
const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
uint64_t consumerUsage, nsecs_t timestampOffset,
- int setId) :
+ int setId, bool isMultiResolution) :
Camera3IOStreamBase(id, type, width, height,
/*maxSize*/0,
format, dataSpace, rotation,
- physicalCameraId, setId),
+ physicalCameraId, sensorPixelModesUsed, setId, isMultiResolution),
mTransform(0),
mTraceFirstBuffer(true),
mUseMonoTimestamp(false),
@@ -159,7 +173,7 @@
disconnectLocked();
}
-status_t Camera3OutputStream::getBufferLocked(camera3_stream_buffer *buffer,
+status_t Camera3OutputStream::getBufferLocked(camera_stream_buffer *buffer,
const std::vector<size_t>&) {
ATRACE_HFR_CALL();
@@ -177,11 +191,70 @@
* in which case we reassign it to acquire_fence
*/
handoutBufferLocked(*buffer, &(anb->handle), /*acquireFence*/fenceFd,
- /*releaseFence*/-1, CAMERA3_BUFFER_STATUS_OK, /*output*/true);
+ /*releaseFence*/-1, CAMERA_BUFFER_STATUS_OK, /*output*/true);
return OK;
}
+status_t Camera3OutputStream::getBuffersLocked(std::vector<OutstandingBuffer>* outBuffers) {
+ status_t res;
+
+ if ((res = getBufferPreconditionCheckLocked()) != OK) {
+ return res;
+ }
+
+ if (mUseBufferManager) {
+ ALOGE("%s: stream %d is managed by buffer manager and does not support batch operation",
+ __FUNCTION__, mId);
+ return INVALID_OPERATION;
+ }
+
+ sp<Surface> consumer = mConsumer;
+ /**
+ * Release the lock briefly to avoid deadlock for below scenario:
+ * Thread 1: StreamingProcessor::startStream -> Camera3Stream::isConfiguring().
+ * This thread acquired StreamingProcessor lock and try to lock Camera3Stream lock.
+ * Thread 2: Camera3Stream::returnBuffer->StreamingProcessor::onFrameAvailable().
+ * This thread acquired Camera3Stream lock and bufferQueue lock, and try to lock
+ * StreamingProcessor lock.
+ * Thread 3: Camera3Stream::getBuffer(). This thread acquired Camera3Stream lock
+ * and try to lock bufferQueue lock.
+ * Then there is circular locking dependency.
+ */
+ mLock.unlock();
+
+ size_t numBuffersRequested = outBuffers->size();
+ std::vector<Surface::BatchBuffer> buffers(numBuffersRequested);
+
+ nsecs_t dequeueStart = systemTime(SYSTEM_TIME_MONOTONIC);
+ res = consumer->dequeueBuffers(&buffers);
+ nsecs_t dequeueEnd = systemTime(SYSTEM_TIME_MONOTONIC);
+ mDequeueBufferLatency.add(dequeueStart, dequeueEnd);
+
+ mLock.lock();
+
+ if (res != OK) {
+ if (shouldLogError(res, mState)) {
+ ALOGE("%s: Stream %d: Can't dequeue %zu output buffers: %s (%d)",
+ __FUNCTION__, mId, numBuffersRequested, strerror(-res), res);
+ }
+ checkRetAndSetAbandonedLocked(res);
+ return res;
+ }
+ checkRemovedBuffersLocked();
+
+ /**
+ * FenceFD now owned by HAL except in case of error,
+ * in which case we reassign it to acquire_fence
+ */
+ for (size_t i = 0; i < numBuffersRequested; i++) {
+ handoutBufferLocked(*(outBuffers->at(i).outBuffer),
+ &(buffers[i].buffer->handle), /*acquireFence*/buffers[i].fenceFd,
+ /*releaseFence*/-1, CAMERA_BUFFER_STATUS_OK, /*output*/true);
+ }
+ return OK;
+}
+
status_t Camera3OutputStream::queueBufferToConsumer(sp<ANativeWindow>& consumer,
ANativeWindowBuffer* buffer, int anwReleaseFence,
const std::vector<size_t>&) {
@@ -189,10 +262,14 @@
}
status_t Camera3OutputStream::returnBufferLocked(
- const camera3_stream_buffer &buffer,
+ const camera_stream_buffer &buffer,
nsecs_t timestamp, const std::vector<size_t>& surface_ids) {
ATRACE_HFR_CALL();
+ if (mHandoutTotalBufferCount == 1) {
+ returnPrefetchedBuffersLocked();
+ }
+
status_t res = returnAnyBufferLocked(buffer, timestamp, /*output*/true, surface_ids);
if (res != OK) {
@@ -206,7 +283,7 @@
}
status_t Camera3OutputStream::returnBufferCheckedLocked(
- const camera3_stream_buffer &buffer,
+ const camera_stream_buffer &buffer,
nsecs_t timestamp,
bool output,
const std::vector<size_t>& surface_ids,
@@ -236,11 +313,11 @@
/**
* Return buffer back to ANativeWindow
*/
- if (buffer.status == CAMERA3_BUFFER_STATUS_ERROR || mDropBuffers || timestamp == 0) {
+ if (buffer.status == CAMERA_BUFFER_STATUS_ERROR || mDropBuffers || timestamp == 0) {
// Cancel buffer
if (mDropBuffers) {
ALOGV("%s: Dropping a frame for stream %d.", __FUNCTION__, mId);
- } else if (buffer.status == CAMERA3_BUFFER_STATUS_ERROR) {
+ } else if (buffer.status == CAMERA_BUFFER_STATUS_ERROR) {
ALOGV("%s: A frame is dropped for stream %d due to buffer error.", __FUNCTION__, mId);
} else {
ALOGE("%s: Stream %d: timestamp shouldn't be 0", __FUNCTION__, mId);
@@ -260,7 +337,7 @@
mBufferProducerListener->onBufferReleased();
}
} else {
- if (mTraceFirstBuffer && (stream_type == CAMERA3_STREAM_OUTPUT)) {
+ if (mTraceFirstBuffer && (stream_type == CAMERA_STREAM_OUTPUT)) {
{
char traceLog[48];
snprintf(traceLog, sizeof(traceLog), "Stream %d: first full buffer\n", mId);
@@ -279,6 +356,12 @@
__FUNCTION__, mId, strerror(-res), res);
return res;
}
+ // If this is a JPEG output, and image dump mask is set, save image to
+ // disk.
+ if (getFormat() == HAL_PIXEL_FORMAT_BLOB && getDataSpace() == HAL_DATASPACE_V0_JFIF &&
+ mImageDumpMask) {
+ dumpImageToDisk(timestamp, anwBuffer, anwReleaseFence);
+ }
res = queueBufferToConsumer(currentConsumer, anwBuffer, anwReleaseFence, surface_ids);
if (shouldLogError(res, state)) {
@@ -290,7 +373,7 @@
// Once a valid buffer has been returned to the queue, can no longer
// dequeue all buffers for preallocation.
- if (buffer.status != CAMERA3_BUFFER_STATUS_ERROR) {
+ if (buffer.status != CAMERA_BUFFER_STATUS_ERROR) {
mStreamUnpreparable = true;
}
@@ -411,7 +494,7 @@
if (mMaxSize == 0) {
// For buffers of known size
res = native_window_set_buffers_dimensions(mConsumer.get(),
- camera3_stream::width, camera3_stream::height);
+ camera_stream::width, camera_stream::height);
} else {
// For buffers with bounded size
res = native_window_set_buffers_dimensions(mConsumer.get(),
@@ -420,23 +503,23 @@
if (res != OK) {
ALOGE("%s: Unable to configure stream buffer dimensions"
" %d x %d (maxSize %zu) for stream %d",
- __FUNCTION__, camera3_stream::width, camera3_stream::height,
+ __FUNCTION__, camera_stream::width, camera_stream::height,
mMaxSize, mId);
return res;
}
res = native_window_set_buffers_format(mConsumer.get(),
- camera3_stream::format);
+ camera_stream::format);
if (res != OK) {
ALOGE("%s: Unable to configure stream buffer format %#x for stream %d",
- __FUNCTION__, camera3_stream::format, mId);
+ __FUNCTION__, camera_stream::format, mId);
return res;
}
res = native_window_set_buffers_data_space(mConsumer.get(),
- camera3_stream::data_space);
+ camera_stream::data_space);
if (res != OK) {
ALOGE("%s: Unable to configure stream dataspace %#x for stream %d",
- __FUNCTION__, camera3_stream::data_space, mId);
+ __FUNCTION__, camera_stream::data_space, mId);
return res;
}
@@ -451,14 +534,14 @@
}
ALOGV("%s: Consumer wants %d buffers, HAL wants %d", __FUNCTION__,
- maxConsumerBuffers, camera3_stream::max_buffers);
- if (camera3_stream::max_buffers == 0) {
+ maxConsumerBuffers, camera_stream::max_buffers);
+ if (camera_stream::max_buffers == 0) {
ALOGE("%s: Camera HAL requested max_buffer count: %d, requires at least 1",
- __FUNCTION__, camera3_stream::max_buffers);
+ __FUNCTION__, camera_stream::max_buffers);
return INVALID_OPERATION;
}
- mTotalBufferCount = maxConsumerBuffers + camera3_stream::max_buffers;
+ mTotalBufferCount = maxConsumerBuffers + camera_stream::max_buffers;
mHandoutTotalBufferCount = 0;
mFrameCount = 0;
mLastTimestamp = 0;
@@ -494,10 +577,12 @@
!(isConsumedByHWComposer() || isConsumedByHWTexture())) {
uint64_t consumerUsage = 0;
getEndpointUsage(&consumerUsage);
+ uint32_t width = (mMaxSize == 0) ? getWidth() : mMaxSize;
+ uint32_t height = (mMaxSize == 0) ? getHeight() : 1;
StreamInfo streamInfo(
- getId(), getStreamSetId(), getWidth(), getHeight(), getFormat(), getDataSpace(),
+ getId(), getStreamSetId(), width, height, getFormat(), getDataSpace(),
mUsage | consumerUsage, mTotalBufferCount,
- /*isConfigured*/true);
+ /*isConfigured*/true, isMultiResolution());
wp<Camera3OutputStream> weakThis(this);
res = mBufferManager->registerStream(weakThis,
streamInfo);
@@ -528,7 +613,8 @@
if (mUseBufferManager) {
sp<GraphicBuffer> gb;
- res = mBufferManager->getBufferForStream(getId(), getStreamSetId(), &gb, fenceFd);
+ res = mBufferManager->getBufferForStream(getId(), getStreamSetId(),
+ isMultiResolution(), &gb, fenceFd);
if (res == OK) {
// Attach this buffer to the bufferQueue: the buffer will be in dequeue state after a
// successful return.
@@ -567,11 +653,46 @@
* and try to lock bufferQueue lock.
* Then there is circular locking dependency.
*/
- sp<ANativeWindow> currentConsumer = mConsumer;
+ sp<Surface> consumer = mConsumer;
+ size_t remainingBuffers = camera_stream::max_buffers - mHandoutTotalBufferCount;
mLock.unlock();
+ std::unique_lock<std::mutex> batchLock(mBatchLock);
nsecs_t dequeueStart = systemTime(SYSTEM_TIME_MONOTONIC);
- res = currentConsumer->dequeueBuffer(currentConsumer.get(), anb, fenceFd);
+
+ if (mBatchSize == 1) {
+ sp<ANativeWindow> anw = consumer;
+ res = anw->dequeueBuffer(anw.get(), anb, fenceFd);
+ } else {
+ res = OK;
+ if (mBatchedBuffers.size() == 0) {
+ size_t batchSize = mBatchSize;
+ if (remainingBuffers == 0) {
+ ALOGE("%s: cannot get buffer while all buffers are handed out", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ if (batchSize > remainingBuffers) {
+ batchSize = remainingBuffers;
+ }
+ // Refill batched buffers
+ mBatchedBuffers.resize(batchSize);
+ res = consumer->dequeueBuffers(&mBatchedBuffers);
+ if (res != OK) {
+ ALOGE("%s: batch dequeueBuffers call failed! %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ mBatchedBuffers.clear();
+ }
+ }
+
+ if (res == OK) {
+ // Dispatch batch buffers
+ *anb = mBatchedBuffers.back().buffer;
+ *fenceFd = mBatchedBuffers.back().fenceFd;
+ mBatchedBuffers.pop_back();
+ }
+ }
+ batchLock.unlock();
+
nsecs_t dequeueEnd = systemTime(SYSTEM_TIME_MONOTONIC);
mDequeueBufferLatency.add(dequeueStart, dequeueEnd);
@@ -582,7 +703,8 @@
sp<GraphicBuffer> gb;
res = mBufferManager->getBufferForStream(
- getId(), getStreamSetId(), &gb, fenceFd, /*noFreeBuffer*/true);
+ getId(), getStreamSetId(), isMultiResolution(),
+ &gb, fenceFd, /*noFreeBuffer*/true);
if (res == OK) {
// Attach this buffer to the bufferQueue: the buffer will be in dequeue state after
@@ -629,7 +751,8 @@
onBuffersRemovedLocked(removedBuffers);
if (notifyBufferManager && mUseBufferManager && removedBuffers.size() > 0) {
- mBufferManager->onBuffersRemoved(getId(), getStreamSetId(), removedBuffers.size());
+ mBufferManager->onBuffersRemoved(getId(), getStreamSetId(), isMultiResolution(),
+ removedBuffers.size());
}
}
}
@@ -665,6 +788,8 @@
return OK;
}
+ returnPrefetchedBuffersLocked();
+
ALOGV("%s: disconnecting stream %d from native window", __FUNCTION__, getId());
res = native_window_api_disconnect(mConsumer.get(),
@@ -689,7 +814,7 @@
// Since device is already idle, there is no getBuffer call to buffer manager, unregister the
// stream at this point should be safe.
if (mUseBufferManager) {
- res = mBufferManager->unregisterStream(getId(), getStreamSetId());
+ res = mBufferManager->unregisterStream(getId(), getStreamSetId(), isMultiResolution());
if (res != OK) {
ALOGE("%s: Unable to unregister stream %d from buffer manager "
"(error %d %s)", __FUNCTION__, mId, res, strerror(-res));
@@ -750,7 +875,7 @@
uint64_t u = 0;
res = native_window_get_consumer_usage(static_cast<ANativeWindow*>(surface.get()), &u);
- applyZSLUsageQuirk(camera3_stream::format, &u);
+ applyZSLUsageQuirk(camera_stream::format, &u);
*usage = u;
return res;
}
@@ -801,7 +926,8 @@
ALOGV("Stream %d: Buffer released", stream->getId());
bool shouldFreeBuffer = false;
status_t res = stream->mBufferManager->onBufferReleased(
- stream->getId(), stream->getStreamSetId(), &shouldFreeBuffer);
+ stream->getId(), stream->getStreamSetId(), stream->isMultiResolution(),
+ &shouldFreeBuffer);
if (res != OK) {
ALOGE("%s: signaling buffer release to buffer manager failed: %s (%d).", __FUNCTION__,
strerror(-res), res);
@@ -814,7 +940,7 @@
stream->detachBufferLocked(&buffer, /*fenceFd*/ nullptr);
if (buffer.get() != nullptr) {
stream->mBufferManager->notifyBufferRemoved(
- stream->getId(), stream->getStreamSetId());
+ stream->getId(), stream->getStreamSetId(), stream->isMultiResolution());
}
}
}
@@ -832,7 +958,7 @@
stream->onBuffersRemovedLocked(buffers);
if (stream->mUseBufferManager) {
stream->mBufferManager->onBuffersRemoved(stream->getId(),
- stream->getStreamSetId(), buffers.size());
+ stream->getStreamSetId(), stream->isMultiResolution(), buffers.size());
}
ALOGV("Stream %d: %zu Buffers discarded.", stream->getId(), buffers.size());
}
@@ -957,6 +1083,95 @@
return (usage & GRALLOC_USAGE_HW_TEXTURE) != 0;
}
+void Camera3OutputStream::dumpImageToDisk(nsecs_t timestamp,
+ ANativeWindowBuffer* anwBuffer, int fence) {
+ // Deriver output file name
+ std::string fileExtension = "jpg";
+ char imageFileName[64];
+ time_t now = time(0);
+ tm *localTime = localtime(&now);
+ snprintf(imageFileName, sizeof(imageFileName), "IMG_%4d%02d%02d_%02d%02d%02d_%" PRId64 ".%s",
+ 1900 + localTime->tm_year, localTime->tm_mon, localTime->tm_mday,
+ localTime->tm_hour, localTime->tm_min, localTime->tm_sec,
+ timestamp, fileExtension.c_str());
+
+ // Lock the image for CPU read
+ sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(anwBuffer);
+ void* mapped = nullptr;
+ base::unique_fd fenceFd(dup(fence));
+ status_t res = graphicBuffer->lockAsync(GraphicBuffer::USAGE_SW_READ_OFTEN, &mapped,
+ fenceFd.get());
+ if (res != OK) {
+ ALOGE("%s: Failed to lock the buffer: %s (%d)", __FUNCTION__, strerror(-res), res);
+ return;
+ }
+
+ // Figure out actual file size
+ auto actualJpegSize = android::camera2::JpegProcessor::findJpegSize((uint8_t*)mapped, mMaxSize);
+ if (actualJpegSize == 0) {
+ actualJpegSize = mMaxSize;
+ }
+
+ // Output image data to file
+ std::string filePath = "/data/misc/cameraserver/";
+ filePath += imageFileName;
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ graphicBuffer->unlock();
+ return;
+ }
+ imageFile.write((const char*)mapped, actualJpegSize);
+
+ graphicBuffer->unlock();
+}
+
+status_t Camera3OutputStream::setBatchSize(size_t batchSize) {
+ Mutex::Autolock l(mLock);
+ std::lock_guard<std::mutex> lock(mBatchLock);
+ if (batchSize == 0) {
+ ALOGE("%s: invalid batch size 0", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ if (mUseBufferManager) {
+ ALOGE("%s: batch operation is not supported with buffer manager", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ if (!isVideoStream()) {
+ ALOGE("%s: batch operation is not supported with non-video stream", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ if (batchSize != mBatchSize) {
+ if (mBatchedBuffers.size() != 0) {
+ ALOGE("%s: change batch size from %zu to %zu dynamically is not supported",
+ __FUNCTION__, mBatchSize, batchSize);
+ return INVALID_OPERATION;
+ }
+
+ if (camera_stream::max_buffers < batchSize) {
+ ALOGW("%s: batch size is capped by max_buffers %d", __FUNCTION__,
+ camera_stream::max_buffers);
+ batchSize = camera_stream::max_buffers;
+ }
+ mBatchSize = batchSize;
+ }
+ return OK;
+}
+
+void Camera3OutputStream::returnPrefetchedBuffersLocked() {
+ std::lock_guard<std::mutex> batchLock(mBatchLock);
+ if (mBatchedBuffers.size() != 0) {
+ ALOGW("%s: %zu extra prefetched buffers detected. Returning",
+ __FUNCTION__, mBatchedBuffers.size());
+
+ mConsumer->cancelBuffers(mBatchedBuffers);
+ mBatchedBuffers.clear();
+ }
+}
+
}; // namespace camera3
}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index b4e49f9..00e4854 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_SERVERS_CAMERA3_OUTPUT_STREAM_H
#define ANDROID_SERVERS_CAMERA3_OUTPUT_STREAM_H
+#include <mutex>
#include <utils/RefBase.h>
#include <gui/IProducerListener.h>
#include <gui/Surface.h>
@@ -47,6 +48,7 @@
uint64_t combinedUsage;
size_t totalBufferCount;
bool isConfigured;
+ bool isMultiRes;
explicit StreamInfo(int id = CAMERA3_STREAM_ID_INVALID,
int setId = CAMERA3_STREAM_SET_ID_INVALID,
uint32_t w = 0,
@@ -55,7 +57,8 @@
android_dataspace ds = HAL_DATASPACE_UNKNOWN,
uint64_t usage = 0,
size_t bufferCount = 0,
- bool configured = false) :
+ bool configured = false,
+ bool multiRes = false) :
streamId(id),
streamSetId(setId),
width(w),
@@ -64,7 +67,8 @@
dataSpace(ds),
combinedUsage(usage),
totalBufferCount(bufferCount),
- isConfigured(configured){}
+ isConfigured(configured),
+ isMultiRes(multiRes) {}
};
/**
@@ -81,10 +85,10 @@
*/
Camera3OutputStream(int id, sp<Surface> consumer,
uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation,
+ android_dataspace dataSpace, camera_stream_rotation_t rotation,
nsecs_t timestampOffset, const String8& physicalCameraId,
- int setId = CAMERA3_STREAM_SET_ID_INVALID);
-
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ int setId = CAMERA3_STREAM_SET_ID_INVALID, bool isMultiResolution = false);
/**
* Set up a stream for formats that have a variable buffer size for the same
* dimensions, such as compressed JPEG.
@@ -93,10 +97,10 @@
*/
Camera3OutputStream(int id, sp<Surface> consumer,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation,
+ android_dataspace dataSpace, camera_stream_rotation_t rotation,
nsecs_t timestampOffset, const String8& physicalCameraId,
- int setId = CAMERA3_STREAM_SET_ID_INVALID);
-
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ int setId = CAMERA3_STREAM_SET_ID_INVALID, bool isMultiResolution = false);
/**
* Set up a stream with deferred consumer for formats that have 2 dimensions, such as
* RAW and YUV. The consumer must be set before using this stream for output. A valid
@@ -104,9 +108,10 @@
*/
Camera3OutputStream(int id, uint32_t width, uint32_t height, int format,
uint64_t consumerUsage, android_dataspace dataSpace,
- camera3_stream_rotation_t rotation, nsecs_t timestampOffset,
+ camera_stream_rotation_t rotation, nsecs_t timestampOffset,
const String8& physicalCameraId,
- int setId = CAMERA3_STREAM_SET_ID_INVALID);
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ int setId = CAMERA3_STREAM_SET_ID_INVALID, bool isMultiResolution = false);
virtual ~Camera3OutputStream();
@@ -206,23 +211,39 @@
KeyedVector<sp<Surface>, size_t> *outputMap/*out*/);
/**
+ * Set the batch size for buffer operations. The output stream will request
+ * buffers from buffer queue on a batch basis. Currently only video streams
+ * are allowed to set the batch size. Also if the stream is managed by
+ * buffer manager (Surface group in Java API) then batching is also not
+ * supported. Changing batch size on the fly while there is already batched
+ * buffers in the stream is also not supported.
+ * If the batch size is larger than the max dequeue count set
+ * by the camera HAL, the batch size will be set to the max dequeue count
+ * instead.
+ */
+ virtual status_t setBatchSize(size_t batchSize = 1) override;
+
+ /**
* Apply ZSL related consumer usage quirk.
*/
static void applyZSLUsageQuirk(int format, uint64_t *consumerUsage /*inout*/);
+ void setImageDumpMask(int mask) { mImageDumpMask = mask; }
+
protected:
- Camera3OutputStream(int id, camera3_stream_type_t type,
+ Camera3OutputStream(int id, camera_stream_type_t type,
uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation,
+ android_dataspace dataSpace, camera_stream_rotation_t rotation,
const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
uint64_t consumerUsage = 0, nsecs_t timestampOffset = 0,
- int setId = CAMERA3_STREAM_SET_ID_INVALID);
+ int setId = CAMERA3_STREAM_SET_ID_INVALID, bool isMultiResolution = false);
/**
* Note that we release the lock briefly in this function
*/
virtual status_t returnBufferCheckedLocked(
- const camera3_stream_buffer &buffer,
+ const camera_stream_buffer &buffer,
nsecs_t timestamp,
bool output,
const std::vector<size_t>& surface_ids,
@@ -290,14 +311,28 @@
// Whether to drop valid buffers.
bool mDropBuffers;
+
+ // Protecting batch states below, must be acquired after mLock
+ std::mutex mBatchLock;
+
+ // The batch size for buffer operation
+ size_t mBatchSize = 1;
+
+ // Prefetched buffers (ready to be handed to client)
+ std::vector<Surface::BatchBuffer> mBatchedBuffers;
+
+ // ---- End of mBatchLock protected scope ----
+
/**
* Internal Camera3Stream interface
*/
- virtual status_t getBufferLocked(camera3_stream_buffer *buffer,
+ virtual status_t getBufferLocked(camera_stream_buffer *buffer,
const std::vector<size_t>& surface_ids);
+ virtual status_t getBuffersLocked(/*out*/std::vector<OutstandingBuffer>* buffers) override;
+
virtual status_t returnBufferLocked(
- const camera3_stream_buffer &buffer,
+ const camera_stream_buffer &buffer,
nsecs_t timestamp, const std::vector<size_t>& surface_ids);
virtual status_t queueBufferToConsumer(sp<ANativeWindow>& consumer,
@@ -325,9 +360,16 @@
// STATE_ABANDONED
static bool shouldLogError(status_t res, StreamState state);
+ // Dump images to disk before returning to consumer
+ void dumpImageToDisk(nsecs_t timestamp, ANativeWindowBuffer* anwBuffer, int fence);
+
+ void returnPrefetchedBuffersLocked();
+
static const int32_t kDequeueLatencyBinSize = 5; // in ms
CameraLatencyHistogram mDequeueBufferLatency;
+ int mImageDumpMask = 0;
+
}; // class Camera3OutputStream
} // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
index 7f5c87a..49f9f62 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
@@ -95,6 +95,19 @@
* Query the physical camera id for the output stream.
*/
virtual const String8& getPhysicalCameraId() const = 0;
+
+ /**
+ * Set the batch size for buffer operations. The output stream will request
+ * buffers from buffer queue on a batch basis. Currently only video streams
+ * are allowed to set the batch size. Also if the stream is managed by
+ * buffer manager (Surface group in Java API) then batching is also not
+ * supported. Changing batch size on the fly while there is already batched
+ * buffers in the stream is also not supported.
+ * If the batch size is larger than the max dequeue count set
+ * by the camera HAL, the batch size will be set to the max dequeue count
+ * instead.
+ */
+ virtual status_t setBatchSize(size_t batchSize = 1) = 0;
};
// Helper class to organize a synchronized mapping of stream IDs to stream instances
diff --git a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
index 08cde5d..9f225d0 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
@@ -182,7 +182,33 @@
return;
}
- insertResultLocked(states, &captureResult, frameNumber);
+ // Update partial result by removing keys remapped by DistortionCorrection, ZoomRatio,
+ // and RotationAndCrop mappers.
+ std::set<uint32_t> keysToRemove;
+
+ auto iter = states.distortionMappers.find(states.cameraId.c_str());
+ if (iter != states.distortionMappers.end()) {
+ const auto& remappedKeys = iter->second.getRemappedKeys();
+ keysToRemove.insert(remappedKeys.begin(), remappedKeys.end());
+ }
+
+ const auto& remappedKeys = states.zoomRatioMappers[states.cameraId.c_str()].getRemappedKeys();
+ keysToRemove.insert(remappedKeys.begin(), remappedKeys.end());
+
+ auto mapper = states.rotateAndCropMappers.find(states.cameraId.c_str());
+ if (mapper != states.rotateAndCropMappers.end()) {
+ const auto& remappedKeys = iter->second.getRemappedKeys();
+ keysToRemove.insert(remappedKeys.begin(), remappedKeys.end());
+ }
+
+ for (uint32_t key : keysToRemove) {
+ captureResult.mMetadata.erase(key);
+ }
+
+ // Send partial result
+ if (captureResult.mMetadata.entryCount() > 0) {
+ insertResultLocked(states, &captureResult, frameNumber);
+ }
}
void sendCaptureResult(
@@ -397,6 +423,7 @@
InFlightRequestMap& inflightMap = states.inflightMap;
const InFlightRequest &request = inflightMap.valueAt(idx);
const uint32_t frameNumber = inflightMap.keyAt(idx);
+ SessionStatsBuilder& sessionStatsBuilder = states.sessionStatsBuilder;
nsecs_t sensorTimestamp = request.sensorTimestamp;
nsecs_t shutterTimestamp = request.shutterTimestamp;
@@ -416,7 +443,7 @@
ATRACE_ASYNC_END("frame capture", frameNumber);
- // Sanity check - if sensor timestamp matches shutter timestamp in the
+ // Validation check - if sensor timestamp matches shutter timestamp in the
// case of request having callback.
if (request.hasCallback && request.requestStatus == OK &&
sensorTimestamp != shutterTimestamp) {
@@ -433,7 +460,9 @@
returnOutputBuffers(
states.useHalBufManager, states.listener,
request.pendingOutputBuffers.array(),
- request.pendingOutputBuffers.size(), 0, /*timestampIncreasing*/true,
+ request.pendingOutputBuffers.size(), 0,
+ /*requested*/true, request.requestTimeNs, states.sessionStatsBuilder,
+ /*timestampIncreasing*/true,
request.outputSurfaces, request.resultExtras,
request.errorBufStrategy);
@@ -446,6 +475,8 @@
states.lastCompletedRegularFrameNumber = frameNumber;
}
+ sessionStatsBuilder.incResultCounter(request.skipResultMetadata);
+
removeInFlightMapEntryLocked(states, idx);
ALOGVV("%s: removed frame %d from InFlightMap", __FUNCTION__, frameNumber);
}
@@ -453,7 +484,21 @@
states.inflightIntf.checkInflightMapLengthLocked();
}
-void processCaptureResult(CaptureOutputStates& states, const camera3_capture_result *result) {
+// Erase the subset of physicalCameraIds that contains id
+bool erasePhysicalCameraIdSet(
+ std::set<std::set<String8>>& physicalCameraIds, const String8& id) {
+ bool found = false;
+ for (auto iter = physicalCameraIds.begin(); iter != physicalCameraIds.end(); iter++) {
+ if (iter->count(id) == 1) {
+ physicalCameraIds.erase(iter);
+ found = true;
+ break;
+ }
+ }
+ return found;
+}
+
+void processCaptureResult(CaptureOutputStates& states, const camera_capture_result *result) {
ATRACE_CALL();
status_t res;
@@ -552,12 +597,10 @@
}
for (uint32_t i = 0; i < result->num_physcam_metadata; i++) {
String8 physicalId(result->physcam_ids[i]);
- std::set<String8>::iterator cameraIdIter =
- request.physicalCameraIds.find(physicalId);
- if (cameraIdIter != request.physicalCameraIds.end()) {
- request.physicalCameraIds.erase(cameraIdIter);
- } else {
- SET_ERR("Total result for frame %d has already returned for camera %s",
+ bool validPhysicalCameraMetadata =
+ erasePhysicalCameraIdSet(request.physicalCameraIds, physicalId);
+ if (!validPhysicalCameraMetadata) {
+ SET_ERR("Unexpected total result for frame %d camera %s",
frameNumber, physicalId.c_str());
return;
}
@@ -602,7 +645,7 @@
if (shutterTimestamp != 0) {
returnAndRemovePendingOutputBuffers(
states.useHalBufManager, states.listener,
- request);
+ request, states.sessionStatsBuilder);
}
if (result->result != NULL && !isPartialResult) {
@@ -657,7 +700,7 @@
using hardware::camera::device::V3_2::BufferStatus;
std::unique_ptr<ResultMetadataQueue>& fmq = states.fmq;
BufferRecordsInterface& bufferRecords = states.bufferRecordsIntf;
- camera3_capture_result r;
+ camera_capture_result r;
status_t res;
r.frame_number = result.frameNumber;
@@ -696,7 +739,7 @@
r.physcam_ids = physCamIds.data();
r.physcam_metadata = phyCamMetadatas.data();
- std::vector<camera3_stream_buffer_t> outputBuffers(result.outputBuffers.size());
+ std::vector<camera_stream_buffer_t> outputBuffers(result.outputBuffers.size());
std::vector<buffer_handle_t> outputBufferHandles(result.outputBuffers.size());
for (size_t i = 0; i < result.outputBuffers.size(); i++) {
auto& bDst = outputBuffers[i];
@@ -757,7 +800,7 @@
r.num_output_buffers = outputBuffers.size();
r.output_buffers = outputBuffers.data();
- camera3_stream_buffer_t inputBuffer;
+ camera_stream_buffer_t inputBuffer;
if (result.inputBuffer.streamId == -1) {
r.input_buffer = nullptr;
} else {
@@ -798,8 +841,9 @@
void returnOutputBuffers(
bool useHalBufManager,
sp<NotificationListener> listener,
- const camera3_stream_buffer_t *outputBuffers, size_t numBuffers,
- nsecs_t timestamp, bool timestampIncreasing,
+ const camera_stream_buffer_t *outputBuffers, size_t numBuffers,
+ nsecs_t timestamp, bool requested, nsecs_t requestTimeNs,
+ SessionStatsBuilder& sessionStatsBuilder, bool timestampIncreasing,
const SurfaceMap& outputSurfaces,
const CaptureResultExtras &inResultExtras,
ERROR_BUF_STRATEGY errorBufStrategy) {
@@ -810,7 +854,7 @@
int streamId = stream->getId();
// Call notify(ERROR_BUFFER) if necessary.
- if (outputBuffers[i].status == CAMERA3_BUFFER_STATUS_ERROR &&
+ if (outputBuffers[i].status == CAMERA_BUFFER_STATUS_ERROR &&
errorBufStrategy == ERROR_BUF_RETURN_NOTIFY) {
if (listener != nullptr) {
CaptureResultExtras extras = inResultExtras;
@@ -827,6 +871,10 @@
// has not got a output buffer handle filled yet. This is though illegal if HAL
// buffer management API is not being used.
ALOGE("%s: cannot return a null buffer!", __FUNCTION__);
+ } else {
+ if (requested) {
+ sessionStatsBuilder.incCounter(streamId, /*dropped*/true, 0);
+ }
}
continue;
}
@@ -836,7 +884,7 @@
// Do not return the buffer if the buffer status is error, and the error
// buffer strategy is CACHE.
- if (outputBuffers[i].status != CAMERA3_BUFFER_STATUS_ERROR ||
+ if (outputBuffers[i].status != CAMERA_BUFFER_STATUS_ERROR ||
errorBufStrategy != ERROR_BUF_CACHE) {
if (it != outputSurfaces.end()) {
res = stream->returnBuffer(
@@ -850,20 +898,33 @@
}
// Note: stream may be deallocated at this point, if this buffer was
// the last reference to it.
+ bool dropped = false;
if (res == NO_INIT || res == DEAD_OBJECT) {
ALOGV("Can't return buffer to its stream: %s (%d)", strerror(-res), res);
+ sessionStatsBuilder.stopCounter(streamId);
} else if (res != OK) {
ALOGE("Can't return buffer to its stream: %s (%d)", strerror(-res), res);
+ dropped = true;
+ } else {
+ if (outputBuffers[i].status == CAMERA_BUFFER_STATUS_ERROR || timestamp == 0) {
+ dropped = true;
+ }
+ }
+ if (requested) {
+ nsecs_t bufferTimeNs = systemTime();
+ int32_t captureLatencyMs = ns2ms(bufferTimeNs - requestTimeNs);
+ sessionStatsBuilder.incCounter(streamId, dropped, captureLatencyMs);
}
// Long processing consumers can cause returnBuffer timeout for shared stream
// If that happens, cancel the buffer and send a buffer error to client
if (it != outputSurfaces.end() && res == TIMED_OUT &&
- outputBuffers[i].status == CAMERA3_BUFFER_STATUS_OK) {
+ outputBuffers[i].status == CAMERA_BUFFER_STATUS_OK) {
// cancel the buffer
- camera3_stream_buffer_t sb = outputBuffers[i];
- sb.status = CAMERA3_BUFFER_STATUS_ERROR;
- stream->returnBuffer(sb, /*timestamp*/0, timestampIncreasing, std::vector<size_t> (),
+ camera_stream_buffer_t sb = outputBuffers[i];
+ sb.status = CAMERA_BUFFER_STATUS_ERROR;
+ stream->returnBuffer(sb, /*timestamp*/0,
+ timestampIncreasing, std::vector<size_t> (),
inResultExtras.frameNumber);
if (listener != nullptr) {
@@ -878,12 +939,14 @@
}
void returnAndRemovePendingOutputBuffers(bool useHalBufManager,
- sp<NotificationListener> listener, InFlightRequest& request) {
+ sp<NotificationListener> listener, InFlightRequest& request,
+ SessionStatsBuilder& sessionStatsBuilder) {
bool timestampIncreasing = !(request.zslCapture || request.hasInputBuffer);
returnOutputBuffers(useHalBufManager, listener,
request.pendingOutputBuffers.array(),
request.pendingOutputBuffers.size(),
- request.shutterTimestamp, timestampIncreasing,
+ request.shutterTimestamp, /*requested*/true,
+ request.requestTimeNs, sessionStatsBuilder, timestampIncreasing,
request.outputSurfaces, request.resultExtras,
request.errorBufStrategy);
@@ -891,7 +954,7 @@
for (auto iter = request.pendingOutputBuffers.begin();
iter != request.pendingOutputBuffers.end(); ) {
if (request.errorBufStrategy != ERROR_BUF_CACHE ||
- iter->status != CAMERA3_BUFFER_STATUS_ERROR) {
+ iter->status != CAMERA_BUFFER_STATUS_ERROR) {
iter = request.pendingOutputBuffers.erase(iter);
} else {
iter++;
@@ -899,7 +962,7 @@
}
}
-void notifyShutter(CaptureOutputStates& states, const camera3_shutter_msg_t &msg) {
+void notifyShutter(CaptureOutputStates& states, const camera_shutter_msg_t &msg) {
ATRACE_CALL();
ssize_t idx;
@@ -966,7 +1029,7 @@
r.rotateAndCropAuto, r.cameraIdsWithZoom, r.physicalMetadatas);
}
returnAndRemovePendingOutputBuffers(
- states.useHalBufManager, states.listener, r);
+ states.useHalBufManager, states.listener, r, states.sessionStatsBuilder);
removeInFlightRequestIfReadyLocked(states, idx);
}
@@ -977,26 +1040,26 @@
}
}
-void notifyError(CaptureOutputStates& states, const camera3_error_msg_t &msg) {
+void notifyError(CaptureOutputStates& states, const camera_error_msg_t &msg) {
ATRACE_CALL();
// Map camera HAL error codes to ICameraDeviceCallback error codes
// Index into this with the HAL error code
- static const int32_t halErrorMap[CAMERA3_MSG_NUM_ERRORS] = {
+ static const int32_t halErrorMap[CAMERA_MSG_NUM_ERRORS] = {
// 0 = Unused error code
hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_INVALID_ERROR,
- // 1 = CAMERA3_MSG_ERROR_DEVICE
+ // 1 = CAMERA_MSG_ERROR_DEVICE
hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE,
- // 2 = CAMERA3_MSG_ERROR_REQUEST
+ // 2 = CAMERA_MSG_ERROR_REQUEST
hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST,
- // 3 = CAMERA3_MSG_ERROR_RESULT
+ // 3 = CAMERA_MSG_ERROR_RESULT
hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT,
- // 4 = CAMERA3_MSG_ERROR_BUFFER
+ // 4 = CAMERA_MSG_ERROR_BUFFER
hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER
};
int32_t errorCode =
((msg.error_code >= 0) &&
- (msg.error_code < CAMERA3_MSG_NUM_ERRORS)) ?
+ (msg.error_code < CAMERA_MSG_NUM_ERRORS)) ?
halErrorMap[msg.error_code] :
hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_INVALID_ERROR;
@@ -1032,14 +1095,14 @@
errorCode) {
if (physicalCameraId.size() > 0) {
String8 cameraId(physicalCameraId);
- auto iter = r.physicalCameraIds.find(cameraId);
- if (iter == r.physicalCameraIds.end()) {
+ bool validPhysicalCameraId =
+ erasePhysicalCameraIdSet(r.physicalCameraIds, cameraId);
+ if (!validPhysicalCameraId) {
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;
physicalDeviceResultError = true;
}
@@ -1085,13 +1148,13 @@
}
}
-void notify(CaptureOutputStates& states, const camera3_notify_msg *msg) {
+void notify(CaptureOutputStates& states, const camera_notify_msg *msg) {
switch (msg->type) {
- case CAMERA3_MSG_ERROR: {
+ case CAMERA_MSG_ERROR: {
notifyError(states, msg->message.error);
break;
}
- case CAMERA3_MSG_SHUTTER: {
+ case CAMERA_MSG_SHUTTER: {
notifyShutter(states, msg->message.shutter);
break;
}
@@ -1107,10 +1170,10 @@
using android::hardware::camera::device::V3_2::ErrorCode;
ATRACE_CALL();
- camera3_notify_msg m;
+ camera_notify_msg m;
switch (msg.type) {
case MsgType::ERROR:
- m.type = CAMERA3_MSG_ERROR;
+ m.type = CAMERA_MSG_ERROR;
m.message.error.frame_number = msg.msg.error.frameNumber;
if (msg.msg.error.errorStreamId >= 0) {
sp<Camera3StreamInterface> stream =
@@ -1126,21 +1189,21 @@
}
switch (msg.msg.error.errorCode) {
case ErrorCode::ERROR_DEVICE:
- m.message.error.error_code = CAMERA3_MSG_ERROR_DEVICE;
+ m.message.error.error_code = CAMERA_MSG_ERROR_DEVICE;
break;
case ErrorCode::ERROR_REQUEST:
- m.message.error.error_code = CAMERA3_MSG_ERROR_REQUEST;
+ m.message.error.error_code = CAMERA_MSG_ERROR_REQUEST;
break;
case ErrorCode::ERROR_RESULT:
- m.message.error.error_code = CAMERA3_MSG_ERROR_RESULT;
+ m.message.error.error_code = CAMERA_MSG_ERROR_RESULT;
break;
case ErrorCode::ERROR_BUFFER:
- m.message.error.error_code = CAMERA3_MSG_ERROR_BUFFER;
+ m.message.error.error_code = CAMERA_MSG_ERROR_BUFFER;
break;
}
break;
case MsgType::SHUTTER:
- m.type = CAMERA3_MSG_SHUTTER;
+ m.type = CAMERA_MSG_SHUTTER;
m.message.shutter.frame_number = msg.msg.shutter.frameNumber;
m.message.shutter.timestamp = msg.msg.shutter.timestamp;
break;
@@ -1241,11 +1304,11 @@
hardware::hidl_vec<StreamBuffer> tmpRetBuffers(numBuffersRequested);
bool currentReqSucceeds = true;
- std::vector<camera3_stream_buffer_t> streamBuffers(numBuffersRequested);
+ std::vector<camera_stream_buffer_t> streamBuffers(numBuffersRequested);
size_t numAllocatedBuffers = 0;
size_t numPushedInflightBuffers = 0;
for (size_t b = 0; b < numBuffersRequested; b++) {
- camera3_stream_buffer_t& sb = streamBuffers[b];
+ camera_stream_buffer_t& sb = streamBuffers[b];
// Since this method can run concurrently with request thread
// We need to update the wait duration everytime we call getbuffer
nsecs_t waitDuration = states.reqBufferIntf.getWaitDuration();
@@ -1255,6 +1318,7 @@
ALOGV("%s: Can't get output buffer for stream %d: %s (%d)",
__FUNCTION__, streamId, strerror(-res), res);
bufRet.val.error(StreamBufferRequestError::STREAM_DISCONNECTED);
+ states.sessionStatsBuilder.stopCounter(streamId);
} else {
ALOGE("%s: Can't get output buffer for stream %d: %s (%d)",
__FUNCTION__, streamId, strerror(-res), res);
@@ -1315,12 +1379,13 @@
}
}
for (size_t b = 0; b < numAllocatedBuffers; b++) {
- camera3_stream_buffer_t& sb = streamBuffers[b];
+ camera_stream_buffer_t& sb = streamBuffers[b];
sb.acquire_fence = -1;
- sb.status = CAMERA3_BUFFER_STATUS_ERROR;
+ sb.status = CAMERA_BUFFER_STATUS_ERROR;
}
returnOutputBuffers(states.useHalBufManager, /*listener*/nullptr,
- streamBuffers.data(), numAllocatedBuffers, 0);
+ streamBuffers.data(), numAllocatedBuffers, 0, /*requested*/false,
+ /*requestTimeNs*/0, states.sessionStatsBuilder);
}
}
@@ -1354,9 +1419,9 @@
continue;
}
- camera3_stream_buffer_t streamBuffer;
+ camera_stream_buffer_t streamBuffer;
streamBuffer.buffer = buffer;
- streamBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
+ streamBuffer.status = CAMERA_BUFFER_STATUS_ERROR;
streamBuffer.acquire_fence = -1;
streamBuffer.release_fence = -1;
@@ -1377,22 +1442,23 @@
}
streamBuffer.stream = stream->asHalStream();
returnOutputBuffers(states.useHalBufManager, /*listener*/nullptr,
- &streamBuffer, /*size*/1, /*timestamp*/ 0);
+ &streamBuffer, /*size*/1, /*timestamp*/ 0, /*requested*/false,
+ /*requestTimeNs*/0, states.sessionStatsBuilder);
}
}
void flushInflightRequests(FlushInflightReqStates& states) {
ATRACE_CALL();
- { // First return buffers cached in mInFlightMap
+ { // First return buffers cached in inFlightMap
std::lock_guard<std::mutex> l(states.inflightLock);
for (size_t idx = 0; idx < states.inflightMap.size(); idx++) {
const InFlightRequest &request = states.inflightMap.valueAt(idx);
returnOutputBuffers(
states.useHalBufManager, states.listener,
request.pendingOutputBuffers.array(),
- request.pendingOutputBuffers.size(), 0,
- /*timestampIncreasing*/true, request.outputSurfaces,
- request.resultExtras, request.errorBufStrategy);
+ request.pendingOutputBuffers.size(), 0, /*requested*/true,
+ request.requestTimeNs, states.sessionStatsBuilder, /*timestampIncreasing*/true,
+ request.outputSurfaces, request.resultExtras, request.errorBufStrategy);
ALOGW("%s: Frame %d | Timestamp: %" PRId64 ", metadata"
" arrived: %s, buffers left: %d.\n", __FUNCTION__,
states.inflightMap.keyAt(idx), request.shutterTimestamp,
@@ -1450,28 +1516,29 @@
int32_t frameNumber = std::get<1>(tuple);
buffer_handle_t* buffer = std::get<2>(tuple);
- camera3_stream_buffer_t streamBuffer;
+ camera_stream_buffer_t streamBuffer;
streamBuffer.buffer = buffer;
- streamBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
+ streamBuffer.status = CAMERA_BUFFER_STATUS_ERROR;
streamBuffer.acquire_fence = -1;
streamBuffer.release_fence = -1;
for (auto& stream : streams) {
if (streamId == stream->getId()) {
// Return buffer to deleted stream
- camera3_stream* halStream = stream->asHalStream();
+ camera_stream* halStream = stream->asHalStream();
streamBuffer.stream = halStream;
switch (halStream->stream_type) {
- case CAMERA3_STREAM_OUTPUT:
+ case CAMERA_STREAM_OUTPUT:
res = stream->returnBuffer(streamBuffer, /*timestamp*/ 0,
- /*timestampIncreasing*/true, std::vector<size_t> (), frameNumber);
+ /*timestampIncreasing*/true,
+ std::vector<size_t> (), frameNumber);
if (res != OK) {
ALOGE("%s: Can't return output buffer for frame %d to"
" stream %d: %s (%d)", __FUNCTION__,
frameNumber, streamId, strerror(-res), res);
}
break;
- case CAMERA3_STREAM_INPUT:
+ case CAMERA_STREAM_INPUT:
res = stream->returnInputBuffer(streamBuffer);
if (res != OK) {
ALOGE("%s: Can't return input buffer for frame %d to"
diff --git a/services/camera/libcameraservice/device3/Camera3OutputUtils.h b/services/camera/libcameraservice/device3/Camera3OutputUtils.h
index 9946312..142889a 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputUtils.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputUtils.h
@@ -33,6 +33,7 @@
#include "device3/InFlightRequest.h"
#include "device3/Camera3Stream.h"
#include "device3/Camera3OutputStreamInterface.h"
+#include "utils/SessionStatsBuilder.h"
#include "utils/TagMonitor.h"
namespace android {
@@ -41,6 +42,66 @@
namespace camera3 {
+ typedef struct camera_stream_configuration {
+ uint32_t num_streams;
+ camera_stream_t **streams;
+ uint32_t operation_mode;
+ bool input_is_multi_resolution;
+ } camera_stream_configuration_t;
+
+ typedef struct camera_capture_request {
+ uint32_t frame_number;
+ const camera_metadata_t *settings;
+ camera_stream_buffer_t *input_buffer;
+ uint32_t num_output_buffers;
+ const camera_stream_buffer_t *output_buffers;
+ uint32_t num_physcam_settings;
+ const char **physcam_id;
+ const camera_metadata_t **physcam_settings;
+ int32_t input_width;
+ int32_t input_height;
+ } camera_capture_request_t;
+
+ typedef struct camera_capture_result {
+ uint32_t frame_number;
+ const camera_metadata_t *result;
+ uint32_t num_output_buffers;
+ const camera_stream_buffer_t *output_buffers;
+ const camera_stream_buffer_t *input_buffer;
+ uint32_t partial_result;
+ uint32_t num_physcam_metadata;
+ const char **physcam_ids;
+ const camera_metadata_t **physcam_metadata;
+ } camera_capture_result_t;
+
+ typedef struct camera_shutter_msg {
+ uint32_t frame_number;
+ uint64_t timestamp;
+ } camera_shutter_msg_t;
+
+ typedef struct camera_error_msg {
+ uint32_t frame_number;
+ camera_stream_t *error_stream;
+ int error_code;
+ } camera_error_msg_t;
+
+ typedef enum camera_error_msg_code {
+ CAMERA_MSG_ERROR_DEVICE = 1,
+ CAMERA_MSG_ERROR_REQUEST = 2,
+ CAMERA_MSG_ERROR_RESULT = 3,
+ CAMERA_MSG_ERROR_BUFFER = 4,
+ CAMERA_MSG_NUM_ERRORS
+ } camera_error_msg_code_t;
+
+ typedef struct camera_notify_msg {
+ int type;
+
+ union {
+ camera_error_msg_t error;
+ camera_shutter_msg_t shutter;
+ } message;
+ } camera_notify_msg_t;
+
/**
* Helper methods shared between Camera3Device/Camera3OfflineSession for HAL callbacks
*/
@@ -50,8 +111,9 @@
void returnOutputBuffers(
bool useHalBufManager,
sp<NotificationListener> listener, // Only needed when outputSurfaces is not empty
- const camera3_stream_buffer_t *outputBuffers,
- size_t numBuffers, nsecs_t timestamp, bool timestampIncreasing = true,
+ const camera_stream_buffer_t *outputBuffers,
+ size_t numBuffers, nsecs_t timestamp, bool requested, nsecs_t requestTimeNs,
+ SessionStatsBuilder& sessionStatsBuilder, bool timestampIncreasing = true,
// The following arguments are only meant for surface sharing use case
const SurfaceMap& outputSurfaces = SurfaceMap{},
// Used to send buffer error callback when failing to return buffer
@@ -64,7 +126,7 @@
void returnAndRemovePendingOutputBuffers(
bool useHalBufManager,
sp<NotificationListener> listener, // Only needed when outputSurfaces is not empty
- InFlightRequest& request);
+ InFlightRequest& request, SessionStatsBuilder& sessionStatsBuilder);
// Camera3Device/Camera3OfflineSession internal states used in notify/processCaptureResult
// callbacks
@@ -72,8 +134,8 @@
const String8& cameraId;
std::mutex& inflightLock;
int64_t& lastCompletedRegularFrameNumber;
- int64_t& lastCompletedZslFrameNumber;
int64_t& lastCompletedReprocessFrameNumber;
+ int64_t& lastCompletedZslFrameNumber;
InFlightRequestMap& inflightMap; // end of inflightLock scope
std::mutex& outputLock;
std::list<CaptureResult>& resultQueue;
@@ -98,6 +160,7 @@
TagMonitor& tagMonitor;
sp<Camera3Stream> inputStream;
StreamSet& outputStreams;
+ SessionStatsBuilder& sessionStatsBuilder;
sp<NotificationListener> listener;
SetErrorInterface& setErrIntf;
InflightRequestUpdateInterface& inflightIntf;
@@ -121,6 +184,7 @@
std::mutex& reqBufferLock; // lock to serialize request buffer calls
const bool useHalBufManager;
StreamSet& outputStreams;
+ SessionStatsBuilder& sessionStatsBuilder;
SetErrorInterface& setErrIntf;
BufferRecordsInterface& bufferRecordsIntf;
RequestBufferInterface& reqBufferIntf;
@@ -134,6 +198,7 @@
const String8& cameraId;
const bool useHalBufManager;
StreamSet& outputStreams;
+ SessionStatsBuilder& sessionStatsBuilder;
BufferRecordsInterface& bufferRecordsIntf;
};
@@ -149,6 +214,7 @@
InflightRequestUpdateInterface& inflightIntf;
BufferRecordsInterface& bufferRecordsIntf;
FlushBufferInterface& flushBufferIntf;
+ SessionStatsBuilder& sessionStatsBuilder;
};
void flushInflightRequests(FlushInflightReqStates& states);
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
index 86b45cb..15cf7f4 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
@@ -30,11 +30,12 @@
const std::vector<sp<Surface>>& surfaces,
uint32_t width, uint32_t height, int format,
uint64_t consumerUsage, android_dataspace dataSpace,
- camera3_stream_rotation_t rotation,
+ camera_stream_rotation_t rotation,
nsecs_t timestampOffset, const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
int setId, bool useHalBufManager) :
- Camera3OutputStream(id, CAMERA3_STREAM_OUTPUT, width, height,
- format, dataSpace, rotation, physicalCameraId,
+ Camera3OutputStream(id, CAMERA_STREAM_OUTPUT, width, height,
+ format, dataSpace, rotation, physicalCameraId, sensorPixelModesUsed,
consumerUsage, timestampOffset, setId),
mUseHalBufManager(useHalBufManager) {
size_t consumerCount = std::min(surfaces.size(), kMaxOutputs);
@@ -65,7 +66,7 @@
}
}
- res = mStreamSplitter->connect(initialSurfaces, usage, mUsage, camera3_stream::max_buffers,
+ res = mStreamSplitter->connect(initialSurfaces, usage, mUsage, camera_stream::max_buffers,
getWidth(), getHeight(), getFormat(), &mConsumer);
if (res != OK) {
ALOGE("%s: Failed to connect to stream splitter: %s(%d)",
@@ -157,7 +158,7 @@
return ret;
}
-status_t Camera3SharedOutputStream::getBufferLocked(camera3_stream_buffer *buffer,
+status_t Camera3SharedOutputStream::getBufferLocked(camera_stream_buffer *buffer,
const std::vector<size_t>& surfaceIds) {
ANativeWindowBuffer* anb;
int fenceFd = -1;
@@ -180,7 +181,7 @@
* in which case we reassign it to acquire_fence
*/
handoutBufferLocked(*buffer, &(anb->handle), /*acquireFence*/fenceFd,
- /*releaseFence*/-1, CAMERA3_BUFFER_STATUS_OK, /*output*/true);
+ /*releaseFence*/-1, CAMERA_BUFFER_STATUS_OK, /*output*/true);
return OK;
}
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
index e645e05..4b6341b 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
@@ -36,8 +36,9 @@
Camera3SharedOutputStream(int id, const std::vector<sp<Surface>>& surfaces,
uint32_t width, uint32_t height, int format,
uint64_t consumerUsage, android_dataspace dataSpace,
- camera3_stream_rotation_t rotation, nsecs_t timestampOffset,
+ camera_stream_rotation_t rotation, nsecs_t timestampOffset,
const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
int setId = CAMERA3_STREAM_SET_ID_INVALID,
bool useHalBufManager = false);
@@ -116,7 +117,7 @@
/**
* Internal Camera3Stream interface
*/
- virtual status_t getBufferLocked(camera3_stream_buffer *buffer,
+ virtual status_t getBufferLocked(camera_stream_buffer *buffer,
const std::vector<size_t>& surface_ids);
virtual status_t queueBufferToConsumer(sp<ANativeWindow>& consumer,
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 20f6168..02b6585 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -37,20 +37,22 @@
}
}
-Camera3Stream* Camera3Stream::cast(camera3_stream *stream) {
+Camera3Stream* Camera3Stream::cast(camera_stream *stream) {
return static_cast<Camera3Stream*>(stream);
}
-const Camera3Stream* Camera3Stream::cast(const camera3_stream *stream) {
+const Camera3Stream* Camera3Stream::cast(const camera_stream *stream) {
return static_cast<const Camera3Stream*>(stream);
}
Camera3Stream::Camera3Stream(int id,
- camera3_stream_type type,
+ camera_stream_type type,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation,
- const String8& physicalCameraId, int setId) :
- camera3_stream(),
+ android_dataspace dataSpace, camera_stream_rotation_t rotation,
+ const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ int setId, bool isMultiResolution) :
+ camera_stream(),
mId(id),
mSetId(setId),
mName(String8::format("Camera3Stream[%d]", id)),
@@ -73,17 +75,18 @@
mDataSpaceOverridden(false),
mOriginalDataSpace(dataSpace),
mPhysicalCameraId(physicalCameraId),
- mLastTimestamp(0) {
+ mLastTimestamp(0),
+ mIsMultiResolution(isMultiResolution) {
- camera3_stream::stream_type = type;
- camera3_stream::width = width;
- camera3_stream::height = height;
- camera3_stream::format = format;
- camera3_stream::data_space = dataSpace;
- camera3_stream::rotation = rotation;
- camera3_stream::max_buffers = 0;
- camera3_stream::priv = NULL;
- camera3_stream::physical_camera_id = mPhysicalCameraId.string();
+ camera_stream::stream_type = type;
+ camera_stream::width = width;
+ camera_stream::height = height;
+ camera_stream::format = format;
+ camera_stream::data_space = dataSpace;
+ camera_stream::rotation = rotation;
+ camera_stream::max_buffers = 0;
+ camera_stream::physical_camera_id = mPhysicalCameraId.string();
+ camera_stream::sensor_pixel_modes_used = sensorPixelModesUsed;
if ((format == HAL_PIXEL_FORMAT_BLOB || format == HAL_PIXEL_FORMAT_RAW_OPAQUE) &&
maxSize == 0) {
@@ -100,20 +103,28 @@
return mSetId;
}
+int Camera3Stream::getHalStreamGroupId() const {
+ return mIsMultiResolution ? mSetId : -1;
+}
+
+bool Camera3Stream::isMultiResolution() const {
+ return mIsMultiResolution;
+}
+
uint32_t Camera3Stream::getWidth() const {
- return camera3_stream::width;
+ return camera_stream::width;
}
uint32_t Camera3Stream::getHeight() const {
- return camera3_stream::height;
+ return camera_stream::height;
}
int Camera3Stream::getFormat() const {
- return camera3_stream::format;
+ return camera_stream::format;
}
android_dataspace Camera3Stream::getDataSpace() const {
- return camera3_stream::data_space;
+ return camera_stream::data_space;
}
uint64_t Camera3Stream::getUsage() const {
@@ -152,6 +163,10 @@
return mPhysicalCameraId;
}
+int Camera3Stream::getMaxHalBuffers() const {
+ return camera_stream::max_buffers;
+}
+
void Camera3Stream::setOfflineProcessingSupport(bool support) {
mSupportOfflineProcessing = support;
}
@@ -229,7 +244,7 @@
return res;
}
-camera3_stream* Camera3Stream::startConfiguration() {
+camera_stream* Camera3Stream::startConfiguration() {
ATRACE_CALL();
Mutex::Autolock l(mLock);
status_t res;
@@ -260,9 +275,9 @@
}
mOldUsage = mUsage;
- mOldMaxBuffers = camera3_stream::max_buffers;
- mOldFormat = camera3_stream::format;
- mOldDataSpace = camera3_stream::data_space;
+ mOldMaxBuffers = camera_stream::max_buffers;
+ mOldFormat = camera_stream::format;
+ mOldDataSpace = camera_stream::data_space;
res = getEndpointUsage(&mUsage);
if (res != OK) {
@@ -330,16 +345,17 @@
// Register for idle tracking
sp<StatusTracker> statusTracker = mStatusTracker.promote();
if (statusTracker != 0 && mStatusId == StatusTracker::NO_STATUS_ID) {
- mStatusId = statusTracker->addComponent();
+ std::string name = std::string("Stream ") + std::to_string(mId);
+ mStatusId = statusTracker->addComponent(name.c_str());
}
// Check if the stream configuration is unchanged, and skip reallocation if
- // so. As documented in hardware/camera3.h:configure_streams().
+ // so.
if (mState == STATE_IN_RECONFIG &&
mOldUsage == mUsage &&
- mOldMaxBuffers == camera3_stream::max_buffers &&
- mOldDataSpace == camera3_stream::data_space &&
- mOldFormat == camera3_stream::format) {
+ mOldMaxBuffers == camera_stream::max_buffers &&
+ mOldDataSpace == camera_stream::data_space &&
+ mOldFormat == camera_stream::format) {
mState = STATE_CONFIGURED;
return OK;
}
@@ -398,7 +414,7 @@
}
mUsage = mOldUsage;
- camera3_stream::max_buffers = mOldMaxBuffers;
+ camera_stream::max_buffers = mOldMaxBuffers;
mState = ((mState == STATE_IN_RECONFIG) || (mState == STATE_IN_IDLE)) ? STATE_CONFIGURED :
STATE_CONSTRUCTED;
@@ -413,6 +429,13 @@
return mStreamUnpreparable;
}
+void Camera3Stream::markUnpreparable() {
+ ATRACE_CALL();
+
+ Mutex::Autolock l(mLock);
+ mStreamUnpreparable = true;
+}
+
status_t Camera3Stream::startPrepare(int maxCount, bool blockRequest) {
ATRACE_CALL();
@@ -458,7 +481,7 @@
mLastMaxCount = bufferCount;
- mPreparedBuffers.insertAt(camera3_stream_buffer_t(), /*index*/0, bufferCount);
+ mPreparedBuffers.insertAt(camera_stream_buffer_t(), /*index*/0, bufferCount);
mPreparedBufferIdx = 0;
mState = STATE_PREPARING;
@@ -533,7 +556,7 @@
// they weren't filled.
for (size_t i = 0; i < mPreparedBufferIdx; i++) {
mPreparedBuffers.editItemAt(i).release_fence = -1;
- mPreparedBuffers.editItemAt(i).status = CAMERA3_BUFFER_STATUS_ERROR;
+ mPreparedBuffers.editItemAt(i).status = CAMERA_BUFFER_STATUS_ERROR;
returnBufferLocked(mPreparedBuffers[i], 0);
}
mPreparedBuffers.clear();
@@ -599,7 +622,7 @@
return OK;
}
-status_t Camera3Stream::getBuffer(camera3_stream_buffer *buffer,
+status_t Camera3Stream::getBuffer(camera_stream_buffer *buffer,
nsecs_t waitBufferTimeout,
const std::vector<size_t>& surface_ids) {
ATRACE_HFR_CALL();
@@ -618,9 +641,10 @@
}
// Wait for new buffer returned back if we are running into the limit.
- if (getHandoutOutputBufferCountLocked() == camera3_stream::max_buffers) {
+ size_t numOutstandingBuffers = getHandoutOutputBufferCountLocked();
+ if (numOutstandingBuffers == camera_stream::max_buffers) {
ALOGV("%s: Already dequeued max output buffers (%d), wait for next returned one.",
- __FUNCTION__, camera3_stream::max_buffers);
+ __FUNCTION__, camera_stream::max_buffers);
nsecs_t waitStart = systemTime(SYSTEM_TIME_MONOTONIC);
if (waitBufferTimeout < kWaitForBufferDuration) {
waitBufferTimeout = kWaitForBufferDuration;
@@ -632,10 +656,18 @@
if (res == TIMED_OUT) {
ALOGE("%s: wait for output buffer return timed out after %lldms (max_buffers %d)",
__FUNCTION__, waitBufferTimeout / 1000000LL,
- camera3_stream::max_buffers);
+ camera_stream::max_buffers);
}
return res;
}
+
+ size_t updatedNumOutstandingBuffers = getHandoutOutputBufferCountLocked();
+ if (updatedNumOutstandingBuffers >= numOutstandingBuffers) {
+ ALOGE("%s: outsanding buffer count goes from %zu to %zu, "
+ "getBuffer(s) call must not run in parallel!", __FUNCTION__,
+ numOutstandingBuffers, updatedNumOutstandingBuffers);
+ return INVALID_OPERATION;
+ }
}
res = getBufferLocked(buffer, surface_ids);
@@ -650,7 +682,7 @@
return res;
}
-bool Camera3Stream::isOutstandingBuffer(const camera3_stream_buffer &buffer) const{
+bool Camera3Stream::isOutstandingBuffer(const camera_stream_buffer &buffer) const{
if (buffer.buffer == nullptr) {
return false;
}
@@ -665,7 +697,7 @@
return false;
}
-void Camera3Stream::removeOutstandingBuffer(const camera3_stream_buffer &buffer) {
+void Camera3Stream::removeOutstandingBuffer(const camera_stream_buffer &buffer) {
if (buffer.buffer == nullptr) {
return;
}
@@ -680,7 +712,7 @@
}
}
-status_t Camera3Stream::returnBuffer(const camera3_stream_buffer &buffer,
+status_t Camera3Stream::returnBuffer(const camera_stream_buffer &buffer,
nsecs_t timestamp, bool timestampIncreasing,
const std::vector<size_t>& surface_ids, uint64_t frameNumber) {
ATRACE_HFR_CALL();
@@ -695,11 +727,11 @@
removeOutstandingBuffer(buffer);
// Buffer status may be changed, so make a copy of the stream_buffer struct.
- camera3_stream_buffer b = buffer;
+ camera_stream_buffer b = buffer;
if (timestampIncreasing && timestamp != 0 && timestamp <= mLastTimestamp) {
ALOGE("%s: Stream %d: timestamp %" PRId64 " is not increasing. Prev timestamp %" PRId64,
__FUNCTION__, mId, timestamp, mLastTimestamp);
- b.status = CAMERA3_BUFFER_STATUS_ERROR;
+ b.status = CAMERA_BUFFER_STATUS_ERROR;
}
mLastTimestamp = timestamp;
@@ -723,11 +755,16 @@
return res;
}
-status_t Camera3Stream::getInputBuffer(camera3_stream_buffer *buffer, bool respectHalLimit) {
+status_t Camera3Stream::getInputBuffer(camera_stream_buffer *buffer,
+ Size* size, bool respectHalLimit) {
ATRACE_CALL();
Mutex::Autolock l(mLock);
status_t res = OK;
+ if (size == nullptr) {
+ ALOGE("%s: size must not be null", __FUNCTION__);
+ return BAD_VALUE;
+ }
// This function should be only called when the stream is configured already.
if (mState != STATE_CONFIGURED) {
ALOGE("%s: Stream %d: Can't get input buffers if stream is not in CONFIGURED state %d",
@@ -736,9 +773,9 @@
}
// Wait for new buffer returned back if we are running into the limit.
- if (getHandoutInputBufferCountLocked() == camera3_stream::max_buffers && respectHalLimit) {
+ if (getHandoutInputBufferCountLocked() == camera_stream::max_buffers && respectHalLimit) {
ALOGV("%s: Already dequeued max input buffers (%d), wait for next returned one.",
- __FUNCTION__, camera3_stream::max_buffers);
+ __FUNCTION__, camera_stream::max_buffers);
res = mInputBufferReturnedSignal.waitRelative(mLock, kWaitForBufferDuration);
if (res != OK) {
if (res == TIMED_OUT) {
@@ -749,7 +786,7 @@
}
}
- res = getInputBufferLocked(buffer);
+ res = getInputBufferLocked(buffer, size);
if (res == OK) {
fireBufferListenersLocked(*buffer, /*acquired*/true, /*output*/false);
if (buffer->buffer) {
@@ -761,7 +798,7 @@
return res;
}
-status_t Camera3Stream::returnInputBuffer(const camera3_stream_buffer &buffer) {
+status_t Camera3Stream::returnInputBuffer(const camera_stream_buffer &buffer) {
ATRACE_CALL();
Mutex::Autolock l(mLock);
@@ -803,7 +840,7 @@
}
void Camera3Stream::fireBufferListenersLocked(
- const camera3_stream_buffer& buffer, bool acquired, bool output, nsecs_t timestamp,
+ const camera_stream_buffer& buffer, bool acquired, bool output, nsecs_t timestamp,
uint64_t frameNumber) {
List<wp<Camera3StreamBufferListener> >::iterator it, end;
@@ -812,7 +849,7 @@
Camera3StreamBufferListener::BufferInfo info =
Camera3StreamBufferListener::BufferInfo();
info.mOutput = output;
- info.mError = (buffer.status == CAMERA3_BUFFER_STATUS_ERROR);
+ info.mError = (buffer.status == CAMERA_BUFFER_STATUS_ERROR);
info.mFrameNumber = frameNumber;
info.mTimestamp = timestamp;
info.mStreamId = getId();
@@ -882,22 +919,28 @@
" Latency histogram for wait on max_buffers");
}
-status_t Camera3Stream::getBufferLocked(camera3_stream_buffer *,
+status_t Camera3Stream::getBufferLocked(camera_stream_buffer *,
const std::vector<size_t>&) {
ALOGE("%s: This type of stream does not support output", __FUNCTION__);
return INVALID_OPERATION;
}
-status_t Camera3Stream::returnBufferLocked(const camera3_stream_buffer &,
+
+status_t Camera3Stream::getBuffersLocked(std::vector<OutstandingBuffer>*) {
+ ALOGE("%s: This type of stream does not support output", __FUNCTION__);
+ return INVALID_OPERATION;
+}
+
+status_t Camera3Stream::returnBufferLocked(const camera_stream_buffer &,
nsecs_t, const std::vector<size_t>&) {
ALOGE("%s: This type of stream does not support output", __FUNCTION__);
return INVALID_OPERATION;
}
-status_t Camera3Stream::getInputBufferLocked(camera3_stream_buffer *) {
+status_t Camera3Stream::getInputBufferLocked(camera_stream_buffer *, Size *) {
ALOGE("%s: This type of stream does not support input", __FUNCTION__);
return INVALID_OPERATION;
}
status_t Camera3Stream::returnInputBufferLocked(
- const camera3_stream_buffer &) {
+ const camera_stream_buffer &) {
ALOGE("%s: This type of stream does not support input", __FUNCTION__);
return INVALID_OPERATION;
}
@@ -960,6 +1003,80 @@
mBufferFreedListener = listener;
}
+status_t Camera3Stream::getBuffers(std::vector<OutstandingBuffer>* buffers,
+ nsecs_t waitBufferTimeout) {
+ ATRACE_CALL();
+ Mutex::Autolock l(mLock);
+ status_t res = OK;
+
+ if (buffers == nullptr) {
+ ALOGI("%s: buffers must not be null!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ size_t numBuffersRequested = buffers->size();
+ if (numBuffersRequested == 0) {
+ ALOGE("%s: 0 buffers are requested!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ // This function should be only called when the stream is configured already.
+ if (mState != STATE_CONFIGURED) {
+ ALOGE("%s: Stream %d: Can't get buffers if stream is not in CONFIGURED state %d",
+ __FUNCTION__, mId, mState);
+ if (mState == STATE_ABANDONED) {
+ return DEAD_OBJECT;
+ } else {
+ return INVALID_OPERATION;
+ }
+ }
+
+ size_t numOutstandingBuffers = getHandoutOutputBufferCountLocked();
+ // Wait for new buffer returned back if we are running into the limit.
+ while (numOutstandingBuffers + numBuffersRequested > camera_stream::max_buffers) {
+ ALOGV("%s: Already dequeued %zu output buffers and requesting %zu (max is %d), waiting.",
+ __FUNCTION__, numOutstandingBuffers, numBuffersRequested,
+ camera_stream::max_buffers);
+ nsecs_t waitStart = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (waitBufferTimeout < kWaitForBufferDuration) {
+ waitBufferTimeout = kWaitForBufferDuration;
+ }
+ res = mOutputBufferReturnedSignal.waitRelative(mLock, waitBufferTimeout);
+ nsecs_t waitEnd = systemTime(SYSTEM_TIME_MONOTONIC);
+ mBufferLimitLatency.add(waitStart, waitEnd);
+ if (res != OK) {
+ if (res == TIMED_OUT) {
+ ALOGE("%s: wait for output buffer return timed out after %lldms (max_buffers %d)",
+ __FUNCTION__, waitBufferTimeout / 1000000LL,
+ camera_stream::max_buffers);
+ }
+ return res;
+ }
+ size_t updatedNumOutstandingBuffers = getHandoutOutputBufferCountLocked();
+ if (updatedNumOutstandingBuffers >= numOutstandingBuffers) {
+ ALOGE("%s: outsanding buffer count goes from %zu to %zu, "
+ "getBuffer(s) call must not run in parallel!", __FUNCTION__,
+ numOutstandingBuffers, updatedNumOutstandingBuffers);
+ return INVALID_OPERATION;
+ }
+ numOutstandingBuffers = updatedNumOutstandingBuffers;
+ }
+
+ res = getBuffersLocked(buffers);
+ if (res == OK) {
+ for (auto& outstandingBuffer : *buffers) {
+ camera_stream_buffer* buffer = outstandingBuffer.outBuffer;
+ fireBufferListenersLocked(*buffer, /*acquired*/true, /*output*/true);
+ if (buffer->buffer) {
+ Mutex::Autolock l(mOutstandingBuffersLock);
+ mOutstandingBuffers.push_back(*buffer->buffer);
+ }
+ }
+ }
+
+ return res;
+}
+
}; // namespace camera3
}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index d768d3d..5a364ab 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -23,8 +23,6 @@
#include <utils/String16.h>
#include <utils/List.h>
-#include "hardware/camera3.h"
-
#include "utils/LatencyHistogram.h"
#include "Camera3StreamBufferListener.h"
#include "Camera3StreamInterface.h"
@@ -50,7 +48,7 @@
* with the HAL.
*
* STATE_IN_CONFIG: Configuration has started, but not yet concluded. During this
- * time, the usage, max_buffers, and priv fields of camera3_stream returned by
+ * time, the usage, max_buffers, and priv fields of camera_stream returned by
* startConfiguration() may be modified.
*
* STATE_IN_RE_CONFIG: Configuration has started, and the stream has been
@@ -130,15 +128,15 @@
*
*/
class Camera3Stream :
- protected camera3_stream,
+ protected camera_stream,
public virtual Camera3StreamInterface,
public virtual RefBase {
public:
virtual ~Camera3Stream();
- static Camera3Stream* cast(camera3_stream *stream);
- static const Camera3Stream* cast(const camera3_stream *stream);
+ static Camera3Stream* cast(camera_stream *stream);
+ static const Camera3Stream* cast(const camera_stream *stream);
/**
* Get the stream's ID
@@ -149,6 +147,14 @@
* Get the output stream set id.
*/
int getStreamSetId() const;
+ /**
+ * Is this stream part of a multi-resolution stream set
+ */
+ bool isMultiResolution() const;
+ /**
+ * Get the HAL stream group id for a multi-resolution stream set
+ */
+ int getHalStreamGroupId() const;
/**
* Get the stream's dimensions and format
@@ -165,12 +171,13 @@
void setDataSpaceOverride(bool dataSpaceOverriden);
bool isDataSpaceOverridden() const;
android_dataspace getOriginalDataSpace() const;
+ int getMaxHalBuffers() const;
const String8& physicalCameraId() const;
void setOfflineProcessingSupport(bool) override;
bool getOfflineProcessingSupport() const override;
- camera3_stream* asHalStream() override {
+ camera_stream* asHalStream() override {
return this;
}
@@ -179,14 +186,12 @@
* information to be passed into the HAL device's configure_streams call.
*
* Until finishConfiguration() is called, no other methods on the stream may be
- * called. The usage and max_buffers fields of camera3_stream may be modified
+ * called. The usage and max_buffers fields of camera_stream may be modified
* between start/finishConfiguration, but may not be changed after that.
- * The priv field of camera3_stream may be modified at any time after
- * startConfiguration.
*
* Returns NULL in case of error starting configuration.
*/
- camera3_stream* startConfiguration();
+ camera_stream* startConfiguration();
/**
* Check if the stream is mid-configuration (start has been called, but not
@@ -226,6 +231,11 @@
bool isUnpreparable();
/**
+ * Mark the stream as unpreparable.
+ */
+ void markUnpreparable() override;
+
+ /**
* Start stream preparation. May only be called in the CONFIGURED state,
* when no valid buffers have yet been returned to this stream. Prepares
* up to maxCount buffers, or the maximum number of buffers needed by the
@@ -307,7 +317,7 @@
status_t tearDown();
/**
- * Fill in the camera3_stream_buffer with the next valid buffer for this
+ * Fill in the camera_stream_buffer with the next valid buffer for this
* stream, to hand over to the HAL.
*
* Multiple surfaces could share the same HAL stream, but a request may
@@ -320,11 +330,17 @@
* buffers.
*
*/
- status_t getBuffer(camera3_stream_buffer *buffer,
+ status_t getBuffer(camera_stream_buffer *buffer,
nsecs_t waitBufferTimeout,
const std::vector<size_t>& surface_ids = std::vector<size_t>());
/**
+ * Similar to getBuffer() except this method fills multiple buffers.
+ */
+ status_t getBuffers(std::vector<OutstandingBuffer>* buffers,
+ nsecs_t waitBufferTimeout);
+
+ /**
* Return a buffer to the stream after use by the HAL.
*
* Multiple surfaces could share the same HAL stream, but a request may
@@ -335,23 +351,26 @@
* This method may only be called for buffers provided by getBuffer().
* For bidirectional streams, this method applies to the output-side buffers
*/
- status_t returnBuffer(const camera3_stream_buffer &buffer,
+ status_t returnBuffer(const camera_stream_buffer &buffer,
nsecs_t timestamp, bool timestampIncreasing,
const std::vector<size_t>& surface_ids = std::vector<size_t>(),
uint64_t frameNumber = 0);
/**
- * Fill in the camera3_stream_buffer with the next valid buffer for this
+ * Fill in the camera_stream_buffer with the next valid buffer for this
* stream, to hand over to the HAL.
*
* This method may only be called once finishConfiguration has been called.
* For bidirectional streams, this method applies to the input-side
* buffers.
*
+ * This method also returns the size of the returned input buffer.
+ *
* Normally this call will block until the handed out buffer count is less than the stream
* max buffer count; if respectHalLimit is set to false, this is ignored.
*/
- status_t getInputBuffer(camera3_stream_buffer *buffer, bool respectHalLimit = true);
+ status_t getInputBuffer(camera_stream_buffer *buffer,
+ Size* size, bool respectHalLimit = true);
/**
* Return a buffer to the stream after use by the HAL.
@@ -359,7 +378,7 @@
* This method may only be called for buffers provided by getBuffer().
* For bidirectional streams, this method applies to the input-side buffers
*/
- status_t returnInputBuffer(const camera3_stream_buffer &buffer);
+ status_t returnInputBuffer(const camera_stream_buffer &buffer);
// get the buffer producer of the input buffer queue.
// only apply to input streams.
@@ -476,10 +495,12 @@
mutable Mutex mLock;
- Camera3Stream(int id, camera3_stream_type type,
+ Camera3Stream(int id, camera_stream_type type,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation,
- const String8& physicalCameraId, int setId);
+ android_dataspace dataSpace, camera_stream_rotation_t rotation,
+ const String8& physicalCameraId,
+ const std::unordered_set<int32_t> &sensorPixelModesUsed,
+ int setId, bool isMultiResolution);
wp<Camera3StreamBufferFreedListener> mBufferFreedListener;
@@ -489,18 +510,22 @@
// getBuffer / returnBuffer implementations
- // Since camera3_stream_buffer includes a raw pointer to the stream,
- // cast to camera3_stream*, implementations must increment the
+ // Since camera_stream_buffer includes a raw pointer to the stream,
+ // cast to camera_stream*, implementations must increment the
// refcount of the stream manually in getBufferLocked, and decrement it in
// returnBufferLocked.
- virtual status_t getBufferLocked(camera3_stream_buffer *buffer,
+ virtual status_t getBufferLocked(camera_stream_buffer *buffer,
const std::vector<size_t>& surface_ids = std::vector<size_t>());
- virtual status_t returnBufferLocked(const camera3_stream_buffer &buffer,
+ virtual status_t returnBufferLocked(const camera_stream_buffer &buffer,
nsecs_t timestamp,
const std::vector<size_t>& surface_ids = std::vector<size_t>());
- virtual status_t getInputBufferLocked(camera3_stream_buffer *buffer);
+
+ virtual status_t getBuffersLocked(std::vector<OutstandingBuffer>*);
+
+ virtual status_t getInputBufferLocked(camera_stream_buffer *buffer, Size* size);
+
virtual status_t returnInputBufferLocked(
- const camera3_stream_buffer &buffer);
+ const camera_stream_buffer &buffer);
virtual bool hasOutstandingBuffersLocked() const = 0;
// Get the buffer producer of the input buffer queue. Only apply to input streams.
virtual status_t getInputBufferProducerLocked(sp<IGraphicBufferProducer> *producer);
@@ -529,7 +554,7 @@
virtual status_t getEndpointUsage(uint64_t *usage) const = 0;
// Return whether the buffer is in the list of outstanding buffers.
- bool isOutstandingBuffer(const camera3_stream_buffer& buffer) const;
+ bool isOutstandingBuffer(const camera_stream_buffer& buffer) const;
// Tracking for idle state
wp<StatusTracker> mStatusTracker;
@@ -553,14 +578,14 @@
Condition mInputBufferReturnedSignal;
static const nsecs_t kWaitForBufferDuration = 3000000000LL; // 3000 ms
- void fireBufferListenersLocked(const camera3_stream_buffer& buffer,
+ void fireBufferListenersLocked(const camera_stream_buffer& buffer,
bool acquired, bool output, nsecs_t timestamp = 0, uint64_t frameNumber = 0);
List<wp<Camera3StreamBufferListener> > mBufferListenerList;
status_t cancelPrepareLocked();
// Remove the buffer from the list of outstanding buffers.
- void removeOutstandingBuffer(const camera3_stream_buffer& buffer);
+ void removeOutstandingBuffer(const camera_stream_buffer& buffer);
// Tracking for PREPARING state
@@ -570,7 +595,7 @@
bool mPrepared;
bool mPrepareBlockRequest;
- Vector<camera3_stream_buffer_t> mPreparedBuffers;
+ Vector<camera_stream_buffer_t> mPreparedBuffers;
size_t mPreparedBufferIdx;
// Number of buffers allocated on last prepare call.
@@ -596,6 +621,7 @@
String8 mPhysicalCameraId;
nsecs_t mLastTimestamp;
+ bool mIsMultiResolution = false;
bool mSupportOfflineProcessing = false;
}; // class Camera3Stream
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index 667e3bb..2d3397c 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -23,13 +23,63 @@
#include "Camera3StreamBufferListener.h"
#include "Camera3StreamBufferFreedListener.h"
-struct camera3_stream;
-struct camera3_stream_buffer;
-
namespace android {
namespace camera3 {
+typedef enum camera_buffer_status {
+ CAMERA_BUFFER_STATUS_OK = 0,
+ CAMERA_BUFFER_STATUS_ERROR = 1
+} camera_buffer_status_t;
+
+typedef enum camera_stream_type {
+ CAMERA_STREAM_OUTPUT = 0,
+ CAMERA_STREAM_INPUT = 1,
+ CAMERA_NUM_STREAM_TYPES
+} camera_stream_type_t;
+
+typedef enum camera_stream_rotation {
+ /* No rotation */
+ CAMERA_STREAM_ROTATION_0 = 0,
+
+ /* Rotate by 90 degree counterclockwise */
+ CAMERA_STREAM_ROTATION_90 = 1,
+
+ /* Rotate by 180 degree counterclockwise */
+ CAMERA_STREAM_ROTATION_180 = 2,
+
+ /* Rotate by 270 degree counterclockwise */
+ CAMERA_STREAM_ROTATION_270 = 3
+} camera_stream_rotation_t;
+
+typedef struct camera_stream {
+ camera_stream_type_t stream_type;
+ uint32_t width;
+ uint32_t height;
+ int format;
+ uint32_t usage;
+ uint32_t max_buffers;
+ android_dataspace_t data_space;
+ camera_stream_rotation_t rotation;
+ const char* physical_camera_id;
+
+ std::unordered_set<int32_t> sensor_pixel_modes_used;
+} camera_stream_t;
+
+typedef struct camera_stream_buffer {
+ camera_stream_t *stream;
+ buffer_handle_t *buffer;
+ camera_buffer_status_t status;
+ int acquire_fence;
+ int release_fence;
+} camera_stream_buffer_t;
+
+struct Size {
+ uint32_t width;
+ uint32_t height;
+ explicit Size(uint32_t w = 0, uint32_t h = 0) : width(w), height(h){}
+};
+
enum {
/**
* This stream set ID indicates that the set ID is invalid, and this stream doesn't intend to
@@ -56,13 +106,15 @@
uint64_t consumerUsage;
bool finalized = false;
bool supportsOffline = false;
+ std::unordered_set<int32_t> sensorPixelModesUsed;
OutputStreamInfo() :
width(-1), height(-1), format(-1), dataSpace(HAL_DATASPACE_UNKNOWN),
consumerUsage(0) {}
OutputStreamInfo(int _width, int _height, int _format, android_dataspace _dataSpace,
- uint64_t _consumerUsage) :
+ uint64_t _consumerUsage, const std::unordered_set<int32_t>& _sensorPixelModesUsed) :
width(_width), height(_height), format(_format),
- dataSpace(_dataSpace), consumerUsage(_consumerUsage) {}
+ dataSpace(_dataSpace), consumerUsage(_consumerUsage),
+ sensorPixelModesUsed(_sensorPixelModesUsed) {}
};
/**
@@ -87,6 +139,16 @@
virtual int getStreamSetId() const = 0;
/**
+ * Is this stream part of a multi-resolution stream set
+ */
+ virtual bool isMultiResolution() const = 0;
+
+ /**
+ * Get the HAL stream group id for a multi-resolution stream set
+ */
+ virtual int getHalStreamGroupId() const = 0;
+
+ /**
* Get the stream's dimensions and format
*/
virtual uint32_t getWidth() const = 0;
@@ -99,6 +161,8 @@
virtual void setDataSpaceOverride(bool dataSpaceOverriden) = 0;
virtual bool isDataSpaceOverridden() const = 0;
virtual android_dataspace getOriginalDataSpace() const = 0;
+ virtual int getMaxHalBuffers() const = 0;
+ virtual int getMaxTotalBuffers() const = 0;
/**
* Offline processing
@@ -107,23 +171,23 @@
virtual bool getOfflineProcessingSupport() const = 0;
/**
- * Get a HAL3 handle for the stream, without starting stream configuration.
+ * Get a handle for the stream, without starting stream configuration.
*/
- virtual camera3_stream* asHalStream() = 0;
+ virtual camera_stream* asHalStream() = 0;
/**
* Start the stream configuration process. Returns a handle to the stream's
- * information to be passed into the HAL device's configure_streams call.
+ * information to be passed into the device's configure_streams call.
*
* Until finishConfiguration() is called, no other methods on the stream may
- * be called. The usage and max_buffers fields of camera3_stream may be
+ * be called. The usage and max_buffers fields of camera_stream may be
* modified between start/finishConfiguration, but may not be changed after
- * that. The priv field of camera3_stream may be modified at any time after
+ * that. The priv field of camera_stream may be modified at any time after
* startConfiguration.
*
* Returns NULL in case of error starting configuration.
*/
- virtual camera3_stream* startConfiguration() = 0;
+ virtual camera_stream* startConfiguration() = 0;
/**
* Check if the stream is mid-configuration (start has been called, but not
@@ -163,6 +227,11 @@
virtual bool isUnpreparable() = 0;
/**
+ * Mark the stream as unpreparable.
+ */
+ virtual void markUnpreparable() = 0;
+
+ /**
* Start stream preparation. May only be called in the CONFIGURED state,
* when no valid buffers have yet been returned to this stream. Prepares
* up to maxCount buffers, or the maximum number of buffers needed by the
@@ -239,7 +308,7 @@
virtual status_t tearDown() = 0;
/**
- * Fill in the camera3_stream_buffer with the next valid buffer for this
+ * Fill in the camera_stream_buffer with the next valid buffer for this
* stream, to hand over to the HAL.
*
* Multiple surfaces could share the same HAL stream, but a request may
@@ -253,10 +322,28 @@
* buffers.
*
*/
- virtual status_t getBuffer(camera3_stream_buffer *buffer,
+ virtual status_t getBuffer(camera_stream_buffer *buffer,
nsecs_t waitBufferTimeout,
const std::vector<size_t>& surface_ids = std::vector<size_t>()) = 0;
+ struct OutstandingBuffer {
+ camera_stream_buffer* outBuffer;
+
+ /**
+ * Multiple surfaces could share the same HAL stream, but a request may
+ * be only for a subset of surfaces. In this case, the
+ * Camera3StreamInterface object needs the surface ID information to acquire
+ * buffers for those surfaces. For the case of single surface for a HAL
+ * stream, surface_ids parameter has no effect.
+ */
+ std::vector<size_t> surface_ids;
+ };
+ /**
+ * Similar to getBuffer() except this method fills multiple buffers.
+ */
+ virtual status_t getBuffers(std::vector<OutstandingBuffer>* buffers,
+ nsecs_t waitBufferTimeout) = 0;
+
/**
* Return a buffer to the stream after use by the HAL.
*
@@ -269,13 +356,13 @@
* This method may only be called for buffers provided by getBuffer().
* For bidirectional streams, this method applies to the output-side buffers
*/
- virtual status_t returnBuffer(const camera3_stream_buffer &buffer,
+ virtual status_t returnBuffer(const camera_stream_buffer &buffer,
nsecs_t timestamp, bool timestampIncreasing = true,
const std::vector<size_t>& surface_ids = std::vector<size_t>(),
uint64_t frameNumber = 0) = 0;
/**
- * Fill in the camera3_stream_buffer with the next valid buffer for this
+ * Fill in the camera_stream_buffer with the next valid buffer for this
* stream, to hand over to the HAL.
*
* This method may only be called once finishConfiguration has been called.
@@ -285,7 +372,8 @@
* Normally this call will block until the handed out buffer count is less than the stream
* max buffer count; if respectHalLimit is set to false, this is ignored.
*/
- virtual status_t getInputBuffer(camera3_stream_buffer *buffer, bool respectHalLimit = true) = 0;
+ virtual status_t getInputBuffer(camera_stream_buffer *buffer,
+ Size *size, bool respectHalLimit = true) = 0;
/**
* Return a buffer to the stream after use by the HAL.
@@ -293,7 +381,7 @@
* This method may only be called for buffers provided by getBuffer().
* For bidirectional streams, this method applies to the input-side buffers
*/
- virtual status_t returnInputBuffer(const camera3_stream_buffer &buffer) = 0;
+ virtual status_t returnInputBuffer(const camera_stream_buffer &buffer) = 0;
/**
* Get the buffer producer of the input buffer queue.
diff --git a/services/camera/libcameraservice/device3/CoordinateMapper.h b/services/camera/libcameraservice/device3/CoordinateMapper.h
index 5164856..558f4c0 100644
--- a/services/camera/libcameraservice/device3/CoordinateMapper.h
+++ b/services/camera/libcameraservice/device3/CoordinateMapper.h
@@ -18,16 +18,23 @@
#define ANDROID_SERVERS_COORDINATEMAPPER_H
#include <array>
+#include <set>
namespace android {
namespace camera3 {
class CoordinateMapper {
- // Right now only stores metadata tags containing 2D coordinates
- // to be corrected.
+public:
+ // The result metadata tags that are to be re-mapped
+ const std::set<uint32_t>& getRemappedKeys() const {
+ return mRemappedKeys;
+ }
+
+ virtual ~CoordinateMapper() = default;
+
protected:
- // Metadata key lists to correct
+ // Metadata tags containing 2D coordinates to be corrected.
// Both capture request and result
static const std::array<uint32_t, 3> kMeteringRegionsToCorrect;
@@ -37,6 +44,10 @@
// Only for capture results; don't clamp
static const std::array<uint32_t, 2> kResultPointsToCorrectNoClamp;
+
+ virtual void initRemappedKeys() = 0;
+ std::set<uint32_t> mRemappedKeys;
+
}; // class CoordinateMapper
} // namespace camera3
diff --git a/services/camera/libcameraservice/device3/DistortionMapper.cpp b/services/camera/libcameraservice/device3/DistortionMapper.cpp
index 8132225..89dd115 100644
--- a/services/camera/libcameraservice/device3/DistortionMapper.cpp
+++ b/services/camera/libcameraservice/device3/DistortionMapper.cpp
@@ -22,13 +22,28 @@
#include <cmath>
#include "device3/DistortionMapper.h"
+#include "utils/SessionConfigurationUtils.h"
namespace android {
namespace camera3 {
-DistortionMapper::DistortionMapper() : mValidMapping(false), mValidGrids(false) {
+DistortionMapper::DistortionMapper() {
+ initRemappedKeys();
+}
+
+void DistortionMapper::initRemappedKeys() {
+ mRemappedKeys.insert(
+ kMeteringRegionsToCorrect.begin(),
+ kMeteringRegionsToCorrect.end());
+ mRemappedKeys.insert(
+ kRectsToCorrect.begin(),
+ kRectsToCorrect.end());
+ mRemappedKeys.insert(
+ kResultPointsToCorrectNoClamp.begin(),
+ kResultPointsToCorrectNoClamp.end());
+ mRemappedKeys.insert(ANDROID_DISTORTION_CORRECTION_MODE);
}
bool DistortionMapper::isDistortionSupported(const CameraMetadata &deviceInfo) {
@@ -47,41 +62,81 @@
status_t DistortionMapper::setupStaticInfo(const CameraMetadata &deviceInfo) {
std::lock_guard<std::mutex> lock(mMutex);
+ status_t res = setupStaticInfoLocked(deviceInfo, /*maxResolution*/false);
+ if (res != OK) {
+ return res;
+ }
+
+ bool mMaxResolution = SessionConfigurationUtils::isUltraHighResolutionSensor(deviceInfo);
+ if (mMaxResolution) {
+ res = setupStaticInfoLocked(deviceInfo, /*maxResolution*/true);
+ }
+ return res;
+}
+
+status_t DistortionMapper::setupStaticInfoLocked(const CameraMetadata &deviceInfo,
+ bool maxResolution) {
+ DistortionMapperInfo *mapperInfo = maxResolution ? &mDistortionMapperInfoMaximumResolution :
+ &mDistortionMapperInfo;
+
camera_metadata_ro_entry_t array;
- array = deviceInfo.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
+ array = deviceInfo.find(
+ SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, maxResolution));
if (array.count != 4) return BAD_VALUE;
float arrayX = static_cast<float>(array.data.i32[0]);
float arrayY = static_cast<float>(array.data.i32[1]);
- mArrayWidth = static_cast<float>(array.data.i32[2]);
- mArrayHeight = static_cast<float>(array.data.i32[3]);
+ mapperInfo->mArrayWidth = static_cast<float>(array.data.i32[2]);
+ mapperInfo->mArrayHeight = static_cast<float>(array.data.i32[3]);
- array = deviceInfo.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+ array = deviceInfo.find(
+ SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, maxResolution));
if (array.count != 4) return BAD_VALUE;
float activeX = static_cast<float>(array.data.i32[0]);
float activeY = static_cast<float>(array.data.i32[1]);
- mActiveWidth = static_cast<float>(array.data.i32[2]);
- mActiveHeight = static_cast<float>(array.data.i32[3]);
+ mapperInfo->mActiveWidth = static_cast<float>(array.data.i32[2]);
+ mapperInfo->mActiveHeight = static_cast<float>(array.data.i32[3]);
- mArrayDiffX = activeX - arrayX;
- mArrayDiffY = activeY - arrayY;
+ mapperInfo->mArrayDiffX = activeX - arrayX;
+ mapperInfo->mArrayDiffY = activeY - arrayY;
- return updateCalibration(deviceInfo);
+ return updateCalibration(deviceInfo, /*isStatic*/ true, maxResolution);
+}
+
+static bool doesSettingsHaveMaxResolution(const CameraMetadata *settings) {
+ if (settings == nullptr) {
+ return false;
+ }
+ // First we get the sensorPixelMode from the settings metadata.
+ camera_metadata_ro_entry sensorPixelModeEntry = settings->find(ANDROID_SENSOR_PIXEL_MODE);
+ if (sensorPixelModeEntry.count != 0) {
+ return (sensorPixelModeEntry.data.u8[0] == ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION);
+ }
+ return false;
}
bool DistortionMapper::calibrationValid() const {
std::lock_guard<std::mutex> lock(mMutex);
-
- return mValidMapping;
+ bool isValid = mDistortionMapperInfo.mValidMapping;
+ if (mMaxResolution) {
+ isValid = isValid && mDistortionMapperInfoMaximumResolution.mValidMapping;
+ }
+ return isValid;
}
status_t DistortionMapper::correctCaptureRequest(CameraMetadata *request) {
std::lock_guard<std::mutex> lock(mMutex);
status_t res;
- if (!mValidMapping) return OK;
+ bool maxResolution = doesSettingsHaveMaxResolution(request);
+ DistortionMapperInfo *mapperInfo = maxResolution ? &mDistortionMapperInfoMaximumResolution :
+ &mDistortionMapperInfo;
+
+ if (!mapperInfo->mValidMapping) return OK;
camera_metadata_entry_t e;
e = request->find(ANDROID_DISTORTION_CORRECTION_MODE);
@@ -93,27 +148,30 @@
if (weight == 0) {
continue;
}
- res = mapCorrectedToRaw(e.data.i32 + j, 2, /*clamp*/true);
+ res = mapCorrectedToRaw(e.data.i32 + j, 2, mapperInfo, /*clamp*/true);
if (res != OK) return res;
}
}
for (auto rect : kRectsToCorrect) {
e = request->find(rect);
- res = mapCorrectedRectToRaw(e.data.i32, e.count / 4, /*clamp*/true);
+ res = mapCorrectedRectToRaw(e.data.i32, e.count / 4, mapperInfo, /*clamp*/true);
if (res != OK) return res;
}
}
-
return OK;
}
status_t DistortionMapper::correctCaptureResult(CameraMetadata *result) {
std::lock_guard<std::mutex> lock(mMutex);
+
+ bool maxResolution = doesSettingsHaveMaxResolution(result);
+ DistortionMapperInfo *mapperInfo = maxResolution ? &mDistortionMapperInfoMaximumResolution :
+ &mDistortionMapperInfo;
status_t res;
- if (!mValidMapping) return OK;
+ if (!mapperInfo->mValidMapping) return OK;
- res = updateCalibration(*result);
+ res = updateCalibration(*result, /*isStatic*/ false, maxResolution);
if (res != OK) {
ALOGE("Failure to update lens calibration information");
return INVALID_OPERATION;
@@ -129,18 +187,18 @@
if (weight == 0) {
continue;
}
- res = mapRawToCorrected(e.data.i32 + j, 2, /*clamp*/true);
+ res = mapRawToCorrected(e.data.i32 + j, 2, mapperInfo, /*clamp*/true);
if (res != OK) return res;
}
}
for (auto rect : kRectsToCorrect) {
e = result->find(rect);
- res = mapRawRectToCorrected(e.data.i32, e.count / 4, /*clamp*/true);
+ res = mapRawRectToCorrected(e.data.i32, e.count / 4, mapperInfo, /*clamp*/true);
if (res != OK) return res;
}
for (auto pts : kResultPointsToCorrectNoClamp) {
e = result->find(pts);
- res = mapRawToCorrected(e.data.i32, e.count / 2, /*clamp*/false);
+ res = mapRawToCorrected(e.data.i32, e.count / 2, mapperInfo, /*clamp*/false);
if (res != OK) return res;
}
}
@@ -150,25 +208,37 @@
// Utility methods; not guarded by mutex
-status_t DistortionMapper::updateCalibration(const CameraMetadata &result) {
+status_t DistortionMapper::updateCalibration(const CameraMetadata &result, bool isStatic,
+ bool maxResolution) {
camera_metadata_ro_entry_t calib, distortion;
+ DistortionMapperInfo *mapperInfo =
+ maxResolution ? &mDistortionMapperInfoMaximumResolution : &mDistortionMapperInfo;
+ // We only need maximum resolution version of LENS_INTRINSIC_CALIBRATION and
+ // LENS_DISTORTION since CaptureResults would still use the same key
+ // regardless of sensor pixel mode.
+ int calibrationKey =
+ SessionConfigurationUtils::getAppropriateModeTag(ANDROID_LENS_INTRINSIC_CALIBRATION,
+ maxResolution && isStatic);
+ int distortionKey =
+ SessionConfigurationUtils::getAppropriateModeTag(ANDROID_LENS_DISTORTION,
+ maxResolution && isStatic);
- calib = result.find(ANDROID_LENS_INTRINSIC_CALIBRATION);
- distortion = result.find(ANDROID_LENS_DISTORTION);
+ calib = result.find(calibrationKey);
+ distortion = result.find(distortionKey);
if (calib.count != 5) return BAD_VALUE;
if (distortion.count != 5) return BAD_VALUE;
// Skip redoing work if no change to calibration fields
- if (mValidMapping &&
- mFx == calib.data.f[0] &&
- mFy == calib.data.f[1] &&
- mCx == calib.data.f[2] &&
- mCy == calib.data.f[3] &&
- mS == calib.data.f[4]) {
+ if (mapperInfo->mValidMapping &&
+ mapperInfo->mFx == calib.data.f[0] &&
+ mapperInfo->mFy == calib.data.f[1] &&
+ mapperInfo->mCx == calib.data.f[2] &&
+ mapperInfo->mCy == calib.data.f[3] &&
+ mapperInfo->mS == calib.data.f[4]) {
bool noChange = true;
for (size_t i = 0; i < distortion.count; i++) {
- if (mK[i] != distortion.data.f[i]) {
+ if (mapperInfo->mK[i] != distortion.data.f[i]) {
noChange = false;
break;
}
@@ -176,39 +246,39 @@
if (noChange) return OK;
}
- mFx = calib.data.f[0];
- mFy = calib.data.f[1];
- mCx = calib.data.f[2];
- mCy = calib.data.f[3];
- mS = calib.data.f[4];
+ mapperInfo->mFx = calib.data.f[0];
+ mapperInfo->mFy = calib.data.f[1];
+ mapperInfo->mCx = calib.data.f[2];
+ mapperInfo->mCy = calib.data.f[3];
+ mapperInfo->mS = calib.data.f[4];
- mInvFx = 1 / mFx;
- mInvFy = 1 / mFy;
+ mapperInfo->mInvFx = 1 / mapperInfo->mFx;
+ mapperInfo->mInvFy = 1 / mapperInfo->mFy;
for (size_t i = 0; i < distortion.count; i++) {
- mK[i] = distortion.data.f[i];
+ mapperInfo->mK[i] = distortion.data.f[i];
}
- mValidMapping = true;
+ mapperInfo->mValidMapping = true;
// Need to recalculate grid
- mValidGrids = false;
+ mapperInfo->mValidGrids = false;
return OK;
}
status_t DistortionMapper::mapRawToCorrected(int32_t *coordPairs, int coordCount,
- bool clamp, bool simple) {
- if (!mValidMapping) return INVALID_OPERATION;
+ DistortionMapperInfo *mapperInfo, bool clamp, bool simple) {
+ if (!mapperInfo->mValidMapping) return INVALID_OPERATION;
- if (simple) return mapRawToCorrectedSimple(coordPairs, coordCount, clamp);
+ if (simple) return mapRawToCorrectedSimple(coordPairs, coordCount, mapperInfo, clamp);
- if (!mValidGrids) {
- status_t res = buildGrids();
+ if (!mapperInfo->mValidGrids) {
+ status_t res = buildGrids(mapperInfo);
if (res != OK) return res;
}
for (int i = 0; i < coordCount * 2; i += 2) {
- const GridQuad *quad = findEnclosingQuad(coordPairs + i, mDistortedGrid);
+ const GridQuad *quad = findEnclosingQuad(coordPairs + i, mapperInfo->mDistortedGrid);
if (quad == nullptr) {
ALOGE("Raw to corrected mapping failure: No quad found for (%d, %d)",
*(coordPairs + i), *(coordPairs + i + 1));
@@ -244,8 +314,8 @@
// Clamp to within active array
if (clamp) {
- corrX = std::min(mActiveWidth - 1, std::max(0.f, corrX));
- corrY = std::min(mActiveHeight - 1, std::max(0.f, corrY));
+ corrX = std::min(mapperInfo->mActiveWidth - 1, std::max(0.f, corrX));
+ corrY = std::min(mapperInfo->mActiveHeight - 1, std::max(0.f, corrY));
}
coordPairs[i] = static_cast<int32_t>(std::round(corrX));
@@ -256,19 +326,19 @@
}
status_t DistortionMapper::mapRawToCorrectedSimple(int32_t *coordPairs, int coordCount,
- bool clamp) const {
- if (!mValidMapping) return INVALID_OPERATION;
+ const DistortionMapperInfo *mapperInfo, bool clamp) const {
+ if (!mapperInfo->mValidMapping) return INVALID_OPERATION;
- float scaleX = mActiveWidth / mArrayWidth;
- float scaleY = mActiveHeight / mArrayHeight;
+ float scaleX = mapperInfo->mActiveWidth / mapperInfo->mArrayWidth;
+ float scaleY = mapperInfo->mActiveHeight / mapperInfo->mArrayHeight;
for (int i = 0; i < coordCount * 2; i += 2) {
float x = coordPairs[i];
float y = coordPairs[i + 1];
float corrX = x * scaleX;
float corrY = y * scaleY;
if (clamp) {
- corrX = std::min(mActiveWidth - 1, std::max(0.f, corrX));
- corrY = std::min(mActiveHeight - 1, std::max(0.f, corrY));
+ corrX = std::min(mapperInfo->mActiveWidth - 1, std::max(0.f, corrX));
+ corrY = std::min(mapperInfo->mActiveHeight - 1, std::max(0.f, corrY));
}
coordPairs[i] = static_cast<int32_t>(std::round(corrX));
coordPairs[i + 1] = static_cast<int32_t>(std::round(corrY));
@@ -277,9 +347,9 @@
return OK;
}
-status_t DistortionMapper::mapRawRectToCorrected(int32_t *rects, int rectCount, bool clamp,
- bool simple) {
- if (!mValidMapping) return INVALID_OPERATION;
+status_t DistortionMapper::mapRawRectToCorrected(int32_t *rects, int rectCount,
+ DistortionMapperInfo *mapperInfo, bool clamp, bool simple) {
+ if (!mapperInfo->mValidMapping) return INVALID_OPERATION;
for (int i = 0; i < rectCount * 4; i += 4) {
// Map from (l, t, width, height) to (l, t, r, b)
int32_t coords[4] = {
@@ -289,7 +359,7 @@
rects[i + 1] + rects[i + 3] - 1
};
- mapRawToCorrected(coords, 2, clamp, simple);
+ mapRawToCorrected(coords, 2, mapperInfo, clamp, simple);
// Map back to (l, t, width, height)
rects[i] = coords[0];
@@ -301,60 +371,60 @@
return OK;
}
-status_t DistortionMapper::mapCorrectedToRaw(int32_t *coordPairs, int coordCount, bool clamp,
- bool simple) const {
- return mapCorrectedToRawImpl(coordPairs, coordCount, clamp, simple);
+status_t DistortionMapper::mapCorrectedToRaw(int32_t *coordPairs, int coordCount,
+ const DistortionMapperInfo *mapperInfo, bool clamp, bool simple) const {
+ return mapCorrectedToRawImpl(coordPairs, coordCount, mapperInfo, clamp, simple);
}
template<typename T>
-status_t DistortionMapper::mapCorrectedToRawImpl(T *coordPairs, int coordCount, bool clamp,
- bool simple) const {
- if (!mValidMapping) return INVALID_OPERATION;
+status_t DistortionMapper::mapCorrectedToRawImpl(T *coordPairs, int coordCount,
+ const DistortionMapperInfo *mapperInfo, bool clamp, bool simple) const {
+ if (!mapperInfo->mValidMapping) return INVALID_OPERATION;
- if (simple) return mapCorrectedToRawImplSimple(coordPairs, coordCount, clamp);
+ if (simple) return mapCorrectedToRawImplSimple(coordPairs, coordCount, mapperInfo, clamp);
- float activeCx = mCx - mArrayDiffX;
- float activeCy = mCy - mArrayDiffY;
+ float activeCx = mapperInfo->mCx - mapperInfo->mArrayDiffX;
+ float activeCy = mapperInfo->mCy - mapperInfo->mArrayDiffY;
for (int i = 0; i < coordCount * 2; i += 2) {
// Move to normalized space from active array space
- float ywi = (coordPairs[i + 1] - activeCy) * mInvFy;
- float xwi = (coordPairs[i] - activeCx - mS * ywi) * mInvFx;
+ float ywi = (coordPairs[i + 1] - activeCy) * mapperInfo->mInvFy;
+ float xwi = (coordPairs[i] - activeCx - mapperInfo->mS * ywi) * mapperInfo->mInvFx;
// Apply distortion model to calculate raw image coordinates
+ const std::array<float, 5> &kK = mapperInfo->mK;
float rSq = xwi * xwi + ywi * ywi;
- float Fr = 1.f + (mK[0] * rSq) + (mK[1] * rSq * rSq) + (mK[2] * rSq * rSq * rSq);
- float xc = xwi * Fr + (mK[3] * 2 * xwi * ywi) + mK[4] * (rSq + 2 * xwi * xwi);
- float yc = ywi * Fr + (mK[4] * 2 * xwi * ywi) + mK[3] * (rSq + 2 * ywi * ywi);
+ float Fr = 1.f + (kK[0] * rSq) + (kK[1] * rSq * rSq) + (kK[2] * rSq * rSq * rSq);
+ float xc = xwi * Fr + (kK[3] * 2 * xwi * ywi) + kK[4] * (rSq + 2 * xwi * xwi);
+ float yc = ywi * Fr + (kK[4] * 2 * xwi * ywi) + kK[3] * (rSq + 2 * ywi * ywi);
// Move back to image space
- float xr = mFx * xc + mS * yc + mCx;
- float yr = mFy * yc + mCy;
+ float xr = mapperInfo->mFx * xc + mapperInfo->mS * yc + mapperInfo->mCx;
+ float yr = mapperInfo->mFy * yc + mapperInfo->mCy;
// Clamp to within pre-correction active array
if (clamp) {
- xr = std::min(mArrayWidth - 1, std::max(0.f, xr));
- yr = std::min(mArrayHeight - 1, std::max(0.f, yr));
+ xr = std::min(mapperInfo->mArrayWidth - 1, std::max(0.f, xr));
+ yr = std::min(mapperInfo->mArrayHeight - 1, std::max(0.f, yr));
}
coordPairs[i] = static_cast<T>(std::round(xr));
coordPairs[i + 1] = static_cast<T>(std::round(yr));
}
-
return OK;
}
template<typename T>
status_t DistortionMapper::mapCorrectedToRawImplSimple(T *coordPairs, int coordCount,
- bool clamp) const {
- if (!mValidMapping) return INVALID_OPERATION;
+ const DistortionMapperInfo *mapperInfo, bool clamp) const {
+ if (!mapperInfo->mValidMapping) return INVALID_OPERATION;
- float scaleX = mArrayWidth / mActiveWidth;
- float scaleY = mArrayHeight / mActiveHeight;
+ float scaleX = mapperInfo->mArrayWidth / mapperInfo->mActiveWidth;
+ float scaleY = mapperInfo->mArrayHeight / mapperInfo->mActiveHeight;
for (int i = 0; i < coordCount * 2; i += 2) {
float x = coordPairs[i];
float y = coordPairs[i + 1];
float rawX = x * scaleX;
float rawY = y * scaleY;
if (clamp) {
- rawX = std::min(mArrayWidth - 1, std::max(0.f, rawX));
- rawY = std::min(mArrayHeight - 1, std::max(0.f, rawY));
+ rawX = std::min(mapperInfo->mArrayWidth - 1, std::max(0.f, rawX));
+ rawY = std::min(mapperInfo->mArrayHeight - 1, std::max(0.f, rawY));
}
coordPairs[i] = static_cast<T>(std::round(rawX));
coordPairs[i + 1] = static_cast<T>(std::round(rawY));
@@ -363,9 +433,9 @@
return OK;
}
-status_t DistortionMapper::mapCorrectedRectToRaw(int32_t *rects, int rectCount, bool clamp,
- bool simple) const {
- if (!mValidMapping) return INVALID_OPERATION;
+status_t DistortionMapper::mapCorrectedRectToRaw(int32_t *rects, int rectCount,
+ const DistortionMapperInfo *mapperInfo, bool clamp, bool simple) const {
+ if (!mapperInfo->mValidMapping) return INVALID_OPERATION;
for (int i = 0; i < rectCount * 4; i += 4) {
// Map from (l, t, width, height) to (l, t, r, b)
@@ -376,7 +446,7 @@
rects[i + 1] + rects[i + 3] - 1
};
- mapCorrectedToRaw(coords, 2, clamp, simple);
+ mapCorrectedToRaw(coords, 2, mapperInfo, clamp, simple);
// Map back to (l, t, width, height)
rects[i] = coords[0];
@@ -388,37 +458,37 @@
return OK;
}
-status_t DistortionMapper::buildGrids() {
- if (mCorrectedGrid.size() != kGridSize * kGridSize) {
- mCorrectedGrid.resize(kGridSize * kGridSize);
- mDistortedGrid.resize(kGridSize * kGridSize);
+status_t DistortionMapper::buildGrids(DistortionMapperInfo *mapperInfo) {
+ if (mapperInfo->mCorrectedGrid.size() != kGridSize * kGridSize) {
+ mapperInfo->mCorrectedGrid.resize(kGridSize * kGridSize);
+ mapperInfo->mDistortedGrid.resize(kGridSize * kGridSize);
}
- float gridMargin = mArrayWidth * kGridMargin;
- float gridSpacingX = (mArrayWidth + 2 * gridMargin) / kGridSize;
- float gridSpacingY = (mArrayHeight + 2 * gridMargin) / kGridSize;
+ float gridMargin = mapperInfo->mArrayWidth * kGridMargin;
+ float gridSpacingX = (mapperInfo->mArrayWidth + 2 * gridMargin) / kGridSize;
+ float gridSpacingY = (mapperInfo->mArrayHeight + 2 * gridMargin) / kGridSize;
size_t index = 0;
float x = -gridMargin;
for (size_t i = 0; i < kGridSize; i++, x += gridSpacingX) {
float y = -gridMargin;
for (size_t j = 0; j < kGridSize; j++, y += gridSpacingY, index++) {
- mCorrectedGrid[index].src = nullptr;
- mCorrectedGrid[index].coords = {
+ mapperInfo->mCorrectedGrid[index].src = nullptr;
+ mapperInfo->mCorrectedGrid[index].coords = {
x, y,
x + gridSpacingX, y,
x + gridSpacingX, y + gridSpacingY,
x, y + gridSpacingY
};
- mDistortedGrid[index].src = &mCorrectedGrid[index];
- mDistortedGrid[index].coords = mCorrectedGrid[index].coords;
- status_t res = mapCorrectedToRawImpl(mDistortedGrid[index].coords.data(), 4,
- /*clamp*/false, /*simple*/false);
+ mapperInfo->mDistortedGrid[index].src = &(mapperInfo->mCorrectedGrid[index]);
+ mapperInfo->mDistortedGrid[index].coords = mapperInfo->mCorrectedGrid[index].coords;
+ status_t res = mapCorrectedToRawImpl(mapperInfo->mDistortedGrid[index].coords.data(), 4,
+ mapperInfo, /*clamp*/false, /*simple*/false);
if (res != OK) return res;
}
}
- mValidGrids = true;
+ mapperInfo->mValidGrids = true;
return OK;
}
@@ -485,7 +555,7 @@
float det = b * b - 4 * a * c;
if (det < 0) {
- // Sanity check - should not happen if pt is within the quad
+ // Validation check - should not happen if pt is within the quad
ALOGE("Bad determinant! a: %f, b: %f, c: %f, det: %f", a,b,c,det);
return -1;
}
diff --git a/services/camera/libcameraservice/device3/DistortionMapper.h b/services/camera/libcameraservice/device3/DistortionMapper.h
index 7dcb67b..96f4fda 100644
--- a/services/camera/libcameraservice/device3/DistortionMapper.h
+++ b/services/camera/libcameraservice/device3/DistortionMapper.h
@@ -32,18 +32,16 @@
* Utilities to transform between raw (distorted) and warped (corrected) coordinate systems
* for cameras that support geometric distortion
*/
-class DistortionMapper : private CoordinateMapper {
+class DistortionMapper : public CoordinateMapper {
public:
DistortionMapper();
DistortionMapper(const DistortionMapper& other) :
- mValidMapping(other.mValidMapping), mValidGrids(other.mValidGrids),
- mFx(other.mFx), mFy(other.mFy), mCx(other.mCx), mCy(other.mCy), mS(other.mS),
- mInvFx(other.mInvFx), mInvFy(other.mInvFy), mK(other.mK),
- mArrayWidth(other.mArrayWidth), mArrayHeight(other.mArrayHeight),
- mActiveWidth(other.mActiveWidth), mActiveHeight(other.mActiveHeight),
- mArrayDiffX(other.mArrayDiffX), mArrayDiffY(other.mArrayDiffY),
- mCorrectedGrid(other.mCorrectedGrid), mDistortedGrid(other.mDistortedGrid) {}
+ mDistortionMapperInfo(other.mDistortionMapperInfo),
+ mDistortionMapperInfoMaximumResolution(other.mDistortionMapperInfoMaximumResolution) {
+ initRemappedKeys(); }
+
+ void initRemappedKeys() override;
/**
* Check whether distortion correction is supported by the camera HAL
@@ -72,10 +70,14 @@
public: // Visible for testing. Not guarded by mutex; do not use concurrently
+
+ struct DistortionMapperInfo;
+
/**
* Update lens calibration from capture results or equivalent
*/
- status_t updateCalibration(const CameraMetadata &result);
+ status_t updateCalibration(const CameraMetadata &result, bool isStatic = false,
+ bool maxResolution = false);
/**
* Transform from distorted (original) to corrected (warped) coordinates.
@@ -86,8 +88,8 @@
* clamp: Whether to clamp the result to the bounds of the active array
* simple: Whether to do complex correction or just a simple linear map
*/
- status_t mapRawToCorrected(int32_t *coordPairs, int coordCount, bool clamp,
- bool simple = true);
+ status_t mapRawToCorrected(int32_t *coordPairs, int coordCount,
+ DistortionMapperInfo *mapperInfo, bool clamp, bool simple = true);
/**
* Transform from distorted (original) to corrected (warped) coordinates.
@@ -98,8 +100,8 @@
* clamp: Whether to clamp the result to the bounds of the active array
* simple: Whether to do complex correction or just a simple linear map
*/
- status_t mapRawRectToCorrected(int32_t *rects, int rectCount, bool clamp,
- bool simple = true);
+ status_t mapRawRectToCorrected(int32_t *rects, int rectCount,
+ DistortionMapperInfo *mapperInfo, bool clamp, bool simple = true);
/**
* Transform from corrected (warped) to distorted (original) coordinates.
@@ -110,8 +112,8 @@
* clamp: Whether to clamp the result to the bounds of the precorrection active array
* simple: Whether to do complex correction or just a simple linear map
*/
- status_t mapCorrectedToRaw(int32_t* coordPairs, int coordCount, bool clamp,
- bool simple = true) const;
+ status_t mapCorrectedToRaw(int32_t* coordPairs, int coordCount,
+ const DistortionMapperInfo *mapperInfo, bool clamp, bool simple = true) const;
/**
* Transform from corrected (warped) to distorted (original) coordinates.
@@ -122,8 +124,8 @@
* clamp: Whether to clamp the result to the bounds of the precorrection active array
* simple: Whether to do complex correction or just a simple linear map
*/
- status_t mapCorrectedRectToRaw(int32_t *rects, int rectCount, bool clamp,
- bool simple = true) const;
+ status_t mapCorrectedRectToRaw(int32_t *rects, int rectCount,
+ const DistortionMapperInfo *mapperInfo, bool clamp, bool simple = true) const;
struct GridQuad {
// Source grid quad, or null
@@ -133,6 +135,28 @@
std::array<float, 8> coords;
};
+ struct DistortionMapperInfo {
+ bool mValidMapping = false;
+ bool mValidGrids = false;
+
+ // intrisic parameters, in pixels
+ float mFx, mFy, mCx, mCy, mS;
+ // pre-calculated inverses for speed
+ float mInvFx, mInvFy;
+ // radial/tangential distortion parameters
+ std::array<float, 5> mK;
+
+ // pre-correction active array dimensions
+ float mArrayWidth, mArrayHeight;
+ // active array dimensions
+ float mActiveWidth, mActiveHeight;
+ // corner offsets between pre-correction and active arrays
+ float mArrayDiffX, mArrayDiffY;
+
+ std::vector<GridQuad> mCorrectedGrid;
+ std::vector<GridQuad> mDistortedGrid;
+ };
+
// Find which grid quad encloses the point; returns null if none do
static const GridQuad* findEnclosingQuad(
const int32_t pt[2], const std::vector<GridQuad>& grid);
@@ -150,6 +174,11 @@
// if it is false, then an interpolation coordinate for edges E14 and E23 is found.
static float calculateUorV(const int32_t pt[2], const GridQuad& quad, bool calculateU);
+ DistortionMapperInfo *getMapperInfo(bool maxResolution = false) {
+ return maxResolution ? &mDistortionMapperInfoMaximumResolution :
+ &mDistortionMapperInfo;
+ };
+
private:
mutable std::mutex mMutex;
@@ -160,39 +189,28 @@
// Fuzziness for float inequality tests
constexpr static float kFloatFuzz = 1e-4;
+ bool mMaxResolution = false;
+
+ status_t setupStaticInfoLocked(const CameraMetadata &deviceInfo, bool maxResolution);
+
// Single implementation for various mapCorrectedToRaw methods
template<typename T>
- status_t mapCorrectedToRawImpl(T* coordPairs, int coordCount, bool clamp, bool simple) const;
+ status_t mapCorrectedToRawImpl(T* coordPairs, int coordCount,
+ const DistortionMapperInfo *mapperInfo, bool clamp, bool simple) const;
// Simple linear interpolation option
template<typename T>
- status_t mapCorrectedToRawImplSimple(T* coordPairs, int coordCount, bool clamp) const;
+ status_t mapCorrectedToRawImplSimple(T* coordPairs, int coordCount,
+ const DistortionMapperInfo *mapperInfo, bool clamp) const;
- status_t mapRawToCorrectedSimple(int32_t *coordPairs, int coordCount, bool clamp) const;
+ status_t mapRawToCorrectedSimple(int32_t *coordPairs, int coordCount,
+ const DistortionMapperInfo *mapperInfo, bool clamp) const;
// Utility to create reverse mapping grids
- status_t buildGrids();
+ status_t buildGrids(DistortionMapperInfo *mapperInfo);
-
- bool mValidMapping;
- bool mValidGrids;
-
- // intrisic parameters, in pixels
- float mFx, mFy, mCx, mCy, mS;
- // pre-calculated inverses for speed
- float mInvFx, mInvFy;
- // radial/tangential distortion parameters
- std::array<float, 5> mK;
-
- // pre-correction active array dimensions
- float mArrayWidth, mArrayHeight;
- // active array dimensions
- float mActiveWidth, mActiveHeight;
- // corner offsets between pre-correction and active arrays
- float mArrayDiffX, mArrayDiffY;
-
- std::vector<GridQuad> mCorrectedGrid;
- std::vector<GridQuad> mDistortedGrid;
+ DistortionMapperInfo mDistortionMapperInfo;
+ DistortionMapperInfo mDistortionMapperInfoMaximumResolution;
}; // class DistortionMapper
diff --git a/services/camera/libcameraservice/device3/InFlightRequest.h b/services/camera/libcameraservice/device3/InFlightRequest.h
index da4f228..523a2c7 100644
--- a/services/camera/libcameraservice/device3/InFlightRequest.h
+++ b/services/camera/libcameraservice/device3/InFlightRequest.h
@@ -24,8 +24,6 @@
#include <utils/String8.h>
#include <utils/Timers.h>
-#include "hardware/camera3.h"
-
#include "common/CameraDeviceBase.h"
namespace android {
@@ -75,7 +73,7 @@
// return from HAL but framework has not yet received the shutter
// event. They will be returned to the streams when framework receives
// the shutter event.
- Vector<camera3_stream_buffer_t> pendingOutputBuffers;
+ Vector<camera_stream_buffer_t> pendingOutputBuffers;
// Whether this inflight request's shutter and result callback are to be
// called. The policy is that if the request is the last one in the constrained
@@ -98,7 +96,10 @@
ERROR_BUF_STRATEGY errorBufStrategy;
// The physical camera ids being requested.
- std::set<String8> physicalCameraIds;
+ // For request on a physical camera stream, the inside set contains one Id
+ // For request on a stream group containing physical camera streams, the
+ // inside set contains all stream Ids in the group.
+ std::set<std::set<String8>> physicalCameraIds;
// Map of physicalCameraId <-> Metadata
std::vector<PhysicalCaptureResultInfo> physicalMetadatas;
@@ -115,6 +116,9 @@
// Requested camera ids (both logical and physical) with zoomRatio != 1.0f
std::set<std::string> cameraIdsWithZoom;
+ // Time of capture request (from systemTime) in Ns
+ nsecs_t requestTimeNs;
+
// What shared surfaces an output should go to
SurfaceMap outputSurfaces;
@@ -135,14 +139,15 @@
errorBufStrategy(ERROR_BUF_CACHE),
stillCapture(false),
zslCapture(false),
- rotateAndCropAuto(false) {
+ rotateAndCropAuto(false),
+ requestTimeNs(0) {
}
InFlightRequest(int numBuffers, CaptureResultExtras extras, bool hasInput,
bool hasAppCallback, nsecs_t maxDuration,
- const std::set<String8>& physicalCameraIdSet, bool isStillCapture,
+ const std::set<std::set<String8>>& physicalCameraIdSet, bool isStillCapture,
bool isZslCapture, bool rotateAndCropAuto, const std::set<std::string>& idsWithZoom,
- const SurfaceMap& outSurfaces = SurfaceMap{}) :
+ nsecs_t requestNs, const SurfaceMap& outSurfaces = SurfaceMap{}) :
shutterTimestamp(0),
sensorTimestamp(0),
requestStatus(OK),
@@ -159,6 +164,7 @@
zslCapture(isZslCapture),
rotateAndCropAuto(rotateAndCropAuto),
cameraIdsWithZoom(idsWithZoom),
+ requestTimeNs(requestNs),
outputSurfaces(outSurfaces) {
}
};
diff --git a/services/camera/libcameraservice/device3/RotateAndCropMapper.cpp b/services/camera/libcameraservice/device3/RotateAndCropMapper.cpp
index 3718f54..a02e5f6 100644
--- a/services/camera/libcameraservice/device3/RotateAndCropMapper.cpp
+++ b/services/camera/libcameraservice/device3/RotateAndCropMapper.cpp
@@ -27,6 +27,18 @@
namespace camera3 {
+void RotateAndCropMapper::initRemappedKeys() {
+ mRemappedKeys.insert(
+ kMeteringRegionsToCorrect.begin(),
+ kMeteringRegionsToCorrect.end());
+ mRemappedKeys.insert(
+ kResultPointsToCorrectNoClamp.begin(),
+ kResultPointsToCorrectNoClamp.end());
+
+ mRemappedKeys.insert(ANDROID_SCALER_ROTATE_AND_CROP);
+ mRemappedKeys.insert(ANDROID_SCALER_CROP_REGION);
+}
+
bool RotateAndCropMapper::isNeeded(const CameraMetadata* deviceInfo) {
auto entry = deviceInfo->find(ANDROID_SCALER_AVAILABLE_ROTATE_AND_CROP_MODES);
for (size_t i = 0; i < entry.count; i++) {
@@ -36,6 +48,8 @@
}
RotateAndCropMapper::RotateAndCropMapper(const CameraMetadata* deviceInfo) {
+ initRemappedKeys();
+
auto entry = deviceInfo->find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
if (entry.count != 4) return;
diff --git a/services/camera/libcameraservice/device3/RotateAndCropMapper.h b/services/camera/libcameraservice/device3/RotateAndCropMapper.h
index 459e27f..f9e2263 100644
--- a/services/camera/libcameraservice/device3/RotateAndCropMapper.h
+++ b/services/camera/libcameraservice/device3/RotateAndCropMapper.h
@@ -32,12 +32,14 @@
* Utilities to transform between unrotated and rotated-and-cropped coordinate systems
* for cameras that support SCALER_ROTATE_AND_CROP controls in AUTO mode.
*/
-class RotateAndCropMapper : private CoordinateMapper {
+class RotateAndCropMapper : public CoordinateMapper {
public:
static bool isNeeded(const CameraMetadata* deviceInfo);
RotateAndCropMapper(const CameraMetadata* deviceInfo);
+ void initRemappedKeys() override;
+
/**
* Adjust capture request assuming rotate and crop AUTO is enabled
*/
diff --git a/services/camera/libcameraservice/device3/StatusTracker.cpp b/services/camera/libcameraservice/device3/StatusTracker.cpp
index 723b5c2..ea1f2c1 100644
--- a/services/camera/libcameraservice/device3/StatusTracker.cpp
+++ b/services/camera/libcameraservice/device3/StatusTracker.cpp
@@ -40,7 +40,7 @@
StatusTracker::~StatusTracker() {
}
-int StatusTracker::addComponent() {
+int StatusTracker::addComponent(std::string componentName) {
int id;
ssize_t err;
{
@@ -49,8 +49,12 @@
ALOGV("%s: Adding new component %d", __FUNCTION__, id);
err = mStates.add(id, IDLE);
- ALOGE_IF(err < 0, "%s: Can't add new component %d: %s (%zd)",
- __FUNCTION__, id, strerror(-err), err);
+ if (componentName.empty()) {
+ componentName = std::to_string(id);
+ }
+ mComponentNames.add(id, componentName);
+ ALOGE_IF(err < 0, "%s: Can't add new component %d (%s): %s (%zd)",
+ __FUNCTION__, id, componentName.c_str(), strerror(-err), err);
}
if (err >= 0) {
@@ -68,6 +72,7 @@
Mutex::Autolock l(mLock);
ALOGV("%s: Removing component %d", __FUNCTION__, id);
idx = mStates.removeItem(id);
+ mComponentNames.removeItem(id);
}
if (idx >= 0) {
@@ -80,6 +85,20 @@
}
+void StatusTracker::dumpActiveComponents() {
+ Mutex::Autolock l(mLock);
+ if (mDeviceState == IDLE) {
+ ALOGI("%s: all components are IDLE", __FUNCTION__);
+ return;
+ }
+ for (size_t i = 0; i < mStates.size(); i++) {
+ if (mStates.valueAt(i) == ACTIVE) {
+ ALOGI("%s: component %d (%s) is active", __FUNCTION__, mStates.keyAt(i),
+ mComponentNames.valueAt(i).c_str());
+ }
+ }
+}
+
void StatusTracker::markComponentIdle(int id, const sp<Fence>& componentFence) {
markComponent(id, IDLE, componentFence);
}
diff --git a/services/camera/libcameraservice/device3/StatusTracker.h b/services/camera/libcameraservice/device3/StatusTracker.h
index 3a1d85c..069bff6 100644
--- a/services/camera/libcameraservice/device3/StatusTracker.h
+++ b/services/camera/libcameraservice/device3/StatusTracker.h
@@ -17,13 +17,13 @@
#ifndef ANDROID_SERVERS_CAMERA3_STATUSTRACKER_H
#define ANDROID_SERVERS_CAMERA3_STATUSTRACKER_H
+#include <string>
#include <utils/Condition.h>
#include <utils/Errors.h>
#include <utils/List.h>
#include <utils/Mutex.h>
#include <utils/Thread.h>
#include <utils/KeyedVector.h>
-#include <hardware/camera3.h>
#include "common/CameraDeviceBase.h"
@@ -54,7 +54,7 @@
// Add a component to track; returns non-negative unique ID for the new
// component on success, negative error code on failure.
// New components start in the idle state.
- int addComponent();
+ int addComponent(std::string componentName);
// Remove existing component from idle tracking. Ignores unknown IDs
void removeComponent(int id);
@@ -68,6 +68,8 @@
// Set the state of a tracked component to be active. Ignores unknown IDs.
void markComponentActive(int id);
+ void dumpActiveComponents();
+
virtual void requestExit();
protected:
@@ -105,6 +107,7 @@
// Current component states
KeyedVector<int, ComponentState> mStates;
+ KeyedVector<int, std::string> mComponentNames;
// Merged fence for all processed state changes
sp<Fence> mIdleFence;
// Current overall device state
diff --git a/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp b/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp
index 4e98bf6..1a39510 100644
--- a/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp
+++ b/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp
@@ -20,11 +20,25 @@
#include <algorithm>
#include "device3/ZoomRatioMapper.h"
+#include "utils/SessionConfigurationUtils.h"
namespace android {
namespace camera3 {
+void ZoomRatioMapper::initRemappedKeys() {
+ mRemappedKeys.insert(
+ kMeteringRegionsToCorrect.begin(),
+ kMeteringRegionsToCorrect.end());
+ mRemappedKeys.insert(
+ kRectsToCorrect.begin(),
+ kRectsToCorrect.end());
+ mRemappedKeys.insert(
+ kResultPointsToCorrectNoClamp.begin(),
+ kResultPointsToCorrectNoClamp.end());
+
+ mRemappedKeys.insert(ANDROID_CONTROL_ZOOM_RATIO);
+}
status_t ZoomRatioMapper::initZoomRatioInTemplate(CameraMetadata *request) {
camera_metadata_entry_t entry;
@@ -115,41 +129,120 @@
return OK;
}
+static bool getArrayWidthAndHeight(const CameraMetadata *deviceInfo,
+ int32_t arrayTag, int32_t *width, int32_t *height) {
+ if (width == nullptr || height == nullptr) {
+ ALOGE("%s: width / height nullptr", __FUNCTION__);
+ return false;
+ }
+ camera_metadata_ro_entry_t entry;
+ entry = deviceInfo->find(arrayTag);
+ if (entry.count != 4) return false;
+ *width = entry.data.i32[2];
+ *height = entry.data.i32[3];
+ return true;
+}
+
ZoomRatioMapper::ZoomRatioMapper(const CameraMetadata* deviceInfo,
bool supportNativeZoomRatio, bool usePrecorrectArray) {
- camera_metadata_ro_entry_t entry;
+ initRemappedKeys();
- 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];
+ int32_t arrayW = 0;
+ int32_t arrayH = 0;
+ int32_t arrayMaximumResolutionW = 0;
+ int32_t arrayMaximumResolutionH = 0;
+ int32_t activeW = 0;
+ int32_t activeH = 0;
+ int32_t activeMaximumResolutionW = 0;
+ int32_t activeMaximumResolutionH = 0;
- 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 (!getArrayWidthAndHeight(deviceInfo, ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
+ &arrayW, &arrayH)) {
+ ALOGE("%s: Couldn't get pre correction active array size", __FUNCTION__);
+ return;
+ }
+ if (!getArrayWidthAndHeight(deviceInfo, ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
+ &activeW, &activeH)) {
+ ALOGE("%s: Couldn't get active array size", __FUNCTION__);
+ return;
+ }
+
+ bool isUltraHighResolutionSensor =
+ camera3::SessionConfigurationUtils::isUltraHighResolutionSensor(*deviceInfo);
+ if (isUltraHighResolutionSensor) {
+ if (!getArrayWidthAndHeight(deviceInfo,
+ ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION,
+ &arrayMaximumResolutionW, &arrayMaximumResolutionH)) {
+ ALOGE("%s: Couldn't get maximum resolution pre correction active array size",
+ __FUNCTION__);
+ return;
+ }
+ if (!getArrayWidthAndHeight(deviceInfo,
+ ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION,
+ &activeMaximumResolutionW, &activeMaximumResolutionH)) {
+ ALOGE("%s: Couldn't get maximum resolution pre correction active array size",
+ __FUNCTION__);
+ return;
+ }
+ }
if (usePrecorrectArray) {
mArrayWidth = arrayW;
mArrayHeight = arrayH;
+ mArrayWidthMaximumResolution = arrayMaximumResolutionW;
+ mArrayHeightMaximumResolution = arrayMaximumResolutionH;
} else {
mArrayWidth = activeW;
mArrayHeight = activeH;
+ mArrayWidthMaximumResolution = activeMaximumResolutionW;
+ mArrayHeightMaximumResolution = activeMaximumResolutionH;
}
mHalSupportsZoomRatio = supportNativeZoomRatio;
- ALOGV("%s: array size: %d x %d, mHalSupportsZoomRatio %d",
- __FUNCTION__, mArrayWidth, mArrayHeight, mHalSupportsZoomRatio);
+ ALOGV("%s: array size: %d x %d, full res array size: %d x %d, mHalSupportsZoomRatio %d",
+ __FUNCTION__, mArrayWidth, mArrayHeight, mArrayWidthMaximumResolution,
+ mArrayHeightMaximumResolution, mHalSupportsZoomRatio);
mIsValid = true;
}
+status_t ZoomRatioMapper::getArrayDimensionsToBeUsed(const CameraMetadata *settings,
+ int32_t *arrayWidth, int32_t *arrayHeight) {
+ if (settings == nullptr || arrayWidth == nullptr || arrayHeight == nullptr) {
+ return BAD_VALUE;
+ }
+ // First we get the sensorPixelMode from the settings metadata.
+ int32_t sensorPixelMode = ANDROID_SENSOR_PIXEL_MODE_DEFAULT;
+ camera_metadata_ro_entry sensorPixelModeEntry = settings->find(ANDROID_SENSOR_PIXEL_MODE);
+ if (sensorPixelModeEntry.count != 0) {
+ sensorPixelMode = sensorPixelModeEntry.data.u8[0];
+ if (sensorPixelMode != ANDROID_SENSOR_PIXEL_MODE_DEFAULT &&
+ sensorPixelMode != ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) {
+ ALOGE("%s: Request sensor pixel mode is not one of the valid values %d",
+ __FUNCTION__, sensorPixelMode);
+ return BAD_VALUE;
+ }
+ }
+ if (sensorPixelMode == ANDROID_SENSOR_PIXEL_MODE_DEFAULT) {
+ *arrayWidth = mArrayWidth;
+ *arrayHeight = mArrayHeight;
+ } else {
+ *arrayWidth = mArrayWidthMaximumResolution;
+ *arrayHeight = mArrayHeightMaximumResolution;
+ }
+ return OK;
+}
+
status_t ZoomRatioMapper::updateCaptureRequest(CameraMetadata* request) {
if (!mIsValid) return INVALID_OPERATION;
status_t res = OK;
bool zoomRatioIs1 = true;
camera_metadata_entry_t entry;
-
+ int arrayHeight, arrayWidth = 0;
+ res = getArrayDimensionsToBeUsed(request, &arrayWidth, &arrayHeight);
+ if (res != OK) {
+ return res;
+ }
entry = request->find(ANDROID_CONTROL_ZOOM_RATIO);
if (entry.count == 1 && entry.data.f[0] != 1.0f) {
zoomRatioIs1 = false;
@@ -159,19 +252,19 @@
if (cropRegionEntry.count == 4) {
int cropWidth = cropRegionEntry.data.i32[2];
int cropHeight = cropRegionEntry.data.i32[3];
- if (cropWidth < mArrayWidth && cropHeight < mArrayHeight) {
+ if (cropWidth < arrayWidth && cropHeight < arrayHeight) {
cropRegionEntry.data.i32[0] = 0;
cropRegionEntry.data.i32[1] = 0;
- cropRegionEntry.data.i32[2] = mArrayWidth;
- cropRegionEntry.data.i32[3] = mArrayHeight;
+ cropRegionEntry.data.i32[2] = arrayWidth;
+ cropRegionEntry.data.i32[3] = arrayHeight;
}
}
}
if (mHalSupportsZoomRatio && zoomRatioIs1) {
- res = separateZoomFromCropLocked(request, false/*isResult*/);
+ res = separateZoomFromCropLocked(request, false/*isResult*/, arrayWidth, arrayHeight);
} else if (!mHalSupportsZoomRatio && !zoomRatioIs1) {
- res = combineZoomAndCropLocked(request, false/*isResult*/);
+ res = combineZoomAndCropLocked(request, false/*isResult*/, arrayWidth, arrayHeight);
}
// If CONTROL_ZOOM_RATIO is in request, but HAL doesn't support
@@ -188,10 +281,15 @@
status_t res = OK;
+ int arrayHeight, arrayWidth = 0;
+ res = getArrayDimensionsToBeUsed(result, &arrayWidth, &arrayHeight);
+ if (res != OK) {
+ return res;
+ }
if (mHalSupportsZoomRatio && requestedZoomRatioIs1) {
- res = combineZoomAndCropLocked(result, true/*isResult*/);
+ res = combineZoomAndCropLocked(result, true/*isResult*/, arrayWidth, arrayHeight);
} else if (!mHalSupportsZoomRatio && !requestedZoomRatioIs1) {
- res = separateZoomFromCropLocked(result, true/*isResult*/);
+ res = separateZoomFromCropLocked(result, true/*isResult*/, arrayWidth, arrayHeight);
} else {
camera_metadata_entry_t entry = result->find(ANDROID_CONTROL_ZOOM_RATIO);
if (entry.count == 0) {
@@ -203,16 +301,22 @@
return res;
}
-float ZoomRatioMapper::deriveZoomRatio(const CameraMetadata* metadata) {
+status_t ZoomRatioMapper::deriveZoomRatio(const CameraMetadata* metadata, float *zoomRatioRet,
+ int arrayWidth, int arrayHeight) {
+ if (metadata == nullptr || zoomRatioRet == nullptr) {
+ return BAD_VALUE;
+ }
float zoomRatio = 1.0;
camera_metadata_ro_entry_t entry;
entry = metadata->find(ANDROID_SCALER_CROP_REGION);
- if (entry.count != 4) return zoomRatio;
-
+ if (entry.count != 4) {
+ *zoomRatioRet = 1;
+ return OK;
+ }
// Center of the preCorrection/active size
- float arrayCenterX = mArrayWidth / 2.0;
- float arrayCenterY = mArrayHeight / 2.0;
+ float arrayCenterX = arrayWidth / 2.0;
+ float arrayCenterY = arrayHeight / 2.0;
// Re-map crop region to coordinate system centered to (arrayCenterX,
// arrayCenterY).
@@ -222,22 +326,30 @@
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);
+ float zoomRatioLeft = std::max(arrayWidth / (2 * cropRegionLeft), 1.0f);
+ float zoomRatioTop = std::max(arrayHeight / (2 * cropRegionTop), 1.0f);
+ float zoomRatioRight = std::max(arrayWidth / (2 * cropRegionRight), 1.0f);
+ float zoomRatioBottom = std::max(arrayHeight / (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;
+ *zoomRatioRet = zoomRatio;
+ return OK;
}
-status_t ZoomRatioMapper::separateZoomFromCropLocked(CameraMetadata* metadata, bool isResult) {
- status_t res;
- float zoomRatio = deriveZoomRatio(metadata);
+status_t ZoomRatioMapper::separateZoomFromCropLocked(CameraMetadata* metadata, bool isResult,
+ int arrayWidth, int arrayHeight) {
+ float zoomRatio = 1.0;
+ status_t res = deriveZoomRatio(metadata, &zoomRatio, arrayWidth, arrayHeight);
+
+ if (res != OK) {
+ ALOGE("%s: Failed to derive zoom ratio: %s(%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
// Update zoomRatio metadata tag
res = metadata->update(ANDROID_CONTROL_ZOOM_RATIO, &zoomRatio, 1);
@@ -257,12 +369,14 @@
continue;
}
// Top left (inclusive)
- scaleCoordinates(entry.data.i32 + j, 1, zoomRatio, true /*clamp*/);
+ scaleCoordinates(entry.data.i32 + j, 1, zoomRatio, true /*clamp*/, arrayWidth,
+ arrayHeight);
// Bottom right (exclusive): Use adjacent inclusive pixel to
// calculate.
entry.data.i32[j+2] -= 1;
entry.data.i32[j+3] -= 1;
- scaleCoordinates(entry.data.i32 + j + 2, 1, zoomRatio, true /*clamp*/);
+ scaleCoordinates(entry.data.i32 + j + 2, 1, zoomRatio, true /*clamp*/, arrayWidth,
+ arrayHeight);
entry.data.i32[j+2] += 1;
entry.data.i32[j+3] += 1;
}
@@ -270,20 +384,22 @@
for (auto rect : kRectsToCorrect) {
entry = metadata->find(rect);
- scaleRects(entry.data.i32, entry.count / 4, zoomRatio);
+ scaleRects(entry.data.i32, entry.count / 4, zoomRatio, arrayWidth, arrayHeight);
}
if (isResult) {
for (auto pts : kResultPointsToCorrectNoClamp) {
entry = metadata->find(pts);
- scaleCoordinates(entry.data.i32, entry.count / 2, zoomRatio, false /*clamp*/);
+ scaleCoordinates(entry.data.i32, entry.count / 2, zoomRatio, false /*clamp*/,
+ arrayWidth, arrayHeight);
}
}
return OK;
}
-status_t ZoomRatioMapper::combineZoomAndCropLocked(CameraMetadata* metadata, bool isResult) {
+status_t ZoomRatioMapper::combineZoomAndCropLocked(CameraMetadata* metadata, bool isResult,
+ int arrayWidth, int arrayHeight) {
float zoomRatio = 1.0f;
camera_metadata_entry_t entry;
entry = metadata->find(ANDROID_CONTROL_ZOOM_RATIO);
@@ -292,7 +408,6 @@
}
// 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) {
@@ -301,29 +416,32 @@
continue;
}
// Top-left (inclusive)
- scaleCoordinates(entry.data.i32 + j, 1, 1.0 / zoomRatio, true /*clamp*/);
+ scaleCoordinates(entry.data.i32 + j, 1, 1.0 / zoomRatio, true /*clamp*/, arrayWidth,
+ arrayHeight);
// Bottom-right (exclusive): Use adjacent inclusive pixel to
// calculate.
entry.data.i32[j+2] -= 1;
entry.data.i32[j+3] -= 1;
- scaleCoordinates(entry.data.i32 + j + 2, 1, 1.0 / zoomRatio, true /*clamp*/);
+ scaleCoordinates(entry.data.i32 + j + 2, 1, 1.0 / zoomRatio, true /*clamp*/, arrayWidth,
+ arrayHeight);
entry.data.i32[j+2] += 1;
entry.data.i32[j+3] += 1;
}
}
for (auto rect : kRectsToCorrect) {
entry = metadata->find(rect);
- scaleRects(entry.data.i32, entry.count / 4, 1.0 / zoomRatio);
+ scaleRects(entry.data.i32, entry.count / 4, 1.0 / zoomRatio, arrayWidth, arrayHeight);
}
if (isResult) {
for (auto pts : kResultPointsToCorrectNoClamp) {
entry = metadata->find(pts);
- scaleCoordinates(entry.data.i32, entry.count / 2, 1.0 / zoomRatio, false /*clamp*/);
+ scaleCoordinates(entry.data.i32, entry.count / 2, 1.0 / zoomRatio, false /*clamp*/,
+ arrayWidth, arrayHeight);
}
}
zoomRatio = 1.0;
- res = metadata->update(ANDROID_CONTROL_ZOOM_RATIO, &zoomRatio, 1);
+ status_t res = metadata->update(ANDROID_CONTROL_ZOOM_RATIO, &zoomRatio, 1);
if (res != OK) {
return res;
}
@@ -332,7 +450,7 @@
}
void ZoomRatioMapper::scaleCoordinates(int32_t* coordPairs, int coordCount,
- float scaleRatio, bool clamp) {
+ float scaleRatio, bool clamp, int32_t arrayWidth, int32_t arrayHeight) {
// A pixel's coordinate is represented by the position of its top-left corner.
// To avoid the rounding error, we use the coordinate for the center of the
// pixel instead:
@@ -345,18 +463,18 @@
for (int i = 0; i < coordCount * 2; i += 2) {
float x = coordPairs[i];
float y = coordPairs[i + 1];
- float xCentered = x - (mArrayWidth - 2) / 2;
- float yCentered = y - (mArrayHeight - 2) / 2;
+ float xCentered = x - (arrayWidth - 2) / 2;
+ float yCentered = y - (arrayHeight - 2) / 2;
float scaledX = xCentered * scaleRatio;
float scaledY = yCentered * scaleRatio;
- scaledX += (mArrayWidth - 2) / 2;
- scaledY += (mArrayHeight - 2) / 2;
+ scaledX += (arrayWidth - 2) / 2;
+ scaledY += (arrayHeight - 2) / 2;
coordPairs[i] = static_cast<int32_t>(std::round(scaledX));
coordPairs[i+1] = static_cast<int32_t>(std::round(scaledY));
// Clamp to within activeArray/preCorrectionActiveArray
if (clamp) {
- int32_t right = mArrayWidth - 1;
- int32_t bottom = mArrayHeight - 1;
+ int32_t right = arrayWidth - 1;
+ int32_t bottom = arrayHeight - 1;
coordPairs[i] =
std::min(right, std::max(0, coordPairs[i]));
coordPairs[i+1] =
@@ -367,7 +485,7 @@
}
void ZoomRatioMapper::scaleRects(int32_t* rects, int rectCount,
- float scaleRatio) {
+ float scaleRatio, int32_t arrayWidth, int32_t arrayHeight) {
for (int i = 0; i < rectCount * 4; i += 4) {
// Map from (l, t, width, height) to (l, t, l+width-1, t+height-1),
// where both top-left and bottom-right are inclusive.
@@ -379,9 +497,9 @@
};
// top-left
- scaleCoordinates(coords, 1, scaleRatio, true /*clamp*/);
+ scaleCoordinates(coords, 1, scaleRatio, true /*clamp*/, arrayWidth, arrayHeight);
// bottom-right
- scaleCoordinates(coords+2, 1, scaleRatio, true /*clamp*/);
+ scaleCoordinates(coords+2, 1, scaleRatio, true /*clamp*/, arrayWidth, arrayHeight);
// Map back to (l, t, width, height)
rects[i] = coords[0];
diff --git a/services/camera/libcameraservice/device3/ZoomRatioMapper.h b/services/camera/libcameraservice/device3/ZoomRatioMapper.h
index 698f87f..b7a9e41 100644
--- a/services/camera/libcameraservice/device3/ZoomRatioMapper.h
+++ b/services/camera/libcameraservice/device3/ZoomRatioMapper.h
@@ -33,7 +33,7 @@
* - HAL supports zoomRatio and the application uses cropRegion, or
* - HAL doesn't support zoomRatio, but the application uses zoomRatio
*/
-class ZoomRatioMapper : private CoordinateMapper {
+class ZoomRatioMapper : public CoordinateMapper {
public:
ZoomRatioMapper() = default;
ZoomRatioMapper(const CameraMetadata *deviceInfo,
@@ -41,7 +41,9 @@
ZoomRatioMapper(const ZoomRatioMapper& other) :
mHalSupportsZoomRatio(other.mHalSupportsZoomRatio),
mArrayWidth(other.mArrayWidth), mArrayHeight(other.mArrayHeight),
- mIsValid(other.mIsValid) {}
+ mIsValid(other.mIsValid) { initRemappedKeys(); }
+
+ void initRemappedKeys() override;
/**
* Initialize request template with valid zoomRatio if necessary.
@@ -66,22 +68,31 @@
public: // Visible for testing. Do not use concurently.
void scaleCoordinates(int32_t* coordPairs, int coordCount,
- float scaleRatio, bool clamp);
+ float scaleRatio, bool clamp, int32_t arrayWidth, int32_t arrayHeight);
bool isValid() { return mIsValid; }
private:
// const after construction
bool mHalSupportsZoomRatio;
- // active array / pre-correction array dimension
+
+ // active array / pre-correction array dimension for default and maximum
+ // resolution modes.
int32_t mArrayWidth, mArrayHeight;
+ int32_t mArrayWidthMaximumResolution, mArrayHeightMaximumResolution;
bool mIsValid = false;
- float deriveZoomRatio(const CameraMetadata* metadata);
- void scaleRects(int32_t* rects, int rectCount, float scaleRatio);
+ status_t deriveZoomRatio(const CameraMetadata* metadata, float *zoomRatio, int arrayWidth,
+ int arrayHeight);
+ void scaleRects(int32_t* rects, int rectCount, float scaleRatio, int32_t arrayWidth,
+ int32_t arrayHeight);
- status_t separateZoomFromCropLocked(CameraMetadata* metadata, bool isResult);
- status_t combineZoomAndCropLocked(CameraMetadata* metadata, bool isResult);
+ status_t separateZoomFromCropLocked(CameraMetadata* metadata, bool isResult, int arrayWidth,
+ int arrayHeight);
+ status_t combineZoomAndCropLocked(CameraMetadata* metadata, bool isResult, int arrayWidth,
+ int arrayHeight);
+ status_t getArrayDimensionsToBeUsed(const CameraMetadata *settings, int32_t *arrayWidth,
+ int32_t *arrayHeight);
};
} // namespace camera3
diff --git a/services/camera/libcameraservice/fuzzer/DistortionMapperFuzzer.cpp b/services/camera/libcameraservice/fuzzer/DistortionMapperFuzzer.cpp
index 96bab4e..88ec85c 100644
--- a/services/camera/libcameraservice/fuzzer/DistortionMapperFuzzer.cpp
+++ b/services/camera/libcameraservice/fuzzer/DistortionMapperFuzzer.cpp
@@ -23,6 +23,7 @@
using namespace android;
using namespace android::camera3;
+using DistortionMapperInfo = android::camera3::DistortionMapper::DistortionMapperInfo;
int32_t testActiveArray[] = {100, 100, 1000, 750};
float testICal[] = { 1000.f, 1000.f, 500.f, 500.f, 0.f };
@@ -62,10 +63,10 @@
for (int index = 0; fdp.remaining_bytes() > 0; index++) {
input.push_back(fdp.ConsumeIntegral<int32_t>());
}
-
+ DistortionMapperInfo *mapperInfo = m.getMapperInfo();
// The size argument counts how many coordinate pairs there are, so
// it is expected to be 1/2 the size of the input.
- m.mapCorrectedToRaw(input.data(), input.size()/2, clamp, simple);
+ m.mapCorrectedToRaw(input.data(), input.size()/2, mapperInfo, clamp, simple);
return 0;
}
diff --git a/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.cpp b/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.cpp
index bf89ca5..2509e6c 100644
--- a/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.cpp
+++ b/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.cpp
@@ -28,7 +28,7 @@
namespace frameworks {
namespace cameraservice {
namespace device {
-namespace V2_0 {
+namespace V2_1 {
namespace implementation {
using hardware::cameraservice::utils::conversion::convertToHidl;
@@ -115,7 +115,7 @@
// is guaranteed to be called serially by the client if it decides to
// use fmq.
if (e.settings.getDiscriminator() ==
- FmqSizeOrMetadata::hidl_discriminator::fmqMetadataSize) {
+ V2_0::FmqSizeOrMetadata::hidl_discriminator::fmqMetadataSize) {
/**
* Get settings from the fmq.
*/
@@ -196,6 +196,12 @@
Return<HStatus> HidlCameraDeviceUser::endConfigure(StreamConfigurationMode operatingMode,
const hidl_vec<uint8_t>& sessionParams) {
+ return endConfigure_2_1(operatingMode, sessionParams, systemTime());
+}
+
+Return<HStatus> HidlCameraDeviceUser::endConfigure_2_1(StreamConfigurationMode operatingMode,
+ const hidl_vec<uint8_t>& sessionParams,
+ nsecs_t startTimeNs) {
android::CameraMetadata cameraMetadata;
if (!convertFromHidl(sessionParams, &cameraMetadata)) {
return HStatus::ILLEGAL_ARGUMENT;
@@ -203,7 +209,8 @@
std::vector<int> offlineStreamIds;
binder::Status ret = mDeviceRemote->endConfigure(convertFromHidl(operatingMode),
- cameraMetadata, &offlineStreamIds);
+ cameraMetadata, ns2ms(startTimeNs),
+ &offlineStreamIds);
return B2HStatus(ret);
}
diff --git a/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.h b/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.h
index c3a80fe..0e2ab3d 100644
--- a/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.h
+++ b/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.h
@@ -24,6 +24,7 @@
#include <android/frameworks/cameraservice/common/2.0/types.h>
#include <android/frameworks/cameraservice/service/2.0/types.h>
#include <android/frameworks/cameraservice/device/2.0/ICameraDeviceUser.h>
+#include <android/frameworks/cameraservice/device/2.1/ICameraDeviceUser.h>
#include <android/frameworks/cameraservice/device/2.0/types.h>
#include <android/hardware/camera2/ICameraDeviceCallbacks.h>
#include <fmq/MessageQueue.h>
@@ -36,7 +37,7 @@
namespace frameworks {
namespace cameraservice {
namespace device {
-namespace V2_0 {
+namespace V2_1 {
namespace implementation {
using frameworks::cameraservice::device::V2_0::StreamConfigurationMode;
@@ -50,7 +51,7 @@
using CaptureRequestMetadataQueue = MessageQueue<uint8_t, kSynchronizedReadWrite>;
using TemplateId = frameworks::cameraservice::device::V2_0::TemplateId;
-using HCameraDeviceUser = device::V2_0::ICameraDeviceUser;
+using HCameraDeviceUser = device::V2_1::ICameraDeviceUser;
using HCameraMetadata = cameraservice::service::V2_0::CameraMetadata;
using HCaptureRequest = device::V2_0::CaptureRequest;
using HSessionConfiguration = frameworks::cameraservice::device::V2_0::SessionConfiguration;
@@ -83,6 +84,10 @@
virtual Return<HStatus> endConfigure(StreamConfigurationMode operatingMode,
const hidl_vec<uint8_t>& sessionParams);
+ virtual Return<HStatus> endConfigure_2_1(StreamConfigurationMode operatingMode,
+ const hidl_vec<uint8_t>& sessionParams,
+ nsecs_t startTimeNs);
+
virtual Return<HStatus> deleteStream(int32_t streamId) override;
virtual Return<void> createStream(const HOutputConfiguration& outputConfiguration,
diff --git a/services/camera/libcameraservice/hidl/HidlCameraService.cpp b/services/camera/libcameraservice/hidl/HidlCameraService.cpp
index 9ea9526..aa1e95a 100644
--- a/services/camera/libcameraservice/hidl/HidlCameraService.cpp
+++ b/services/camera/libcameraservice/hidl/HidlCameraService.cpp
@@ -38,7 +38,7 @@
using hardware::Void;
using device::V2_0::implementation::H2BCameraDeviceCallbacks;
-using device::V2_0::implementation::HidlCameraDeviceUser;
+using device::V2_1::implementation::HidlCameraDeviceUser;
using service::V2_0::implementation::H2BCameraServiceListener;
using HCameraMetadataType = frameworks::cameraservice::common::V2_0::CameraMetadataType;
using HVendorTag = frameworks::cameraservice::common::V2_0::VendorTag;
diff --git a/services/camera/libcameraservice/hidl/HidlCameraService.h b/services/camera/libcameraservice/hidl/HidlCameraService.h
index 097f4c5..86a7cec 100644
--- a/services/camera/libcameraservice/hidl/HidlCameraService.h
+++ b/services/camera/libcameraservice/hidl/HidlCameraService.h
@@ -21,7 +21,7 @@
#include <thread>
#include <android/frameworks/cameraservice/common/2.0/types.h>
-#include <android/frameworks/cameraservice/service/2.1/ICameraService.h>
+#include <android/frameworks/cameraservice/service/2.2/ICameraService.h>
#include <android/frameworks/cameraservice/service/2.0/types.h>
#include <android/frameworks/cameraservice/device/2.0/types.h>
@@ -42,7 +42,7 @@
using HCameraDeviceCallback = frameworks::cameraservice::device::V2_0::ICameraDeviceCallback;
using HCameraMetadata = frameworks::cameraservice::service::V2_0::CameraMetadata;
-using HCameraService = frameworks::cameraservice::service::V2_1::ICameraService;
+using HCameraService = frameworks::cameraservice::service::V2_2::ICameraService;
using HCameraServiceListener = frameworks::cameraservice::service::V2_0::ICameraServiceListener;
using HCameraServiceListener2_1 = frameworks::cameraservice::service::V2_1::ICameraServiceListener;
using HStatus = frameworks::cameraservice::common::V2_0::Status;
diff --git a/services/camera/libcameraservice/libcameraservice_fuzzer/Android.bp b/services/camera/libcameraservice/libcameraservice_fuzzer/Android.bp
index d06b2c5..3d74f0b 100644
--- a/services/camera/libcameraservice/libcameraservice_fuzzer/Android.bp
+++ b/services/camera/libcameraservice/libcameraservice_fuzzer/Android.bp
@@ -44,18 +44,21 @@
"libcutils",
"libcameraservice",
"libcamera_client",
+ "liblog",
"libui",
"libgui",
"android.hardware.camera.common@1.0",
"android.hardware.camera.provider@2.4",
"android.hardware.camera.provider@2.5",
"android.hardware.camera.provider@2.6",
+ "android.hardware.camera.provider@2.7",
"android.hardware.camera.device@1.0",
"android.hardware.camera.device@3.2",
"android.hardware.camera.device@3.3",
"android.hardware.camera.device@3.4",
"android.hardware.camera.device@3.5",
"android.hardware.camera.device@3.6",
+ "android.hardware.camera.device@3.7",
],
fuzz_config: {
cc: [
diff --git a/services/camera/libcameraservice/libcameraservice_fuzzer/camera_service_fuzzer.cpp b/services/camera/libcameraservice/libcameraservice_fuzzer/camera_service_fuzzer.cpp
index 54550a5..985b2f8 100644
--- a/services/camera/libcameraservice/libcameraservice_fuzzer/camera_service_fuzzer.cpp
+++ b/services/camera/libcameraservice/libcameraservice_fuzzer/camera_service_fuzzer.cpp
@@ -18,8 +18,18 @@
* Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
*/
+#define LOG_TAG "CameraServiceFuzzer"
+//#define LOG_NDEBUG 0
+
#include <CameraService.h>
+#include <device3/Camera3StreamInterface.h>
+#include <android/hardware/BnCameraServiceListener.h>
+#include <android/hardware/camera2/BnCameraDeviceCallbacks.h>
#include <android/hardware/ICameraServiceListener.h>
+#include <android/hardware/camera2/ICameraDeviceUser.h>
+#include <camera/camera2/OutputConfiguration.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <private/android_filesystem_config.h>
@@ -30,6 +40,7 @@
using namespace std;
const int32_t kPreviewThreshold = 8;
+const int32_t kNumRequestsTested = 8;
const nsecs_t kPreviewTimeout = 5000000000; // .5 [s.]
const nsecs_t kEventTimeout = 10000000000; // 1 [s.]
const size_t kMaxNumLines = USHRT_MAX;
@@ -39,6 +50,23 @@
hardware::ICameraService::CAMERA_TYPE_ALL};
const int kCameraApiVersion[] = {android::CameraService::API_VERSION_1,
android::CameraService::API_VERSION_2};
+const uint8_t kSensorPixelModes[] = {ANDROID_SENSOR_PIXEL_MODE_DEFAULT,
+ ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION};
+const int32_t kRequestTemplates[] = {
+ hardware::camera2::ICameraDeviceUser::TEMPLATE_PREVIEW,
+ hardware::camera2::ICameraDeviceUser::TEMPLATE_STILL_CAPTURE,
+ hardware::camera2::ICameraDeviceUser::TEMPLATE_RECORD,
+ hardware::camera2::ICameraDeviceUser::TEMPLATE_VIDEO_SNAPSHOT,
+ hardware::camera2::ICameraDeviceUser::TEMPLATE_MANUAL,
+ hardware::camera2::ICameraDeviceUser::TEMPLATE_ZERO_SHUTTER_LAG
+};
+
+const int32_t kRotations[] = {
+ camera3::CAMERA_STREAM_ROTATION_0,
+ camera3::CAMERA_STREAM_ROTATION_90,
+ camera3::CAMERA_STREAM_ROTATION_270
+};
+
const int kLayerMetadata[] = {
0x00100000 /*GRALLOC_USAGE_RENDERSCRIPT*/, 0x00000003 /*GRALLOC_USAGE_SW_READ_OFTEN*/,
0x00000100 /*GRALLOC_USAGE_HW_TEXTURE*/, 0x00000800 /*GRALLOC_USAGE_HW_COMPOSER*/,
@@ -69,15 +97,15 @@
class CameraFuzzer : public ::android::hardware::BnCameraClient {
public:
- CameraFuzzer() = default;
+ CameraFuzzer(sp<CameraService> cs, std::shared_ptr<FuzzedDataProvider> fp) :
+ mCameraService(cs), mFuzzedDataProvider(fp) {};
~CameraFuzzer() { deInit(); }
- bool init();
- void process(const uint8_t *data, size_t size);
+ void process();
void deInit();
private:
- FuzzedDataProvider *mFuzzedDataProvider = nullptr;
sp<CameraService> mCameraService = nullptr;
+ std::shared_ptr<FuzzedDataProvider> mFuzzedDataProvider = nullptr;
sp<SurfaceComposerClient> mComposerClient = nullptr;
int32_t mNumCameras = 0;
size_t mPreviewBufferCount = 0;
@@ -167,19 +195,7 @@
return rc;
}
-bool CameraFuzzer::init() {
- setuid(AID_MEDIA);
- mCameraService = new CameraService();
- if (mCameraService) {
- return true;
- }
- return false;
-}
-
void CameraFuzzer::deInit() {
- if (mCameraService) {
- mCameraService = nullptr;
- }
if (mComposerClient) {
mComposerClient->dispose();
}
@@ -298,12 +314,12 @@
for (int32_t cameraId = 0; cameraId < mNumCameras; ++cameraId) {
getCameraInformation(cameraId);
- const String16 opPackageName("com.fuzzer.poc");
::android::binder::Status rc;
sp<ICamera> cameraDevice;
- rc = mCameraService->connect(this, cameraId, opPackageName, AID_MEDIA, AID_ROOT,
- &cameraDevice);
+ rc = mCameraService->connect(this, cameraId, String16(),
+ android::CameraService::USE_CALLING_UID, android::CameraService::USE_CALLING_PID,
+ &cameraDevice);
if (!rc.isOk()) {
// camera not connected
return;
@@ -405,8 +421,7 @@
}
}
-void CameraFuzzer::process(const uint8_t *data, size_t size) {
- mFuzzedDataProvider = new FuzzedDataProvider(data, size);
+void CameraFuzzer::process() {
getNumCameras();
invokeCameraSound();
if (mNumCameras > 0) {
@@ -415,19 +430,169 @@
invokeDump();
invokeShellCommand();
invokeNotifyCalls();
- delete mFuzzedDataProvider;
+}
+
+class TestCameraServiceListener : public hardware::BnCameraServiceListener {
+public:
+ virtual ~TestCameraServiceListener() {};
+
+ virtual binder::Status onStatusChanged(int32_t , const String16&) {
+ return binder::Status::ok();
+ };
+
+ virtual binder::Status onPhysicalCameraStatusChanged(int32_t /*status*/,
+ const String16& /*cameraId*/, const String16& /*physicalCameraId*/) {
+ // No op
+ return binder::Status::ok();
+ };
+
+ virtual binder::Status onTorchStatusChanged(int32_t /*status*/, const String16& /*cameraId*/) {
+ return binder::Status::ok();
+ };
+
+ virtual binder::Status onCameraAccessPrioritiesChanged() {
+ // No op
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onCameraOpened(const String16& /*cameraId*/,
+ const String16& /*clientPackageName*/) {
+ // No op
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onCameraClosed(const String16& /*cameraId*/) {
+ // No op
+ return binder::Status::ok();
+ }
+};
+
+class TestCameraDeviceCallbacks : public hardware::camera2::BnCameraDeviceCallbacks {
+public:
+ TestCameraDeviceCallbacks() {}
+
+ virtual ~TestCameraDeviceCallbacks() {}
+
+ virtual binder::Status onDeviceError(int /*errorCode*/,
+ const CaptureResultExtras& /*resultExtras*/) {
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onDeviceIdle() {
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onCaptureStarted(const CaptureResultExtras& /*resultExtras*/,
+ int64_t /*timestamp*/) {
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onResultReceived(const CameraMetadata& /*metadata*/,
+ const CaptureResultExtras& /*resultExtras*/,
+ const std::vector<PhysicalCaptureResultInfo>& /*physicalResultInfos*/) {
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onPrepared(int /*streamId*/) {
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onRepeatingRequestError(
+ int64_t /*lastFrameNumber*/, int32_t /*stoppedSequenceId*/) {
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onRequestQueueEmpty() {
+ return binder::Status::ok();
+ }
+};
+
+class Camera2Fuzzer {
+ public:
+ Camera2Fuzzer(sp<CameraService> cs, std::shared_ptr<FuzzedDataProvider> fp) :
+ mCameraService(cs), mFuzzedDataProvider(fp) { };
+ ~Camera2Fuzzer() {}
+ void process();
+ private:
+ sp<CameraService> mCameraService = nullptr;
+ std::shared_ptr<FuzzedDataProvider> mFuzzedDataProvider = nullptr;
+};
+
+void Camera2Fuzzer::process() {
+ sp<TestCameraServiceListener> listener = new TestCameraServiceListener();
+ std::vector<hardware::CameraStatus> statuses;
+ mCameraService->addListenerTest(listener, &statuses);
+ for (auto s : statuses) {
+ sp<TestCameraDeviceCallbacks> callbacks(new TestCameraDeviceCallbacks());
+ sp<hardware::camera2::ICameraDeviceUser> device;
+ mCameraService->connectDevice(callbacks, String16(s.cameraId), String16(), {},
+ android::CameraService::USE_CALLING_UID, &device);
+ if (device == nullptr) {
+ continue;
+ }
+ device->beginConfigure();
+ sp<IGraphicBufferProducer> gbProducer;
+ sp<IGraphicBufferConsumer> gbConsumer;
+ BufferQueue::createBufferQueue(&gbProducer, &gbConsumer);
+ sp<BufferItemConsumer> opaqueConsumer = new BufferItemConsumer(gbConsumer,
+ GRALLOC_USAGE_SW_READ_NEVER, /*maxImages*/8, /*controlledByApp*/true);
+ opaqueConsumer->setName(String8("Roger"));
+
+ // Set to VGA dimension for default, as that is guaranteed to be present
+ gbConsumer->setDefaultBufferSize(640, 480);
+ gbConsumer->setDefaultBufferFormat(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
+
+ sp<Surface> surface(new Surface(gbProducer, /*controlledByApp*/false));
+
+ String16 noPhysicalId;
+ size_t rotations = sizeof(kRotations) / sizeof(int32_t) - 1;
+ OutputConfiguration output(gbProducer,
+ kRotations[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(0, rotations)],
+ noPhysicalId);
+ int streamId;
+ device->createStream(output, &streamId);
+ CameraMetadata sessionParams;
+ std::vector<int> offlineStreamIds;
+ device->endConfigure(/*isConstrainedHighSpeed*/ mFuzzedDataProvider->ConsumeBool(),
+ sessionParams, ns2ms(systemTime()), &offlineStreamIds);
+
+ CameraMetadata requestTemplate;
+ size_t requestTemplatesSize = sizeof(kRequestTemplates) /sizeof(int32_t) - 1;
+ device->createDefaultRequest(kRequestTemplates[
+ mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(0, requestTemplatesSize)],
+ /*out*/&requestTemplate);
+ hardware::camera2::CaptureRequest request;
+ request.mSurfaceList.add(surface);
+ request.mIsReprocess = false;
+ hardware::camera2::utils::SubmitInfo info;
+ for (int i = 0; i < kNumRequestsTested; i++) {
+ uint8_t sensorPixelMode =
+ kSensorPixelModes[mFuzzedDataProvider->ConsumeBool() ? 1 : 0];
+ requestTemplate.update(ANDROID_SENSOR_PIXEL_MODE, &sensorPixelMode, 1);
+ request.mPhysicalCameraSettings.clear();
+ request.mPhysicalCameraSettings.push_back({s.cameraId.string(), requestTemplate});
+ device->submitRequest(request, /*streaming*/false, /*out*/&info);
+ ALOGV("%s : camera id %s submit request id %d",__FUNCTION__, s.cameraId.string(),
+ info.mRequestId);
+ }
+ device->disconnect();
+ }
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (size < 1) {
return 0;
}
- sp<CameraFuzzer> camerafuzzer = new CameraFuzzer();
+ setuid(AID_CAMERASERVER);
+ std::shared_ptr<FuzzedDataProvider> fp = std::make_shared<FuzzedDataProvider>(data, size);
+ sp<CameraService> cs = new CameraService();
+ cs->clearCachedVariables();
+ sp<CameraFuzzer> camerafuzzer = new CameraFuzzer(cs, fp);
if (!camerafuzzer) {
return 0;
}
- if (camerafuzzer->init()) {
- camerafuzzer->process(data, size);
- }
+ camerafuzzer->process();
+ Camera2Fuzzer camera2fuzzer(cs, fp);
+ camera2fuzzer.process();
return 0;
}
diff --git a/services/camera/libcameraservice/tests/Android.mk b/services/camera/libcameraservice/tests/Android.mk
index b530342..0b5ad79 100644
--- a/services/camera/libcameraservice/tests/Android.mk
+++ b/services/camera/libcameraservice/tests/Android.mk
@@ -33,9 +33,11 @@
android.hardware.camera.provider@2.4 \
android.hardware.camera.provider@2.5 \
android.hardware.camera.provider@2.6 \
+ android.hardware.camera.provider@2.7 \
android.hardware.camera.device@1.0 \
android.hardware.camera.device@3.2 \
android.hardware.camera.device@3.4 \
+ android.hardware.camera.device@3.7 \
android.hidl.token@1.0-utils
LOCAL_STATIC_LIBRARIES := \
diff --git a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
index 855b5ab..a74fd9d 100644
--- a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
+++ b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
@@ -23,7 +23,9 @@
#include <android/hardware/camera/device/3.2/ICameraDeviceCallback.h>
#include <android/hardware/camera/device/3.2/ICameraDeviceSession.h>
#include <camera_metadata_hidden.h>
+#include <hidl/HidlBinderSupport.h>
#include <gtest/gtest.h>
+#include <utility>
using namespace android;
using namespace android::hardware::camera;
@@ -173,6 +175,25 @@
return hardware::Void();
}
+ virtual ::android::hardware::Return<bool> linkToDeath(
+ const ::android::sp<::android::hardware::hidl_death_recipient>& recipient,
+ uint64_t cookie) {
+ if (mInitialDeathRecipient.get() == nullptr) {
+ mInitialDeathRecipient =
+ std::make_unique<::android::hardware::hidl_binder_death_recipient>(recipient,
+ cookie, this);
+ }
+ return true;
+ }
+
+ void signalInitialBinderDeathRecipient() {
+ if (mInitialDeathRecipient.get() != nullptr) {
+ mInitialDeathRecipient->binderDied(nullptr /*who*/);
+ }
+ }
+
+ std::unique_ptr<::android::hardware::hidl_binder_death_recipient> mInitialDeathRecipient;
+
enum MethodNames {
SET_CALLBACK,
GET_VENDOR_TAGS,
@@ -567,3 +588,47 @@
ASSERT_EQ(serviceProxy.mLastRequestedServiceNames.back(), testProviderInstanceName) <<
"Incorrect instance requested from service manager";
}
+
+// Test that CameraProviderManager can handle races between provider death notifications and
+// provider registration callbacks
+TEST(CameraProviderManagerTest, BinderDeathRegistrationRaceTest) {
+
+ std::vector<hardware::hidl_string> deviceNames;
+ deviceNames.push_back("device@3.2/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);
+
+ hardware::hidl_string testProviderFqInterfaceName =
+ "android.hardware.camera.provider@2.4::ICameraProvider";
+ hardware::hidl_string testProviderInstanceName = "test/0";
+ serviceProxy.mManagerNotificationInterface->onRegistration(
+ testProviderFqInterfaceName,
+ testProviderInstanceName, false);
+
+ // Simulate artificial delay of the registration callback which arrives before the
+ // death notification
+ serviceProxy.mManagerNotificationInterface->onRegistration(
+ testProviderFqInterfaceName,
+ testProviderInstanceName, false);
+
+ provider->signalInitialBinderDeathRecipient();
+
+ auto deviceCount = static_cast<unsigned> (providerManager->getCameraCount().second);
+ ASSERT_EQ(deviceCount, deviceNames.size()) <<
+ "Unexpected amount of camera devices";
+}
diff --git a/services/camera/libcameraservice/tests/ClientManagerTest.cpp b/services/camera/libcameraservice/tests/ClientManagerTest.cpp
index 6a38427..037c5c2 100644
--- a/services/camera/libcameraservice/tests/ClientManagerTest.cpp
+++ b/services/camera/libcameraservice/tests/ClientManagerTest.cpp
@@ -17,10 +17,13 @@
#define LOG_NDEBUG 0
#define LOG_TAG "ClientManagerTest"
+#include <binder/ActivityManager.h>
+
#include "../utils/ClientManager.h"
#include <gtest/gtest.h>
using namespace android::resource_policy;
+using namespace android;
struct TestClient {
TestClient(int id, int32_t cost, const std::set<int>& conflictingKeys, int32_t ownerId,
@@ -59,13 +62,15 @@
TestClientManager cm;
TestClient cam0Client(/*ID*/0, /*cost*/100, /*conflicts*/{1},
- /*ownerId*/ 1000, /*score*/50, /*state*/ 1, /*isVendorClient*/ false);
+ /*ownerId*/ 1000, PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ,
+ ActivityManager::PROCESS_STATE_PERSISTENT_UI, /*isVendorClient*/ false);
auto cam0Desc = makeDescFromTestClient(cam0Client);
auto evicted = cm.addAndEvict(cam0Desc);
ASSERT_EQ(evicted.size(), 0u) << "Evicted list must be empty";
TestClient cam1Client(/*ID*/1, /*cost*/100, /*conflicts*/{0},
- /*ownerId*/ 1000, /*score*/50, /*state*/ 1, /*isVendorClient*/ false);
+ /*ownerId*/ 1000, PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ,
+ ActivityManager::PROCESS_STATE_PERSISTENT_UI, /*isVendorClient*/ false);
auto cam1Desc = makeDescFromTestClient(cam1Client);
// 1. Check with conflicting devices, new client would be evicted
@@ -76,13 +81,15 @@
cm.removeAll();
TestClient cam2Client(/*ID*/2, /*cost*/100, /*conflicts*/{},
- /*ownerId*/ 1000, /*score*/50, /*state*/ 1, /*isVendorClient*/ false);
+ /*ownerId*/ 1000, PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ,
+ ActivityManager::PROCESS_STATE_PERSISTENT_UI, /*isVendorClient*/ false);
auto cam2Desc = makeDescFromTestClient(cam2Client);
evicted = cm.addAndEvict(cam2Desc);
ASSERT_EQ(evicted.size(), 0u) << "Evicted list must be empty";
TestClient cam3Client(/*ID*/3, /*cost*/100, /*conflicts*/{},
- /*ownerId*/ 1000, /*score*/50, /*state*/ 1, /*isVendorClient*/ false);
+ /*ownerId*/ 1000, PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ,
+ ActivityManager::PROCESS_STATE_PERSISTENT_UI, /*isVendorClient*/ false);
auto cam3Desc = makeDescFromTestClient(cam3Client);
// 2. Check without conflicting devices, the pre-existing client won't be evicted
@@ -97,12 +104,42 @@
ASSERT_EQ(evicted.size(), 0u) << "Evicted list must be empty";
TestClient cam0ClientNew(/*ID*/0, /*cost*/100, /*conflicts*/{1},
- /*ownerId*/ 1000, /*score*/50, /*state*/ 1, /*isVendorClient*/ false);
+ /*ownerId*/ 1000, PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ,
+ ActivityManager::PROCESS_STATE_PERSISTENT_UI, /*isVendorClient*/ false);
auto cam0DescNew = makeDescFromTestClient(cam0ClientNew);
wouldBeEvicted = cm.wouldEvict(cam0DescNew);
// 3. Check opening the same camera twice will evict the older client
ASSERT_EQ(wouldBeEvicted.size(), 1u) << "Evicted list length must be 1";
ASSERT_EQ(wouldBeEvicted[0], cam0Desc) << "cam0 (old) must be evicted";
-}
+ // 4. Check that an invalid client (dead process) will be evicted
+
+ cm.removeAll();
+
+ TestClient camDeadClient(/*ID*/ 0, /*cost*/100, /*conflicts*/{},
+ /*ownerId*/ 1000, INVALID_ADJ,
+ ActivityManager::PROCESS_STATE_NONEXISTENT, /*isVendorClient*/ false);
+ auto camDeadDesc = makeDescFromTestClient(camDeadClient);
+ evicted = cm.addAndEvict(camDeadDesc);
+ wouldBeEvicted = cm.wouldEvict(cam0Desc);
+
+ ASSERT_EQ(evicted.size(), 0u) << "Evicted list must be empty";
+ ASSERT_EQ(wouldBeEvicted.size(), 1u) << "Evicted list length must be 1";
+ ASSERT_EQ(wouldBeEvicted[0], camDeadDesc) << "dead cam must be evicted";
+
+ // 5. Check that a more important client will win
+
+ TestClient cam0ForegroundClient(/*ID*/0, /*cost*/100, /*conflicts*/{1},
+ /*ownerId*/ 1000, FOREGROUND_APP_ADJ,
+ ActivityManager::PROCESS_STATE_PERSISTENT_UI, /*isVendorClient*/ false);
+ auto cam0FgDesc = makeDescFromTestClient(cam0ForegroundClient);
+
+ cm.removeAll();
+ evicted = cm.addAndEvict(cam0Desc);
+ wouldBeEvicted = cm.wouldEvict(cam0FgDesc);
+
+ ASSERT_EQ(evicted.size(), 0u);
+ ASSERT_EQ(wouldBeEvicted.size(), 1u);
+ ASSERT_EQ(wouldBeEvicted[0],cam0Desc) << "less important cam0 must be evicted";
+}
diff --git a/services/camera/libcameraservice/tests/DistortionMapperTest.cpp b/services/camera/libcameraservice/tests/DistortionMapperTest.cpp
index 54935c9..8331136 100644
--- a/services/camera/libcameraservice/tests/DistortionMapperTest.cpp
+++ b/services/camera/libcameraservice/tests/DistortionMapperTest.cpp
@@ -27,7 +27,7 @@
using namespace android;
using namespace android::camera3;
-
+using DistortionMapperInfo = android::camera3::DistortionMapper::DistortionMapperInfo;
int32_t testActiveArray[] = {100, 100, 1000, 750};
int32_t testPreCorrActiveArray[] = {90, 90, 1020, 770};
@@ -132,14 +132,15 @@
/*preCorrectionActiveArray*/ testActiveArray);
auto coords = basicCoords;
- res = m.mapCorrectedToRaw(coords.data(), 5, /*clamp*/true);
+ DistortionMapperInfo *mapperInfo = m.getMapperInfo();
+ res = m.mapCorrectedToRaw(coords.data(), 5, mapperInfo, /*clamp*/true);
ASSERT_EQ(res, OK);
for (size_t i = 0; i < coords.size(); i++) {
EXPECT_EQ(coords[i], basicCoords[i]);
}
- res = m.mapRawToCorrected(coords.data(), 5, /*clamp*/true);
+ res = m.mapRawToCorrected(coords.data(), 5, mapperInfo, /*clamp*/true);
ASSERT_EQ(res, OK);
for (size_t i = 0; i < coords.size(); i++) {
@@ -152,14 +153,14 @@
};
auto rectsOrig = rects;
- res = m.mapCorrectedRectToRaw(rects.data(), 2, /*clamp*/true);
+ res = m.mapCorrectedRectToRaw(rects.data(), 2, mapperInfo, /*clamp*/true);
ASSERT_EQ(res, OK);
for (size_t i = 0; i < rects.size(); i++) {
EXPECT_EQ(rects[i], rectsOrig[i]);
}
- res = m.mapRawRectToCorrected(rects.data(), 2, /*clamp*/true);
+ res = m.mapRawRectToCorrected(rects.data(), 2, mapperInfo, /*clamp*/true);
ASSERT_EQ(res, OK);
for (size_t i = 0; i < rects.size(); i++) {
@@ -176,14 +177,17 @@
/*preCorrectionActiveArray*/ activeArray.data());
auto rectsOrig = activeArray;
- res = m.mapCorrectedRectToRaw(activeArray.data(), 1, /*clamp*/true, /*simple*/ true);
+ DistortionMapperInfo *mapperInfo = m.getMapperInfo();
+ res = m.mapCorrectedRectToRaw(activeArray.data(), 1, mapperInfo, /*clamp*/true,
+ /*simple*/ true);
ASSERT_EQ(res, OK);
for (size_t i = 0; i < activeArray.size(); i++) {
EXPECT_EQ(activeArray[i], rectsOrig[i]);
}
- res = m.mapRawRectToCorrected(activeArray.data(), 1, /*clamp*/true, /*simple*/ true);
+ res = m.mapRawRectToCorrected(activeArray.data(), 1, mapperInfo, /*clamp*/true,
+ /*simple*/ true);
ASSERT_EQ(res, OK);
for (size_t i = 0; i < activeArray.size(); i++) {
@@ -200,7 +204,8 @@
/*preCorrectionActiveArray*/ testPreCorrActiveArray);
auto coords = basicCoords;
- res = m.mapCorrectedToRaw(coords.data(), 5, /*clamp*/true, /*simple*/true);
+ DistortionMapperInfo *mapperInfo = m.getMapperInfo();
+ res = m.mapCorrectedToRaw(coords.data(), 5, mapperInfo, /*clamp*/true, /*simple*/true);
ASSERT_EQ(res, OK);
ASSERT_EQ(coords[0], 0); ASSERT_EQ(coords[1], 0);
@@ -237,12 +242,13 @@
auto origCoords = randCoords;
base::Timer correctedToRawTimer;
- res = m.mapCorrectedToRaw(randCoords.data(), randCoords.size() / 2, clamp, simple);
+ DistortionMapperInfo *mapperInfo = m.getMapperInfo();
+ res = m.mapCorrectedToRaw(randCoords.data(), randCoords.size() / 2, mapperInfo, clamp, simple);
auto correctedToRawDurationMs = correctedToRawTimer.duration();
EXPECT_EQ(res, OK);
base::Timer rawToCorrectedTimer;
- res = m.mapRawToCorrected(randCoords.data(), randCoords.size() / 2, clamp, simple);
+ res = m.mapRawToCorrected(randCoords.data(), randCoords.size() / 2, mapperInfo, clamp, simple);
auto rawToCorrectedDurationMs = rawToCorrectedTimer.duration();
EXPECT_EQ(res, OK);
@@ -363,7 +369,8 @@
using namespace openCvData;
- res = m.mapRawToCorrected(rawCoords.data(), rawCoords.size() / 2, /*clamp*/false,
+ DistortionMapperInfo *mapperInfo = m.getMapperInfo();
+ res = m.mapRawToCorrected(rawCoords.data(), rawCoords.size() / 2, mapperInfo, /*clamp*/false,
/*simple*/false);
for (size_t i = 0; i < rawCoords.size(); i+=2) {
diff --git a/services/camera/libcameraservice/tests/ZoomRatioTest.cpp b/services/camera/libcameraservice/tests/ZoomRatioTest.cpp
index 4e94991..ff7aafd 100644
--- a/services/camera/libcameraservice/tests/ZoomRatioTest.cpp
+++ b/services/camera/libcameraservice/tests/ZoomRatioTest.cpp
@@ -182,7 +182,7 @@
// Verify 1.0x zoom doesn't change the coordinates
auto coords = originalCoords;
- mapper.scaleCoordinates(coords.data(), coords.size()/2, 1.0f, false /*clamp*/);
+ mapper.scaleCoordinates(coords.data(), coords.size()/2, 1.0f, false /*clamp*/, width, height);
for (size_t i = 0; i < coords.size(); i++) {
EXPECT_EQ(coords[i], originalCoords[i]);
}
@@ -199,7 +199,7 @@
(width - 1) * 5.0f / 4.0f, (height - 1) / 2.0f, // middle-right after 1.33x zoom
};
coords = originalCoords;
- mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, false /*clamp*/);
+ mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, false /*clamp*/, width, height);
for (size_t i = 0; i < coords.size(); i++) {
EXPECT_LE(std::abs(coords[i] - expected2xCoords[i]), kMaxAllowedPixelError);
}
@@ -216,7 +216,7 @@
width - 1.0f, (height - 1) / 2.0f, // middle-right after 1.33x zoom
};
coords = originalCoords;
- mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, true /*clamp*/);
+ mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, true /*clamp*/, width, height);
for (size_t i = 0; i < coords.size(); i++) {
EXPECT_LE(std::abs(coords[i] - expected2xCoordsClampedInc[i]), kMaxAllowedPixelError);
}
@@ -233,7 +233,7 @@
width - 1.0f, height / 2.0f, // middle-right after 1.33x zoom
};
coords = originalCoords;
- mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, true /*clamp*/);
+ mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, true /*clamp*/, width, height);
for (size_t i = 0; i < coords.size(); i++) {
EXPECT_LE(std::abs(coords[i] - expected2xCoordsClampedExc[i]), kMaxAllowedPixelError);
}
@@ -250,7 +250,7 @@
(width - 1) * 5 / 8.0f, (height - 1) / 2.0f, // middle-right after 1.33x zoom-in
};
coords = originalCoords;
- mapper.scaleCoordinates(coords.data(), coords.size()/2, 1.0f/3, false /*clamp*/);
+ mapper.scaleCoordinates(coords.data(), coords.size()/2, 1.0f/3, false /*clamp*/, width, height);
for (size_t i = 0; i < coords.size(); i++) {
EXPECT_LE(std::abs(coords[i] - expectedZoomOutCoords[i]), kMaxAllowedPixelError);
}
diff --git a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
new file mode 100644
index 0000000..76927c0
--- /dev/null
+++ b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "CameraServiceProxyWrapper"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <inttypes.h>
+#include <utils/Log.h>
+#include <binder/IServiceManager.h>
+
+#include "CameraServiceProxyWrapper.h"
+
+namespace android {
+
+using hardware::ICameraServiceProxy;
+using hardware::CameraSessionStats;
+
+Mutex CameraServiceProxyWrapper::sProxyMutex;
+sp<hardware::ICameraServiceProxy> CameraServiceProxyWrapper::sCameraServiceProxy;
+
+Mutex CameraServiceProxyWrapper::mLock;
+std::map<String8, std::shared_ptr<CameraServiceProxyWrapper::CameraSessionStatsWrapper>>
+ CameraServiceProxyWrapper::mSessionStatsMap;
+
+/**
+ * CameraSessionStatsWrapper functions
+ */
+
+void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onOpen() {
+ Mutex::Autolock l(mLock);
+
+ updateProxyDeviceState(mSessionStats);
+}
+
+void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onClose(int32_t latencyMs) {
+ Mutex::Autolock l(mLock);
+
+ mSessionStats.mNewCameraState = CameraSessionStats::CAMERA_STATE_CLOSED;
+ mSessionStats.mLatencyMs = latencyMs;
+ updateProxyDeviceState(mSessionStats);
+}
+
+void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onStreamConfigured(
+ int operatingMode, bool internalReconfig, int32_t latencyMs) {
+ Mutex::Autolock l(mLock);
+
+ if (internalReconfig) {
+ mSessionStats.mInternalReconfigure++;
+ } else {
+ mSessionStats.mLatencyMs = latencyMs;
+ mSessionStats.mSessionType = operatingMode;
+ }
+}
+
+void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onActive() {
+ Mutex::Autolock l(mLock);
+
+ mSessionStats.mNewCameraState = CameraSessionStats::CAMERA_STATE_ACTIVE;
+ updateProxyDeviceState(mSessionStats);
+
+ // Reset mCreationDuration to -1 to distinguish between 1st session
+ // after configuration, and all other sessions after configuration.
+ mSessionStats.mLatencyMs = -1;
+}
+
+void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onIdle(
+ int64_t requestCount, int64_t resultErrorCount, bool deviceError,
+ const std::vector<hardware::CameraStreamStats>& streamStats) {
+ Mutex::Autolock l(mLock);
+
+ mSessionStats.mNewCameraState = CameraSessionStats::CAMERA_STATE_IDLE;
+ mSessionStats.mRequestCount = requestCount;
+ mSessionStats.mResultErrorCount = resultErrorCount;
+ mSessionStats.mDeviceError = deviceError;
+ mSessionStats.mStreamStats = streamStats;
+ updateProxyDeviceState(mSessionStats);
+
+ mSessionStats.mInternalReconfigure = 0;
+ mSessionStats.mStreamStats.clear();
+}
+
+/**
+ * CameraServiceProxyWrapper functions
+ */
+
+sp<ICameraServiceProxy> CameraServiceProxyWrapper::getCameraServiceProxy() {
+#ifndef __BRILLO__
+ Mutex::Autolock al(sProxyMutex);
+ if (sCameraServiceProxy == nullptr) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ // Use checkService because cameraserver normally starts before the
+ // system server and the proxy service. So the long timeout that getService
+ // has before giving up is inappropriate.
+ sp<IBinder> binder = sm->checkService(String16("media.camera.proxy"));
+ if (binder != nullptr) {
+ sCameraServiceProxy = interface_cast<ICameraServiceProxy>(binder);
+ }
+ }
+#endif
+ return sCameraServiceProxy;
+}
+
+void CameraServiceProxyWrapper::pingCameraServiceProxy() {
+ sp<ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
+ if (proxyBinder == nullptr) return;
+ proxyBinder->pingForUserUpdate();
+}
+
+bool CameraServiceProxyWrapper::isRotateAndCropOverrideNeeded(
+ String16 packageName, int sensorOrientation, int lensFacing) {
+ sp<ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
+ if (proxyBinder == nullptr) return true;
+ bool ret = true;
+ auto status = proxyBinder->isRotateAndCropOverrideNeeded(packageName, sensorOrientation,
+ lensFacing, &ret);
+ if (!status.isOk()) {
+ ALOGE("%s: Failed during top activity orientation query: %s", __FUNCTION__,
+ status.exceptionMessage().c_str());
+ }
+
+ return ret;
+}
+
+void CameraServiceProxyWrapper::updateProxyDeviceState(const CameraSessionStats& sessionStats) {
+ sp<ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
+ if (proxyBinder == nullptr) return;
+ proxyBinder->notifyCameraState(sessionStats);
+}
+
+void CameraServiceProxyWrapper::logStreamConfigured(const String8& id,
+ int operatingMode, bool internalConfig, int32_t latencyMs) {
+ std::shared_ptr<CameraSessionStatsWrapper> sessionStats;
+ {
+ Mutex::Autolock l(mLock);
+ sessionStats = mSessionStatsMap[id];
+ if (sessionStats == nullptr) {
+ ALOGE("%s: SessionStatsMap should contain camera %s",
+ __FUNCTION__, id.c_str());
+ return;
+ }
+ }
+
+ ALOGV("%s: id %s, operatingMode %d, internalConfig %d, latencyMs %d",
+ __FUNCTION__, id.c_str(), operatingMode, internalConfig, latencyMs);
+ sessionStats->onStreamConfigured(operatingMode, internalConfig, latencyMs);
+}
+
+void CameraServiceProxyWrapper::logActive(const String8& id) {
+ std::shared_ptr<CameraSessionStatsWrapper> sessionStats;
+ {
+ Mutex::Autolock l(mLock);
+ sessionStats = mSessionStatsMap[id];
+ if (sessionStats == nullptr) {
+ ALOGE("%s: SessionStatsMap should contain camera %s when logActive is called",
+ __FUNCTION__, id.c_str());
+ return;
+ }
+ }
+
+ ALOGV("%s: id %s", __FUNCTION__, id.c_str());
+ sessionStats->onActive();
+}
+
+void CameraServiceProxyWrapper::logIdle(const String8& id,
+ int64_t requestCount, int64_t resultErrorCount, bool deviceError,
+ const std::vector<hardware::CameraStreamStats>& streamStats) {
+ std::shared_ptr<CameraSessionStatsWrapper> sessionStats;
+ {
+ Mutex::Autolock l(mLock);
+ sessionStats = mSessionStatsMap[id];
+ }
+
+ if (sessionStats == nullptr) {
+ ALOGE("%s: SessionStatsMap should contain camera %s when logIdle is called",
+ __FUNCTION__, id.c_str());
+ return;
+ }
+
+ ALOGV("%s: id %s, requestCount %" PRId64 ", resultErrorCount %" PRId64 ", deviceError %d",
+ __FUNCTION__, id.c_str(), requestCount, resultErrorCount, deviceError);
+ for (size_t i = 0; i < streamStats.size(); i++) {
+ ALOGV("%s: streamStats[%zu]: w %d h %d, requestedCount %" PRId64 ", dropCount %"
+ PRId64 ", startTimeMs %d" ,
+ __FUNCTION__, i, streamStats[i].mWidth, streamStats[i].mHeight,
+ streamStats[i].mRequestCount, streamStats[i].mErrorCount,
+ streamStats[i].mStartLatencyMs);
+ }
+
+ sessionStats->onIdle(requestCount, resultErrorCount, deviceError, streamStats);
+}
+
+void CameraServiceProxyWrapper::logOpen(const String8& id, int facing,
+ const String16& clientPackageName, int effectiveApiLevel, bool isNdk,
+ int32_t latencyMs) {
+ std::shared_ptr<CameraSessionStatsWrapper> sessionStats;
+ {
+ Mutex::Autolock l(mLock);
+ if (mSessionStatsMap.count(id) > 0) {
+ ALOGE("%s: SessionStatsMap shouldn't contain camera %s",
+ __FUNCTION__, id.c_str());
+ return;
+ }
+
+ int apiLevel = CameraSessionStats::CAMERA_API_LEVEL_1;
+ if (effectiveApiLevel == 2) {
+ apiLevel = CameraSessionStats::CAMERA_API_LEVEL_2;
+ }
+
+ sessionStats = std::make_shared<CameraSessionStatsWrapper>(String16(id), facing,
+ CameraSessionStats::CAMERA_STATE_OPEN, clientPackageName,
+ apiLevel, isNdk, latencyMs);
+ mSessionStatsMap.emplace(id, sessionStats);
+ ALOGV("%s: Adding id %s", __FUNCTION__, id.c_str());
+ }
+
+ ALOGV("%s: id %s, facing %d, effectiveApiLevel %d, isNdk %d, latencyMs %d",
+ __FUNCTION__, id.c_str(), facing, effectiveApiLevel, isNdk, latencyMs);
+ sessionStats->onOpen();
+}
+
+void CameraServiceProxyWrapper::logClose(const String8& id, int32_t latencyMs) {
+ std::shared_ptr<CameraSessionStatsWrapper> sessionStats;
+ {
+ Mutex::Autolock l(mLock);
+ if (mSessionStatsMap.count(id) == 0) {
+ ALOGE("%s: SessionStatsMap should contain camera %s before it's closed",
+ __FUNCTION__, id.c_str());
+ return;
+ }
+
+ sessionStats = mSessionStatsMap[id];
+ if (sessionStats == nullptr) {
+ ALOGE("%s: SessionStatsMap should contain camera %s",
+ __FUNCTION__, id.c_str());
+ return;
+ }
+ mSessionStatsMap.erase(id);
+ ALOGV("%s: Erasing id %s", __FUNCTION__, id.c_str());
+ }
+
+ ALOGV("%s: id %s, latencyMs %d", __FUNCTION__, id.c_str(), latencyMs);
+ sessionStats->onClose(latencyMs);
+}
+
+}; // namespace android
diff --git a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
new file mode 100644
index 0000000..ad9db68
--- /dev/null
+++ b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA_SERVICE_PROXY_WRAPPER_H_
+#define ANDROID_SERVERS_CAMERA_SERVICE_PROXY_WRAPPER_H_
+
+#include <android/hardware/ICameraServiceProxy.h>
+
+#include <utils/Mutex.h>
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+
+#include <camera/CameraSessionStats.h>
+
+namespace android {
+
+class CameraServiceProxyWrapper {
+private:
+ // Guard mCameraServiceProxy
+ static Mutex sProxyMutex;
+ // Cached interface to the camera service proxy in system service
+ static sp<hardware::ICameraServiceProxy> sCameraServiceProxy;
+
+ struct CameraSessionStatsWrapper {
+ hardware::CameraSessionStats mSessionStats;
+ Mutex mLock; // lock for per camera session stats
+
+ CameraSessionStatsWrapper(const String16& cameraId, int facing, int newCameraState,
+ const String16& clientName, int apiLevel, bool isNdk, int32_t latencyMs) :
+ mSessionStats(cameraId, facing, newCameraState, clientName, apiLevel, isNdk, latencyMs)
+ {}
+
+ void onOpen();
+ void onClose(int32_t latencyMs);
+ void onStreamConfigured(int operatingMode, bool internalReconfig, int32_t latencyMs);
+ void onActive();
+ void onIdle(int64_t requestCount, int64_t resultErrorCount, bool deviceError,
+ const std::vector<hardware::CameraStreamStats>& streamStats);
+ };
+
+ // Lock for camera session stats map
+ static Mutex mLock;
+ // Map from camera id to the camera's session statistics
+ static std::map<String8, std::shared_ptr<CameraSessionStatsWrapper>> mSessionStatsMap;
+
+ /**
+ * Update the session stats of a given camera device (open/close/active/idle) with
+ * the camera proxy service in the system service
+ */
+ static void updateProxyDeviceState(
+ const hardware::CameraSessionStats& sessionStats);
+
+ static sp<hardware::ICameraServiceProxy> getCameraServiceProxy();
+
+public:
+ // Open
+ static void logOpen(const String8& id, int facing,
+ const String16& clientPackageName, int apiLevel, bool isNdk,
+ int32_t latencyMs);
+
+ // Close
+ static void logClose(const String8& id, int32_t latencyMs);
+
+ // Stream configuration
+ static void logStreamConfigured(const String8& id, int operatingMode, bool internalReconfig,
+ int32_t latencyMs);
+
+ // Session state becomes active
+ static void logActive(const String8& id);
+
+ // Session state becomes idle
+ static void logIdle(const String8& id,
+ int64_t requestCount, int64_t resultErrorCount, bool deviceError,
+ const std::vector<hardware::CameraStreamStats>& streamStats);
+
+ // Ping camera service proxy for user update
+ static void pingCameraServiceProxy();
+
+ // Check whether the current top activity needs a rotate and crop override.
+ static bool isRotateAndCropOverrideNeeded(String16 packageName, int sensorOrientation,
+ int lensFacing);
+};
+
+} // android
+
+#endif // ANDROID_SERVERS_CAMERA_SERVICE_PROXY_WRAPPER_H_
diff --git a/services/camera/libcameraservice/utils/ClientManager.h b/services/camera/libcameraservice/utils/ClientManager.h
index 64be6c5..09258ef 100644
--- a/services/camera/libcameraservice/utils/ClientManager.h
+++ b/services/camera/libcameraservice/utils/ClientManager.h
@@ -31,6 +31,31 @@
namespace android {
namespace resource_policy {
+// Values from frameworks/base/services/core/java/com/android/server/am/ProcessList.java
+const int32_t INVALID_ADJ = -10000;
+const int32_t UNKNOWN_ADJ = 1001;
+const int32_t CACHED_APP_MAX_ADJ = 999;
+const int32_t CACHED_APP_MIN_ADJ = 900;
+const int32_t CACHED_APP_LMK_FIRST_ADJ = 950;
+const int32_t CACHED_APP_IMPORTANCE_LEVELS = 5;
+const int32_t SERVICE_B_ADJ = 800;
+const int32_t PREVIOUS_APP_ADJ = 700;
+const int32_t HOME_APP_ADJ = 600;
+const int32_t SERVICE_ADJ = 500;
+const int32_t HEAVY_WEIGHT_APP_ADJ = 400;
+const int32_t BACKUP_APP_ADJ = 300;
+const int32_t PERCEPTIBLE_LOW_APP_ADJ = 250;
+const int32_t PERCEPTIBLE_MEDIUM_APP_ADJ = 225;
+const int32_t PERCEPTIBLE_APP_ADJ = 200;
+const int32_t VISIBLE_APP_ADJ = 100;
+const int32_t VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1;
+const int32_t PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ = 50;
+const int32_t FOREGROUND_APP_ADJ = 0;
+const int32_t PERSISTENT_SERVICE_ADJ = -700;
+const int32_t PERSISTENT_PROC_ADJ = -800;
+const int32_t SYSTEM_ADJ = -900;
+const int32_t NATIVE_ADJ = -1000;
+
class ClientPriority {
public:
/**
@@ -40,7 +65,9 @@
* hwbinder thread.
*/
ClientPriority(int32_t score, int32_t state, bool isVendorClient) :
- mScore(score), mState(state), mIsVendorClient(isVendorClient) { }
+ mScore((score == INVALID_ADJ) ? UNKNOWN_ADJ : score),
+ mState(state),
+ mIsVendorClient(isVendorClient) { }
int32_t getScore() const { return mScore; }
int32_t getState() const { return mState; }
@@ -50,7 +77,7 @@
// construction. Otherwise, it can get reset each time cameraserver
// queries ActivityManagerService for oom_adj scores / states .
if (!mIsVendorClient) {
- mScore = score;
+ mScore = (score == INVALID_ADJ) ? UNKNOWN_ADJ : score;
}
}
diff --git a/services/camera/libcameraservice/utils/ExifUtils.cpp b/services/camera/libcameraservice/utils/ExifUtils.cpp
index 8a0303a..485705c 100644
--- a/services/camera/libcameraservice/utils/ExifUtils.cpp
+++ b/services/camera/libcameraservice/utils/ExifUtils.cpp
@@ -916,11 +916,25 @@
ALOGV("%s: Cannot find focal length in metadata.", __FUNCTION__);
}
+ int32_t sensorPixelMode = ANDROID_SENSOR_PIXEL_MODE_DEFAULT;
+ camera_metadata_ro_entry sensorPixelModeEntry = metadata.find(ANDROID_SENSOR_PIXEL_MODE);
+ if (sensorPixelModeEntry.count != 0) {
+ sensorPixelMode = sensorPixelModeEntry.data.u8[0];
+ if (sensorPixelMode != ANDROID_SENSOR_PIXEL_MODE_DEFAULT ||
+ sensorPixelMode != ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) {
+ ALOGE("%s: Request sensor pixel mode is not one of the valid values %d",
+ __FUNCTION__, sensorPixelMode);
+ return false;
+ }
+ }
+ int32_t activeArrayTag = sensorPixelMode == ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION ?
+ ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION :
+ ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE;
if (metadata.exists(ANDROID_SCALER_CROP_REGION) &&
- staticInfo.exists(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE)) {
+ staticInfo.exists(activeArrayTag)) {
entry = metadata.find(ANDROID_SCALER_CROP_REGION);
camera_metadata_ro_entry activeArrayEntry =
- staticInfo.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+ staticInfo.find(activeArrayTag);
if (!setDigitalZoomRatio(entry.data.i32[2], entry.data.i32[3],
activeArrayEntry.data.i32[2], activeArrayEntry.data.i32[3])) {
diff --git a/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp b/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
index 888671c..6dcf440 100644
--- a/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
+++ b/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
@@ -14,20 +14,752 @@
* limitations under the License.
*/
#include "SessionConfigurationUtils.h"
-#include "../api2/CameraDeviceClient.h"
+#include "../api2/DepthCompositeStream.h"
+#include "../api2/HeicCompositeStream.h"
+#include "common/CameraDeviceBase.h"
+#include "../CameraService.h"
+#include "device3/Camera3Device.h"
+#include "device3/Camera3OutputStream.h"
+
+using android::camera3::OutputStreamInfo;
+using android::camera3::OutputStreamInfo;
+using android::hardware::camera2::ICameraDeviceUser;
+using android::hardware::camera::metadata::V3_6::CameraMetadataEnumAndroidSensorPixelMode;
namespace android {
+namespace camera3 {
+
+void StreamConfiguration::getStreamConfigurations(
+ const CameraMetadata &staticInfo, int configuration,
+ std::unordered_map<int, std::vector<StreamConfiguration>> *scm) {
+ if (scm == nullptr) {
+ ALOGE("%s: StreamConfigurationMap nullptr", __FUNCTION__);
+ return;
+ }
+ const int STREAM_FORMAT_OFFSET = 0;
+ const int STREAM_WIDTH_OFFSET = 1;
+ const int STREAM_HEIGHT_OFFSET = 2;
+ const int STREAM_IS_INPUT_OFFSET = 3;
+
+ camera_metadata_ro_entry availableStreamConfigs = staticInfo.find(configuration);
+ for (size_t i = 0; i < availableStreamConfigs.count; i += 4) {
+ int32_t format = availableStreamConfigs.data.i32[i + STREAM_FORMAT_OFFSET];
+ int32_t width = availableStreamConfigs.data.i32[i + STREAM_WIDTH_OFFSET];
+ int32_t height = availableStreamConfigs.data.i32[i + STREAM_HEIGHT_OFFSET];
+ int32_t isInput = availableStreamConfigs.data.i32[i + STREAM_IS_INPUT_OFFSET];
+ StreamConfiguration sc = {format, width, height, isInput};
+ (*scm)[format].push_back(sc);
+ }
+}
+
+void StreamConfiguration::getStreamConfigurations(
+ const CameraMetadata &staticInfo, bool maxRes,
+ std::unordered_map<int, std::vector<StreamConfiguration>> *scm) {
+ int32_t scalerKey =
+ SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, maxRes);
+
+ int32_t depthKey =
+ SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, maxRes);
+
+ int32_t dynamicDepthKey =
+ SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS);
+
+ int32_t heicKey =
+ SessionConfigurationUtils::getAppropriateModeTag(
+ ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS);
+
+ getStreamConfigurations(staticInfo, scalerKey, scm);
+ getStreamConfigurations(staticInfo, depthKey, scm);
+ getStreamConfigurations(staticInfo, dynamicDepthKey, scm);
+ getStreamConfigurations(staticInfo, heicKey, scm);
+}
+
+int32_t SessionConfigurationUtils::getAppropriateModeTag(int32_t defaultTag, bool maxResolution) {
+ if (!maxResolution) {
+ return defaultTag;
+ }
+ switch (defaultTag) {
+ case ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS:
+ return ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION;
+ case ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS:
+ return ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION;
+ case ANDROID_SCALER_AVAILABLE_STALL_DURATIONS:
+ return ANDROID_SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION;
+ case ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS:
+ return ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION;
+ case ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS:
+ return ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION;
+ case ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS:
+ return ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS;
+ case ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS:
+ return ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION;
+ case ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS:
+ return ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION;
+ case ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS:
+ return ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS;
+ case ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS:
+ return ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION;
+ case ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS:
+ return ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION;
+ case ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS:
+ return ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS;
+ case ANDROID_SENSOR_OPAQUE_RAW_SIZE:
+ return ANDROID_SENSOR_OPAQUE_RAW_SIZE_MAXIMUM_RESOLUTION;
+ case ANDROID_LENS_INTRINSIC_CALIBRATION:
+ return ANDROID_LENS_INTRINSIC_CALIBRATION_MAXIMUM_RESOLUTION;
+ case ANDROID_LENS_DISTORTION:
+ return ANDROID_LENS_DISTORTION_MAXIMUM_RESOLUTION;
+ default:
+ ALOGE("%s: Tag %d doesn't have a maximum resolution counterpart", __FUNCTION__,
+ defaultTag);
+ return -1;
+ }
+ return -1;
+}
+
+
+StreamConfigurationPair
+SessionConfigurationUtils::getStreamConfigurationPair(const CameraMetadata &staticInfo) {
+ camera3::StreamConfigurationPair streamConfigurationPair;
+ camera3::StreamConfiguration::getStreamConfigurations(staticInfo, false,
+ &streamConfigurationPair.mDefaultStreamConfigurationMap);
+ camera3::StreamConfiguration::getStreamConfigurations(staticInfo, true,
+ &streamConfigurationPair.mMaximumResolutionStreamConfigurationMap);
+ return streamConfigurationPair;
+}
+
+int64_t SessionConfigurationUtils::euclidDistSquare(int32_t x0, int32_t y0, int32_t x1, int32_t y1) {
+ int64_t d0 = x0 - x1;
+ int64_t d1 = y0 - y1;
+ return d0 * d0 + d1 * d1;
+}
+
+bool SessionConfigurationUtils::roundBufferDimensionNearest(int32_t width, int32_t height,
+ int32_t format, android_dataspace dataSpace,
+ const CameraMetadata& info, bool maxResolution, /*out*/int32_t* outWidth,
+ /*out*/int32_t* outHeight) {
+ const int32_t depthSizesTag =
+ getAppropriateModeTag(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS,
+ maxResolution);
+ const int32_t scalerSizesTag =
+ getAppropriateModeTag(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, maxResolution);
+ const int32_t heicSizesTag =
+ getAppropriateModeTag(ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS, maxResolution);
+
+ camera_metadata_ro_entry streamConfigs =
+ (dataSpace == HAL_DATASPACE_DEPTH) ? info.find(depthSizesTag) :
+ (dataSpace == static_cast<android_dataspace>(HAL_DATASPACE_HEIF)) ?
+ info.find(heicSizesTag) :
+ info.find(scalerSizesTag);
+
+ int32_t bestWidth = -1;
+ int32_t bestHeight = -1;
+
+ // Iterate through listed stream configurations and find the one with the smallest euclidean
+ // distance from the given dimensions for the given format.
+ for (size_t i = 0; i < streamConfigs.count; i += 4) {
+ int32_t fmt = streamConfigs.data.i32[i];
+ int32_t w = streamConfigs.data.i32[i + 1];
+ int32_t h = streamConfigs.data.i32[i + 2];
+
+ // Ignore input/output type for now
+ if (fmt == format) {
+ if (w == width && h == height) {
+ bestWidth = width;
+ bestHeight = height;
+ break;
+ } else if (w <= ROUNDING_WIDTH_CAP && (bestWidth == -1 ||
+ SessionConfigurationUtils::euclidDistSquare(w, h, width, height) <
+ SessionConfigurationUtils::euclidDistSquare(bestWidth, bestHeight, width,
+ height))) {
+ bestWidth = w;
+ bestHeight = h;
+ }
+ }
+ }
+
+ if (bestWidth == -1) {
+ // Return false if no configurations for this format were listed
+ return false;
+ }
+
+ // Set the outputs to the closet width/height
+ if (outWidth != NULL) {
+ *outWidth = bestWidth;
+ }
+ if (outHeight != NULL) {
+ *outHeight = bestHeight;
+ }
+
+ // Return true if at least one configuration for this format was listed
+ return true;
+}
+
+bool SessionConfigurationUtils::isPublicFormat(int32_t format)
+{
+ switch(format) {
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ case HAL_PIXEL_FORMAT_RGB_888:
+ case HAL_PIXEL_FORMAT_RGB_565:
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ case HAL_PIXEL_FORMAT_YV12:
+ case HAL_PIXEL_FORMAT_Y8:
+ case HAL_PIXEL_FORMAT_Y16:
+ case HAL_PIXEL_FORMAT_RAW16:
+ case HAL_PIXEL_FORMAT_RAW10:
+ case HAL_PIXEL_FORMAT_RAW12:
+ case HAL_PIXEL_FORMAT_RAW_OPAQUE:
+ case HAL_PIXEL_FORMAT_BLOB:
+ case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+ case HAL_PIXEL_FORMAT_YCbCr_420_888:
+ case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ case HAL_PIXEL_FORMAT_YCbCr_422_I:
+ return true;
+ default:
+ return false;
+ }
+}
+
+binder::Status SessionConfigurationUtils::createSurfaceFromGbp(
+ OutputStreamInfo& streamInfo, bool isStreamInfoValid,
+ sp<Surface>& surface, const sp<IGraphicBufferProducer>& gbp,
+ const String8 &logicalCameraId, const CameraMetadata &physicalCameraMetadata,
+ const std::vector<int32_t> &sensorPixelModesUsed){
+ // bufferProducer must be non-null
+ if (gbp == nullptr) {
+ String8 msg = String8::format("Camera %s: Surface is NULL", logicalCameraId.string());
+ ALOGW("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
+ // HACK b/10949105
+ // Query consumer usage bits to set async operation mode for
+ // GLConsumer using controlledByApp parameter.
+ bool useAsync = false;
+ uint64_t consumerUsage = 0;
+ status_t err;
+ if ((err = gbp->getConsumerUsage(&consumerUsage)) != OK) {
+ String8 msg = String8::format("Camera %s: Failed to query Surface consumer usage: %s (%d)",
+ logicalCameraId.string(), strerror(-err), err);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
+ }
+ if (consumerUsage & GraphicBuffer::USAGE_HW_TEXTURE) {
+ ALOGW("%s: Camera %s with consumer usage flag: %" PRIu64 ": Forcing asynchronous mode for"
+ "stream", __FUNCTION__, logicalCameraId.string(), consumerUsage);
+ useAsync = true;
+ }
+
+ uint64_t disallowedFlags = GraphicBuffer::USAGE_HW_VIDEO_ENCODER |
+ GRALLOC_USAGE_RENDERSCRIPT;
+ uint64_t allowedFlags = GraphicBuffer::USAGE_SW_READ_MASK |
+ GraphicBuffer::USAGE_HW_TEXTURE |
+ GraphicBuffer::USAGE_HW_COMPOSER;
+ bool flexibleConsumer = (consumerUsage & disallowedFlags) == 0 &&
+ (consumerUsage & allowedFlags) != 0;
+
+ surface = new Surface(gbp, useAsync);
+ ANativeWindow *anw = surface.get();
+
+ int width, height, format;
+ android_dataspace dataSpace;
+ if ((err = anw->query(anw, NATIVE_WINDOW_WIDTH, &width)) != OK) {
+ String8 msg = String8::format("Camera %s: Failed to query Surface width: %s (%d)",
+ logicalCameraId.string(), strerror(-err), err);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
+ }
+ if ((err = anw->query(anw, NATIVE_WINDOW_HEIGHT, &height)) != OK) {
+ String8 msg = String8::format("Camera %s: Failed to query Surface height: %s (%d)",
+ logicalCameraId.string(), strerror(-err), err);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
+ }
+ if ((err = anw->query(anw, NATIVE_WINDOW_FORMAT, &format)) != OK) {
+ String8 msg = String8::format("Camera %s: Failed to query Surface format: %s (%d)",
+ logicalCameraId.string(), strerror(-err), err);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
+ }
+ if ((err = anw->query(anw, NATIVE_WINDOW_DEFAULT_DATASPACE,
+ reinterpret_cast<int*>(&dataSpace))) != OK) {
+ String8 msg = String8::format("Camera %s: Failed to query Surface dataspace: %s (%d)",
+ logicalCameraId.string(), strerror(-err), err);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
+ }
+
+ // FIXME: remove this override since the default format should be
+ // IMPLEMENTATION_DEFINED. b/9487482 & b/35317944
+ if ((format >= HAL_PIXEL_FORMAT_RGBA_8888 && format <= HAL_PIXEL_FORMAT_BGRA_8888) &&
+ ((consumerUsage & GRALLOC_USAGE_HW_MASK) &&
+ ((consumerUsage & GRALLOC_USAGE_SW_READ_MASK) == 0))) {
+ ALOGW("%s: Camera %s: Overriding format %#x to IMPLEMENTATION_DEFINED",
+ __FUNCTION__, logicalCameraId.string(), format);
+ format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
+ }
+ std::unordered_set<int32_t> overriddenSensorPixelModes;
+ if (checkAndOverrideSensorPixelModesUsed(sensorPixelModesUsed, format, width, height,
+ physicalCameraMetadata, flexibleConsumer, &overriddenSensorPixelModes) != OK) {
+ String8 msg = String8::format("Camera %s: sensor pixel modes for stream with "
+ "format %#x are not valid",logicalCameraId.string(), format);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
+ bool foundInMaxRes = false;
+ if (overriddenSensorPixelModes.find(ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) !=
+ overriddenSensorPixelModes.end()) {
+ // we can use the default stream configuration map
+ foundInMaxRes = true;
+ }
+ // Round dimensions to the nearest dimensions available for this format
+ if (flexibleConsumer && isPublicFormat(format) &&
+ !SessionConfigurationUtils::roundBufferDimensionNearest(width, height,
+ format, dataSpace, physicalCameraMetadata, foundInMaxRes, /*out*/&width,
+ /*out*/&height)) {
+ String8 msg = String8::format("Camera %s: No supported stream configurations with "
+ "format %#x defined, failed to create output stream",
+ logicalCameraId.string(), format);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
+
+ if (!isStreamInfoValid) {
+ streamInfo.width = width;
+ streamInfo.height = height;
+ streamInfo.format = format;
+ streamInfo.dataSpace = dataSpace;
+ streamInfo.consumerUsage = consumerUsage;
+ streamInfo.sensorPixelModesUsed = overriddenSensorPixelModes;
+ return binder::Status::ok();
+ }
+ if (width != streamInfo.width) {
+ String8 msg = String8::format("Camera %s:Surface width doesn't match: %d vs %d",
+ logicalCameraId.string(), width, streamInfo.width);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
+ if (height != streamInfo.height) {
+ String8 msg = String8::format("Camera %s:Surface height doesn't match: %d vs %d",
+ logicalCameraId.string(), height, streamInfo.height);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
+ if (format != streamInfo.format) {
+ String8 msg = String8::format("Camera %s:Surface format doesn't match: %d vs %d",
+ logicalCameraId.string(), format, streamInfo.format);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
+ if (format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
+ if (dataSpace != streamInfo.dataSpace) {
+ String8 msg = String8::format("Camera %s:Surface dataSpace doesn't match: %d vs %d",
+ logicalCameraId.string(), dataSpace, streamInfo.dataSpace);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
+ //At the native side, there isn't a way to check whether 2 surfaces come from the same
+ //surface class type. Use usage flag to approximate the comparison.
+ if (consumerUsage != streamInfo.consumerUsage) {
+ String8 msg = String8::format(
+ "Camera %s:Surface usage flag doesn't match %" PRIu64 " vs %" PRIu64 "",
+ logicalCameraId.string(), consumerUsage, streamInfo.consumerUsage);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
+ }
+ return binder::Status::ok();
+}
+
+void SessionConfigurationUtils::mapStreamInfo(const OutputStreamInfo &streamInfo,
+ camera3::camera_stream_rotation_t rotation, String8 physicalId,
+ int32_t groupId, hardware::camera::device::V3_7::Stream *stream /*out*/) {
+ if (stream == nullptr) {
+ return;
+ }
+
+ stream->v3_4.v3_2.streamType = hardware::camera::device::V3_2::StreamType::OUTPUT;
+ stream->v3_4.v3_2.width = streamInfo.width;
+ stream->v3_4.v3_2.height = streamInfo.height;
+ stream->v3_4.v3_2.format = Camera3Device::mapToPixelFormat(streamInfo.format);
+ auto u = streamInfo.consumerUsage;
+ camera3::Camera3OutputStream::applyZSLUsageQuirk(streamInfo.format, &u);
+ stream->v3_4.v3_2.usage = Camera3Device::mapToConsumerUsage(u);
+ stream->v3_4.v3_2.dataSpace = Camera3Device::mapToHidlDataspace(streamInfo.dataSpace);
+ stream->v3_4.v3_2.rotation = Camera3Device::mapToStreamRotation(rotation);
+ stream->v3_4.v3_2.id = -1; // Invalid stream id
+ stream->v3_4.physicalCameraId = std::string(physicalId.string());
+ stream->v3_4.bufferSize = 0;
+ stream->groupId = groupId;
+ stream->sensorPixelModesUsed.resize(streamInfo.sensorPixelModesUsed.size());
+ size_t idx = 0;
+ for (auto mode : streamInfo.sensorPixelModesUsed) {
+ stream->sensorPixelModesUsed[idx++] =
+ static_cast<CameraMetadataEnumAndroidSensorPixelMode>(mode);
+ }
+}
+
+binder::Status SessionConfigurationUtils::checkPhysicalCameraId(
+ const std::vector<std::string> &physicalCameraIds, const String8 &physicalCameraId,
+ const String8 &logicalCameraId) {
+ if (physicalCameraId.size() == 0) {
+ return binder::Status::ok();
+ }
+ if (std::find(physicalCameraIds.begin(), physicalCameraIds.end(),
+ physicalCameraId.string()) == physicalCameraIds.end()) {
+ String8 msg = String8::format("Camera %s: Camera doesn't support physicalCameraId %s.",
+ logicalCameraId.string(), physicalCameraId.string());
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
+ return binder::Status::ok();
+}
+
+binder::Status SessionConfigurationUtils::checkSurfaceType(size_t numBufferProducers,
+ bool deferredConsumer, int surfaceType) {
+ if (numBufferProducers > MAX_SURFACES_PER_STREAM) {
+ ALOGE("%s: GraphicBufferProducer count %zu for stream exceeds limit of %d",
+ __FUNCTION__, numBufferProducers, MAX_SURFACES_PER_STREAM);
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, "Surface count is too high");
+ } else if ((numBufferProducers == 0) && (!deferredConsumer)) {
+ ALOGE("%s: Number of consumers cannot be smaller than 1", __FUNCTION__);
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, "No valid consumers.");
+ }
+
+ bool validSurfaceType = ((surfaceType == OutputConfiguration::SURFACE_TYPE_SURFACE_VIEW) ||
+ (surfaceType == OutputConfiguration::SURFACE_TYPE_SURFACE_TEXTURE));
+
+ if (deferredConsumer && !validSurfaceType) {
+ ALOGE("%s: Target surface has invalid surfaceType = %d.", __FUNCTION__, surfaceType);
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, "Target Surface is invalid");
+ }
+
+ return binder::Status::ok();
+}
+
+binder::Status SessionConfigurationUtils::checkOperatingMode(int operatingMode,
+ const CameraMetadata &staticInfo, const String8 &cameraId) {
+ if (operatingMode < 0) {
+ String8 msg = String8::format(
+ "Camera %s: Invalid operating mode %d requested", cameraId.string(), operatingMode);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
+ msg.string());
+ }
+
+ bool isConstrainedHighSpeed = (operatingMode == ICameraDeviceUser::CONSTRAINED_HIGH_SPEED_MODE);
+ if (isConstrainedHighSpeed) {
+ camera_metadata_ro_entry_t entry = staticInfo.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
+ bool isConstrainedHighSpeedSupported = false;
+ for(size_t i = 0; i < entry.count; ++i) {
+ uint8_t capability = entry.data.u8[i];
+ if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO) {
+ isConstrainedHighSpeedSupported = true;
+ break;
+ }
+ }
+ if (!isConstrainedHighSpeedSupported) {
+ String8 msg = String8::format(
+ "Camera %s: Try to create a constrained high speed configuration on a device"
+ " that doesn't support it.", cameraId.string());
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
+ msg.string());
+ }
+ }
+
+ return binder::Status::ok();
+}
binder::Status
SessionConfigurationUtils::convertToHALStreamCombination(
const SessionConfiguration& sessionConfiguration,
const String8 &logicalCameraId, const CameraMetadata &deviceInfo,
metadataGetter getMetadata, const std::vector<std::string> &physicalCameraIds,
- hardware::camera::device::V3_4::StreamConfiguration &streamConfiguration, bool *earlyExit) {
- // TODO: http://b/148329298 Move the other dependencies from
- // CameraDeviceClient into SessionConfigurationUtils.
- return CameraDeviceClient::convertToHALStreamCombination(sessionConfiguration, logicalCameraId,
- deviceInfo, getMetadata, physicalCameraIds, streamConfiguration, earlyExit);
+ hardware::camera::device::V3_7::StreamConfiguration &streamConfiguration, bool *earlyExit) {
+
+ auto operatingMode = sessionConfiguration.getOperatingMode();
+ binder::Status res = checkOperatingMode(operatingMode, deviceInfo, logicalCameraId);
+ if (!res.isOk()) {
+ return res;
+ }
+
+ if (earlyExit == nullptr) {
+ String8 msg("earlyExit nullptr");
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
+ *earlyExit = false;
+ auto ret = Camera3Device::mapToStreamConfigurationMode(
+ static_cast<camera_stream_configuration_mode_t> (operatingMode),
+ /*out*/ &streamConfiguration.operationMode);
+ if (ret != OK) {
+ String8 msg = String8::format(
+ "Camera %s: Failed mapping operating mode %d requested: %s (%d)",
+ logicalCameraId.string(), operatingMode, strerror(-ret), ret);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
+ msg.string());
+ }
+
+ bool isInputValid = (sessionConfiguration.getInputWidth() > 0) &&
+ (sessionConfiguration.getInputHeight() > 0) &&
+ (sessionConfiguration.getInputFormat() > 0);
+ auto outputConfigs = sessionConfiguration.getOutputConfigurations();
+ size_t streamCount = outputConfigs.size();
+ streamCount = isInputValid ? streamCount + 1 : streamCount;
+ streamConfiguration.streams.resize(streamCount);
+ size_t streamIdx = 0;
+ if (isInputValid) {
+ hardware::hidl_vec<CameraMetadataEnumAndroidSensorPixelMode> defaultSensorPixelModes;
+ defaultSensorPixelModes.resize(1);
+ defaultSensorPixelModes[0] =
+ static_cast<CameraMetadataEnumAndroidSensorPixelMode>(
+ ANDROID_SENSOR_PIXEL_MODE_DEFAULT);
+ streamConfiguration.streams[streamIdx++] = {{{/*streamId*/0,
+ hardware::camera::device::V3_2::StreamType::INPUT,
+ static_cast<uint32_t> (sessionConfiguration.getInputWidth()),
+ static_cast<uint32_t> (sessionConfiguration.getInputHeight()),
+ Camera3Device::mapToPixelFormat(sessionConfiguration.getInputFormat()),
+ /*usage*/ 0, HAL_DATASPACE_UNKNOWN,
+ hardware::camera::device::V3_2::StreamRotation::ROTATION_0},
+ /*physicalId*/ nullptr, /*bufferSize*/0}, /*groupId*/-1, defaultSensorPixelModes};
+ streamConfiguration.multiResolutionInputImage =
+ sessionConfiguration.inputIsMultiResolution();
+ }
+
+ for (const auto &it : outputConfigs) {
+ const std::vector<sp<IGraphicBufferProducer>>& bufferProducers =
+ it.getGraphicBufferProducers();
+ bool deferredConsumer = it.isDeferred();
+ String8 physicalCameraId = String8(it.getPhysicalCameraId());
+
+ std::vector<int32_t> sensorPixelModesUsed = it.getSensorPixelModesUsed();
+ const CameraMetadata &physicalDeviceInfo = getMetadata(physicalCameraId);
+ const CameraMetadata &metadataChosen =
+ physicalCameraId.size() > 0 ? physicalDeviceInfo : deviceInfo;
+
+ size_t numBufferProducers = bufferProducers.size();
+ bool isStreamInfoValid = false;
+ int32_t groupId = it.isMultiResolution() ? it.getSurfaceSetID() : -1;
+ OutputStreamInfo streamInfo;
+
+ res = checkSurfaceType(numBufferProducers, deferredConsumer, it.getSurfaceType());
+ if (!res.isOk()) {
+ return res;
+ }
+ res = checkPhysicalCameraId(physicalCameraIds, physicalCameraId,
+ logicalCameraId);
+ if (!res.isOk()) {
+ return res;
+ }
+
+ if (deferredConsumer) {
+ streamInfo.width = it.getWidth();
+ streamInfo.height = it.getHeight();
+ streamInfo.format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
+ streamInfo.dataSpace = android_dataspace_t::HAL_DATASPACE_UNKNOWN;
+ auto surfaceType = it.getSurfaceType();
+ streamInfo.consumerUsage = GraphicBuffer::USAGE_HW_TEXTURE;
+ if (surfaceType == OutputConfiguration::SURFACE_TYPE_SURFACE_VIEW) {
+ streamInfo.consumerUsage |= GraphicBuffer::USAGE_HW_COMPOSER;
+ }
+ if (checkAndOverrideSensorPixelModesUsed(sensorPixelModesUsed,
+ streamInfo.format, streamInfo.width,
+ streamInfo.height, metadataChosen, false /*flexibleConsumer*/,
+ &streamInfo.sensorPixelModesUsed) != OK) {
+ ALOGE("%s: Deferred surface sensor pixel modes not valid",
+ __FUNCTION__);
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
+ "Deferred surface sensor pixel modes not valid");
+ }
+ mapStreamInfo(streamInfo, camera3::CAMERA_STREAM_ROTATION_0, physicalCameraId, groupId,
+ &streamConfiguration.streams[streamIdx++]);
+ isStreamInfoValid = true;
+
+ if (numBufferProducers == 0) {
+ continue;
+ }
+ }
+
+ for (auto& bufferProducer : bufferProducers) {
+ sp<Surface> surface;
+ res = createSurfaceFromGbp(streamInfo, isStreamInfoValid, surface, bufferProducer,
+ logicalCameraId, metadataChosen, sensorPixelModesUsed);
+
+ if (!res.isOk())
+ return res;
+
+ if (!isStreamInfoValid) {
+ bool isDepthCompositeStream =
+ camera3::DepthCompositeStream::isDepthCompositeStream(surface);
+ bool isHeicCompositeStream =
+ camera3::HeicCompositeStream::isHeicCompositeStream(surface);
+ if (isDepthCompositeStream || isHeicCompositeStream) {
+ // We need to take in to account that composite streams can have
+ // additional internal camera streams.
+ std::vector<OutputStreamInfo> compositeStreams;
+ if (isDepthCompositeStream) {
+ // TODO: Take care of composite streams.
+ ret = camera3::DepthCompositeStream::getCompositeStreamInfo(streamInfo,
+ deviceInfo, &compositeStreams);
+ } else {
+ ret = camera3::HeicCompositeStream::getCompositeStreamInfo(streamInfo,
+ deviceInfo, &compositeStreams);
+ }
+ if (ret != OK) {
+ String8 msg = String8::format(
+ "Camera %s: Failed adding composite streams: %s (%d)",
+ logicalCameraId.string(), strerror(-ret), ret);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
+
+ if (compositeStreams.size() == 0) {
+ // No internal streams means composite stream not
+ // supported.
+ *earlyExit = true;
+ return binder::Status::ok();
+ } else if (compositeStreams.size() > 1) {
+ streamCount += compositeStreams.size() - 1;
+ streamConfiguration.streams.resize(streamCount);
+ }
+
+ for (const auto& compositeStream : compositeStreams) {
+ mapStreamInfo(compositeStream,
+ static_cast<camera_stream_rotation_t> (it.getRotation()),
+ physicalCameraId, groupId,
+ &streamConfiguration.streams[streamIdx++]);
+ }
+ } else {
+ mapStreamInfo(streamInfo,
+ static_cast<camera_stream_rotation_t> (it.getRotation()),
+ physicalCameraId, groupId, &streamConfiguration.streams[streamIdx++]);
+ }
+ isStreamInfoValid = true;
+ }
+ }
+ }
+ return binder::Status::ok();
}
-}// namespace android
+static bool inStreamConfigurationMap(int format, int width, int height,
+ const std::unordered_map<int, std::vector<camera3::StreamConfiguration>> &sm) {
+ auto scs = sm.find(format);
+ if (scs == sm.end()) {
+ return false;
+ }
+ for (auto &sc : scs->second) {
+ if (sc.width == width && sc.height == height && sc.isInput == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static std::unordered_set<int32_t> convertToSet(const std::vector<int32_t> &sensorPixelModesUsed) {
+ return std::unordered_set<int32_t>(sensorPixelModesUsed.begin(), sensorPixelModesUsed.end());
+}
+
+status_t SessionConfigurationUtils::checkAndOverrideSensorPixelModesUsed(
+ const std::vector<int32_t> &sensorPixelModesUsed, int format, int width, int height,
+ const CameraMetadata &staticInfo, bool flexibleConsumer,
+ std::unordered_set<int32_t> *overriddenSensorPixelModesUsed) {
+ if (!isUltraHighResolutionSensor(staticInfo)) {
+ overriddenSensorPixelModesUsed->clear();
+ overriddenSensorPixelModesUsed->insert(ANDROID_SENSOR_PIXEL_MODE_DEFAULT);
+ return OK;
+ }
+
+ StreamConfigurationPair streamConfigurationPair = getStreamConfigurationPair(staticInfo);
+ const std::unordered_set<int32_t> &sensorPixelModesUsedSet =
+ convertToSet(sensorPixelModesUsed);
+ bool isInDefaultStreamConfigurationMap =
+ inStreamConfigurationMap(format, width, height,
+ streamConfigurationPair.mDefaultStreamConfigurationMap);
+
+ bool isInMaximumResolutionStreamConfigurationMap =
+ inStreamConfigurationMap(format, width, height,
+ streamConfigurationPair.mMaximumResolutionStreamConfigurationMap);
+
+ // Case 1: The client has not changed the sensor mode defaults. In this case, we check if the
+ // size + format of the OutputConfiguration is found exclusively in 1.
+ // If yes, add that sensorPixelMode to overriddenSensorPixelModes.
+ // If no, add 'DEFAULT' to sensorPixelMode. This maintains backwards
+ // compatibility.
+ if (sensorPixelModesUsedSet.size() == 0) {
+ // Ambiguous case, default to only 'DEFAULT' mode.
+ if (isInDefaultStreamConfigurationMap && isInMaximumResolutionStreamConfigurationMap) {
+ overriddenSensorPixelModesUsed->insert(ANDROID_SENSOR_PIXEL_MODE_DEFAULT);
+ return OK;
+ }
+ // We don't allow flexible consumer for max resolution mode.
+ if (isInMaximumResolutionStreamConfigurationMap) {
+ overriddenSensorPixelModesUsed->insert(ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION);
+ return OK;
+ }
+ if (isInDefaultStreamConfigurationMap || (flexibleConsumer && width < ROUNDING_WIDTH_CAP)) {
+ overriddenSensorPixelModesUsed->insert(ANDROID_SENSOR_PIXEL_MODE_DEFAULT);
+ return OK;
+ }
+ return BAD_VALUE;
+ }
+
+ // Case2: The app has set sensorPixelModesUsed, we need to verify that they
+ // are valid / err out.
+ if (sensorPixelModesUsedSet.find(ANDROID_SENSOR_PIXEL_MODE_DEFAULT) !=
+ sensorPixelModesUsedSet.end() && !isInDefaultStreamConfigurationMap) {
+ return BAD_VALUE;
+ }
+
+ if (sensorPixelModesUsedSet.find(ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) !=
+ sensorPixelModesUsedSet.end() && !isInMaximumResolutionStreamConfigurationMap) {
+ return BAD_VALUE;
+ }
+ *overriddenSensorPixelModesUsed = sensorPixelModesUsedSet;
+ return OK;
+}
+
+bool SessionConfigurationUtils::isUltraHighResolutionSensor(const CameraMetadata &deviceInfo) {
+ camera_metadata_ro_entry_t entryCap;
+ entryCap = deviceInfo.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
+ // Go through the capabilities and check if it has
+ // ANDROID_REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR
+ for (size_t i = 0; i < entryCap.count; ++i) {
+ uint8_t capability = entryCap.data.u8[i];
+ if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SessionConfigurationUtils::convertHALStreamCombinationFromV37ToV34(
+ hardware::camera::device::V3_4::StreamConfiguration &streamConfigV34,
+ const hardware::camera::device::V3_7::StreamConfiguration &streamConfigV37) {
+ if (streamConfigV37.multiResolutionInputImage) {
+ // ICameraDevice older than 3.7 doesn't support multi-resolution input image.
+ return false;
+ }
+
+ streamConfigV34.streams.resize(streamConfigV37.streams.size());
+ for (size_t i = 0; i < streamConfigV37.streams.size(); i++) {
+ if (streamConfigV37.streams[i].groupId != -1) {
+ // ICameraDevice older than 3.7 doesn't support multi-resolution output
+ // image
+ return false;
+ }
+ streamConfigV34.streams[i] = streamConfigV37.streams[i].v3_4;
+ }
+ streamConfigV34.operationMode = streamConfigV37.operationMode;
+ streamConfigV34.sessionParams = streamConfigV37.sessionParams;
+
+ return true;
+}
+
+} // namespace camera3
+} // namespace android
diff --git a/services/camera/libcameraservice/utils/SessionConfigurationUtils.h b/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
index cfb9f17..863a0cd 100644
--- a/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
+++ b/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
@@ -21,16 +21,90 @@
#include <camera/camera2/OutputConfiguration.h>
#include <camera/camera2/SessionConfiguration.h>
#include <camera/camera2/SubmitInfo.h>
+#include <android/hardware/camera/device/3.7/types.h>
#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
+#include <android/hardware/camera/device/3.7/ICameraDeviceSession.h>
+
+#include <device3/Camera3StreamInterface.h>
#include <stdint.h>
+// Convenience methods for constructing binder::Status objects for error returns
+
+#define STATUS_ERROR(errorCode, errorString) \
+ binder::Status::fromServiceSpecificError(errorCode, \
+ String8::format("%s:%d: %s", __FUNCTION__, __LINE__, errorString))
+
+#define STATUS_ERROR_FMT(errorCode, errorString, ...) \
+ binder::Status::fromServiceSpecificError(errorCode, \
+ String8::format("%s:%d: " errorString, __FUNCTION__, __LINE__, \
+ __VA_ARGS__))
+
namespace android {
+namespace camera3 {
typedef std::function<CameraMetadata (const String8 &)> metadataGetter;
+class StreamConfiguration {
+public:
+ int32_t format;
+ int32_t width;
+ int32_t height;
+ int32_t isInput;
+ static void getStreamConfigurations(
+ const CameraMetadata &static_info, bool maxRes,
+ std::unordered_map<int, std::vector<StreamConfiguration>> *scm);
+ static void getStreamConfigurations(
+ const CameraMetadata &static_info, int configuration,
+ std::unordered_map<int, std::vector<StreamConfiguration>> *scm);
+};
+
+// Holds the default StreamConfigurationMap and Maximum resolution
+// StreamConfigurationMap for a camera device.
+struct StreamConfigurationPair {
+ std::unordered_map<int, std::vector<camera3::StreamConfiguration>>
+ mDefaultStreamConfigurationMap;
+ std::unordered_map<int, std::vector<camera3::StreamConfiguration>>
+ mMaximumResolutionStreamConfigurationMap;
+};
+
class SessionConfigurationUtils {
public:
+ static int64_t euclidDistSquare(int32_t x0, int32_t y0, int32_t x1, int32_t y1);
+
+ // Find the closest dimensions for a given format in available stream configurations with
+ // a width <= ROUNDING_WIDTH_CAP
+ static bool roundBufferDimensionNearest(int32_t width, int32_t height, int32_t format,
+ android_dataspace dataSpace, const CameraMetadata& info, bool maxResolution,
+ /*out*/int32_t* outWidth, /*out*/int32_t* outHeight);
+
+ //check if format is not custom format
+ static bool isPublicFormat(int32_t format);
+
+ // Create a Surface from an IGraphicBufferProducer. Returns error if
+ // IGraphicBufferProducer's property doesn't match with streamInfo
+ static binder::Status createSurfaceFromGbp(
+ camera3::OutputStreamInfo& streamInfo, bool isStreamInfoValid,
+ sp<Surface>& surface, const sp<IGraphicBufferProducer>& gbp,
+ const String8 &logicalCameraId, const CameraMetadata &physicalCameraMetadata,
+ const std::vector<int32_t> &sensorPixelModesUsed);
+
+ static void mapStreamInfo(const camera3::OutputStreamInfo &streamInfo,
+ camera3::camera_stream_rotation_t rotation, String8 physicalId, int32_t groupId,
+ hardware::camera::device::V3_7::Stream *stream /*out*/);
+
+ // Check that the physicalCameraId passed in is spported by the camera
+ // device.
+ static binder::Status checkPhysicalCameraId(
+ const std::vector<std::string> &physicalCameraIds, const String8 &physicalCameraId,
+ const String8 &logicalCameraId);
+
+ static binder::Status checkSurfaceType(size_t numBufferProducers,
+ bool deferredConsumer, int surfaceType);
+
+ static binder::Status checkOperatingMode(int operatingMode,
+ const CameraMetadata &staticInfo, const String8 &cameraId);
+
// utility function to convert AIDL SessionConfiguration to HIDL
// streamConfiguration. Also checks for validity of SessionConfiguration and
// returns a non-ok binder::Status if the passed in session configuration
@@ -39,9 +113,33 @@
convertToHALStreamCombination(const SessionConfiguration& sessionConfiguration,
const String8 &cameraId, const CameraMetadata &deviceInfo,
metadataGetter getMetadata, const std::vector<std::string> &physicalCameraIds,
- hardware::camera::device::V3_4::StreamConfiguration &streamConfiguration,
+ hardware::camera::device::V3_7::StreamConfiguration &streamConfiguration,
bool *earlyExit);
+
+ // Utility function to convert a V3_7::StreamConfiguration to
+ // V3_4::StreamConfiguration. Return false if the original V3_7 configuration cannot
+ // be used by older version HAL.
+ static bool convertHALStreamCombinationFromV37ToV34(
+ hardware::camera::device::V3_4::StreamConfiguration &streamConfigV34,
+ const hardware::camera::device::V3_7::StreamConfiguration &streamConfigV37);
+
+ static StreamConfigurationPair getStreamConfigurationPair(const CameraMetadata &metadata);
+
+ static status_t checkAndOverrideSensorPixelModesUsed(
+ const std::vector<int32_t> &sensorPixelModesUsed, int format, int width, int height,
+ const CameraMetadata &staticInfo, bool flexibleConsumer,
+ std::unordered_set<int32_t> *overriddenSensorPixelModesUsed);
+
+ static bool isUltraHighResolutionSensor(const CameraMetadata &deviceInfo);
+
+ static int32_t getAppropriateModeTag(int32_t defaultTag, bool maxResolution = false);
+
+ static const int32_t MAX_SURFACES_PER_STREAM = 4;
+
+ static const int32_t ROUNDING_WIDTH_CAP = 1920;
+
};
+} // camera3
} // android
#endif
diff --git a/services/camera/libcameraservice/utils/SessionStatsBuilder.cpp b/services/camera/libcameraservice/utils/SessionStatsBuilder.cpp
new file mode 100644
index 0000000..7a7707c
--- /dev/null
+++ b/services/camera/libcameraservice/utils/SessionStatsBuilder.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "CameraSessionStatsBuilder"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <numeric>
+
+#include <inttypes.h>
+#include <utils/Log.h>
+
+#include "SessionStatsBuilder.h"
+
+namespace android {
+
+// Bins for capture latency: [0, 100], [100, 200], [200, 300], ...
+// [1300, 2100], [2100, inf].
+// Capture latency is in the unit of millisecond.
+const std::array<int32_t, StreamStats::LATENCY_BIN_COUNT-1> StreamStats::mCaptureLatencyBins {
+ { 100, 200, 300, 400, 500, 700, 900, 1300, 2100 } };
+
+status_t SessionStatsBuilder::addStream(int id) {
+ std::lock_guard<std::mutex> l(mLock);
+ StreamStats stats;
+ mStatsMap.emplace(id, stats);
+ return OK;
+}
+
+status_t SessionStatsBuilder::removeStream(int id) {
+ std::lock_guard<std::mutex> l(mLock);
+ mStatsMap.erase(id);
+ return OK;
+}
+
+void SessionStatsBuilder::buildAndReset(int64_t* requestCount,
+ int64_t* errorResultCount, bool* deviceError,
+ std::map<int, StreamStats> *statsMap) {
+ std::lock_guard<std::mutex> l(mLock);
+ *requestCount = mRequestCount;
+ *errorResultCount = mErrorResultCount;
+ *deviceError = mDeviceError;
+ *statsMap = mStatsMap;
+
+ // Reset internal states
+ mRequestCount = 0;
+ mErrorResultCount = 0;
+ mCounterStopped = false;
+ mDeviceError = false;
+ for (auto& streamStats : mStatsMap) {
+ StreamStats& streamStat = streamStats.second;
+ streamStat.mRequestedFrameCount = 0;
+ streamStat.mDroppedFrameCount = 0;
+ streamStat.mCounterStopped = false;
+ streamStat.mStartLatencyMs = 0;
+
+ std::fill(streamStat.mCaptureLatencyHistogram.begin(),
+ streamStat.mCaptureLatencyHistogram.end(), 0);
+ }
+}
+
+void SessionStatsBuilder::startCounter(int id) {
+ std::lock_guard<std::mutex> l(mLock);
+ mStatsMap[id].mCounterStopped = false;
+}
+
+void SessionStatsBuilder::stopCounter(int id) {
+ std::lock_guard<std::mutex> l(mLock);
+ StreamStats& streamStat = mStatsMap[id];
+ streamStat.mCounterStopped = true;
+}
+
+void SessionStatsBuilder::incCounter(int id, bool dropped, int32_t captureLatencyMs) {
+ std::lock_guard<std::mutex> l(mLock);
+
+ auto it = mStatsMap.find(id);
+ if (it == mStatsMap.end()) return;
+
+ StreamStats& streamStat = it->second;
+ if (streamStat.mCounterStopped) return;
+
+ streamStat.mRequestedFrameCount++;
+ if (dropped) {
+ streamStat.mDroppedFrameCount++;
+ } else if (streamStat.mRequestedFrameCount - streamStat.mDroppedFrameCount == 1) {
+ // The capture latency for the first request.
+ streamStat.mStartLatencyMs = captureLatencyMs;
+ }
+
+ streamStat.updateLatencyHistogram(captureLatencyMs);
+}
+
+void SessionStatsBuilder::stopCounter() {
+ std::lock_guard<std::mutex> l(mLock);
+ mCounterStopped = true;
+ for (auto& streamStats : mStatsMap) {
+ streamStats.second.mCounterStopped = true;
+ }
+}
+
+void SessionStatsBuilder::incResultCounter(bool dropped) {
+ std::lock_guard<std::mutex> l(mLock);
+ if (mCounterStopped) return;
+
+ mRequestCount++;
+ if (dropped) mErrorResultCount++;
+}
+
+void SessionStatsBuilder::onDeviceError() {
+ std::lock_guard<std::mutex> l(mLock);
+ mDeviceError = true;
+}
+
+void StreamStats::updateLatencyHistogram(int32_t latencyMs) {
+ size_t i;
+ for (i = 0; i < mCaptureLatencyBins.size(); i++) {
+ if (latencyMs < mCaptureLatencyBins[i]) {
+ mCaptureLatencyHistogram[i] ++;
+ break;
+ }
+ }
+
+ if (i == mCaptureLatencyBins.size()) {
+ mCaptureLatencyHistogram[i]++;
+ }
+}
+
+}; // namespace android
diff --git a/services/camera/libcameraservice/utils/SessionStatsBuilder.h b/services/camera/libcameraservice/utils/SessionStatsBuilder.h
new file mode 100644
index 0000000..c23abb6
--- /dev/null
+++ b/services/camera/libcameraservice/utils/SessionStatsBuilder.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SERVICE_UTILS_SESSION_STATS_BUILDER_H
+#define ANDROID_SERVICE_UTILS_SESSION_STATS_BUILDER_H
+
+#include <utils/Errors.h>
+
+#include <array>
+#include <map>
+#include <mutex>
+
+namespace android {
+
+// Helper class to build stream stats
+struct StreamStats {
+ // Fields for buffer drop
+ int64_t mRequestedFrameCount;
+ int64_t mDroppedFrameCount;
+ bool mCounterStopped;
+
+ // Fields for stream startup latency
+ int32_t mStartLatencyMs;
+
+ // Fields for capture latency measurement
+ const static int LATENCY_BIN_COUNT = 10;
+ // Boundary values separating between adjacent bins, excluding 0 and
+ // infinity.
+ const static std::array<int32_t, LATENCY_BIN_COUNT-1> mCaptureLatencyBins;
+ // Counter values for all histogram bins. One more entry than mCaptureLatencyBins.
+ std::array<int64_t, LATENCY_BIN_COUNT> mCaptureLatencyHistogram;
+
+ StreamStats() : mRequestedFrameCount(0),
+ mDroppedFrameCount(0),
+ mCounterStopped(false),
+ mStartLatencyMs(0),
+ mCaptureLatencyHistogram{}
+ {}
+
+ void updateLatencyHistogram(int32_t latencyMs);
+};
+
+// Helper class to build session stats
+class SessionStatsBuilder {
+public:
+
+ status_t addStream(int streamId);
+ status_t removeStream(int streamId);
+
+ // Return the session statistics and reset the internal states.
+ void buildAndReset(/*out*/int64_t* requestCount,
+ /*out*/int64_t* errorResultCount,
+ /*out*/bool* deviceError,
+ /*out*/std::map<int, StreamStats> *statsMap);
+
+ // Stream specific counter
+ void startCounter(int streamId);
+ void stopCounter(int streamId);
+ void incCounter(int streamId, bool dropped, int32_t captureLatencyMs);
+
+ // Session specific counter
+ void stopCounter();
+ void incResultCounter(bool dropped);
+ void onDeviceError();
+
+ SessionStatsBuilder() : mRequestCount(0), mErrorResultCount(0),
+ mCounterStopped(false), mDeviceError(false) {}
+private:
+ std::mutex mLock;
+ int64_t mRequestCount;
+ int64_t mErrorResultCount;
+ bool mCounterStopped;
+ bool mDeviceError;
+ // Map from stream id to stream statistics
+ std::map<int, StreamStats> mStatsMap;
+};
+
+}; // namespace android
+
+#endif // ANDROID_SERVICE_UTILS_SESSION_STATS_BUILDER_H
diff --git a/services/mediacodec/Android.bp b/services/mediacodec/Android.bp
index 6788afb..bf4d524 100644
--- a/services/mediacodec/Android.bp
+++ b/services/mediacodec/Android.bp
@@ -36,8 +36,6 @@
"libmedia_headers",
],
- init_rc: ["mediaswcodec.rc"],
-
cflags: [
"-Werror",
"-Wall",
diff --git a/services/mediacodec/mediaswcodec.rc b/services/mediacodec/mediaswcodec.rc
deleted file mode 100644
index 3549666..0000000
--- a/services/mediacodec/mediaswcodec.rc
+++ /dev/null
@@ -1,7 +0,0 @@
-service media.swcodec /system/bin/mediaswcodec
- class main
- user mediacodec
- group camera drmrpc mediadrm
- updatable
- ioprio rt 4
- writepid /dev/cpuset/foreground/tasks
diff --git a/services/mediacodec/registrant/CodecServiceRegistrant.cpp b/services/mediacodec/registrant/CodecServiceRegistrant.cpp
index 184251a..b479433 100644
--- a/services/mediacodec/registrant/CodecServiceRegistrant.cpp
+++ b/services/mediacodec/registrant/CodecServiceRegistrant.cpp
@@ -25,8 +25,9 @@
#include <C2PlatformSupport.h>
#include <codec2/hidl/1.0/ComponentStore.h>
#include <codec2/hidl/1.1/ComponentStore.h>
-#include <codec2/hidl/1.1/Configurable.h>
-#include <codec2/hidl/1.1/types.h>
+#include <codec2/hidl/1.2/ComponentStore.h>
+#include <codec2/hidl/1.2/Configurable.h>
+#include <codec2/hidl/1.2/types.h>
#include <hidl/HidlSupport.h>
#include <media/CodecServiceRegistrant.h>
@@ -37,8 +38,8 @@
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::sp;
-using namespace ::android::hardware::media::c2::V1_1;
-using namespace ::android::hardware::media::c2::V1_1::utils;
+using namespace ::android::hardware::media::c2::V1_2;
+using namespace ::android::hardware::media::c2::V1_2::utils;
constexpr c2_status_t C2_TRANSACTION_FAILED = C2_CORRUPTED;
@@ -420,11 +421,20 @@
// STOPSHIP: Remove code name checking once platform version bumps up to 30.
std::string codeName =
android::base::GetProperty("ro.build.version.codename", "");
- if (codeName == "R") {
- platformVersion = 30;
+ if (codeName == "S") {
+ platformVersion = 31;
}
switch (platformVersion) {
+ case 31: {
+ android::sp<V1_2::IComponentStore> storeV1_2 =
+ new V1_2::utils::ComponentStore(store);
+ if (storeV1_2->registerAsService("software") != android::OK) {
+ LOG(ERROR) << "Cannot register software Codec2 v1.2 service.";
+ return;
+ }
+ break;
+ }
case 30: {
android::sp<V1_1::IComponentStore> storeV1_1 =
new V1_1::utils::ComponentStore(store);
diff --git a/services/mediametrics/Android.bp b/services/mediametrics/Android.bp
index f13ca74..5989181 100644
--- a/services/mediametrics/Android.bp
+++ b/services/mediametrics/Android.bp
@@ -113,6 +113,7 @@
"libmediametricsservice",
"libmediautils",
"libutils",
+ "mediametricsservice-aidl-cpp",
],
header_libs: [
"libaudioutils_headers",
@@ -155,6 +156,7 @@
},
shared_libs: [
+ "mediametricsservice-aidl-cpp",
"libbase", // android logging
"libbinder",
"libcutils",
@@ -164,10 +166,18 @@
"libmediautils",
"libmemunreachable",
"libprotobuf-cpp-lite",
+ "libstagefright_foundation",
"libstatslog",
+ "libstatspull",
+ "libstatssocket",
"libutils",
],
+ export_shared_lib_headers: [
+ "libstatspull",
+ "libstatssocket",
+ ],
+
static_libs: [
"libplatformprotos",
],
diff --git a/services/mediametrics/AudioAnalytics.cpp b/services/mediametrics/AudioAnalytics.cpp
index 2a52599..11ec993 100644
--- a/services/mediametrics/AudioAnalytics.cpp
+++ b/services/mediametrics/AudioAnalytics.cpp
@@ -87,6 +87,7 @@
"selected_device_id",
"caller",
"source",
+ "log_session_id",
};
static constexpr const char * const AudioThreadDeviceUsageFields[] = {
@@ -124,6 +125,7 @@
"content_type",
"caller",
"traits",
+ "log_session_id",
};
static constexpr const char * const AudioDeviceConnectionFields[] = {
@@ -136,6 +138,56 @@
"connection_count",
};
+static constexpr const char * const AAudioStreamFields[] {
+ "mediametrics_aaudiostream_reported",
+ "path",
+ "direction",
+ "frames_per_burst",
+ "buffer_size",
+ "buffer_capacity",
+ "channel_count",
+ "total_frames_transferred",
+ "perf_mode_requested",
+ "perf_mode_actual",
+ "sharing",
+ "xrun_count",
+ "device_type",
+ "format_app",
+ "format_device",
+ "log_session_id",
+ "sample_rate",
+ "content_type",
+ "sharing_requested",
+};
+
+/**
+ * printFields is a helper method that prints the fields and corresponding values
+ * in a human readable style.
+ */
+template <size_t N, typename ...Types>
+std::string printFields(const char * const (& fields)[N], Types ... args)
+{
+ std::stringstream ss;
+ ss << " { ";
+ stringutils::fieldPrint(ss, fields, args...);
+ ss << "}";
+ return ss.str();
+}
+
+/**
+ * sendToStatsd is a helper method that sends the arguments to statsd
+ */
+template <typename ...Types>
+int sendToStatsd(Types ... args)
+{
+ int result = 0;
+
+#ifdef STATSD_ENABLE
+ result = android::util::stats_write(args...);
+#endif
+ return result;
+}
+
/**
* sendToStatsd is a helper method that sends the arguments to statsd
* and returns a pair { result, summary_string }.
@@ -156,8 +208,10 @@
return { result, ss.str() };
}
-AudioAnalytics::AudioAnalytics()
+AudioAnalytics::AudioAnalytics(const std::shared_ptr<StatsdLog>& statsdLog)
: mDeliverStatistics(property_get_bool(PROP_AUDIO_ANALYTICS_CLOUD_ENABLED, true))
+ , mStatsdLog(statsdLog)
+ , mAudioPowerUsage(this, statsdLog)
{
SetMinimumLogSeverity(android::base::DEBUG); // for LOG().
ALOGD("%s", __func__);
@@ -192,6 +246,33 @@
});
}));
+ // Handle legacy aaudio playback stream statistics
+ mActions.addAction(
+ AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK "*." AMEDIAMETRICS_PROP_EVENT,
+ std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAAUDIOSTREAM),
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &item) {
+ mAAudioStreamInfo.endAAudioStream(item, AAudioStreamInfo::CALLER_PATH_LEGACY);
+ }));
+
+ // Handle legacy aaudio capture stream statistics
+ mActions.addAction(
+ AMEDIAMETRICS_KEY_PREFIX_AUDIO_RECORD "*." AMEDIAMETRICS_PROP_EVENT,
+ std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAAUDIOSTREAM),
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &item) {
+ mAAudioStreamInfo.endAAudioStream(item, AAudioStreamInfo::CALLER_PATH_LEGACY);
+ }));
+
+ // Handle mmap aaudio stream statistics
+ mActions.addAction(
+ AMEDIAMETRICS_KEY_PREFIX_AUDIO_STREAM "*." AMEDIAMETRICS_PROP_EVENT,
+ std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAAUDIOSTREAM),
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &item) {
+ mAAudioStreamInfo.endAAudioStream(item, AAudioStreamInfo::CALLER_PATH_MMAP);
+ }));
+
// Handle device use record statistics
mActions.addAction(
AMEDIAMETRICS_KEY_PREFIX_AUDIO_RECORD "*." AMEDIAMETRICS_PROP_EVENT,
@@ -338,20 +419,6 @@
ll -= l;
}
- if (ll > 0) {
- // Print the statsd atoms we sent out.
- const std::string statsd = mStatsdLog.dumpToString(" " /* prefix */, ll - 1);
- const size_t n = std::count(statsd.begin(), statsd.end(), '\n') + 1; // we control this.
- if ((size_t)ll >= n) {
- if (n == 1) {
- ss << "Statsd atoms: empty or truncated\n";
- } else {
- ss << "Statsd atoms:\n" << statsd;
- }
- ll -= (int32_t)n;
- }
- }
-
if (ll > 0 && prefix == nullptr) {
auto [s, l] = mAudioPowerUsage.dump(ll);
ss << s;
@@ -484,12 +551,18 @@
std::string source;
mAudioAnalytics.mAnalyticsState->timeMachine().get(
key, AMEDIAMETRICS_PROP_SOURCE, &source);
+ // Android S
+ std::string logSessionId;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_LOGSESSIONID, &logSessionId);
const auto callerNameForStats =
types::lookup<types::CALLER_NAME, short_enum_type_t>(callerName);
const auto encodingForStats = types::lookup<types::ENCODING, short_enum_type_t>(encoding);
const auto flagsForStats = types::lookup<types::INPUT_FLAG, short_enum_type_t>(flags);
const auto sourceForStats = types::lookup<types::SOURCE_TYPE, short_enum_type_t>(source);
+ // Android S
+ const auto logSessionIdForStats = stringutils::sanitizeLogSessionId(logSessionId);
LOG(LOG_LEVEL) << "key:" << key
<< " id:" << id
@@ -504,7 +577,9 @@
<< ") packageName:" << packageName
<< " selectedDeviceId:" << selectedDeviceId
<< " callerName:" << callerName << "(" << callerNameForStats
- << ") source:" << source << "(" << sourceForStats << ")";
+ << ") source:" << source << "(" << sourceForStats
+ << ") logSessionId:" << logSessionId << "(" << logSessionIdForStats
+ << ")";
if (clientCalled // only log if client app called AudioRecord.
&& mAudioAnalytics.mDeliverStatistics) {
const auto [ result, str ] = sendToStatsd(AudioRecordDeviceUsageFields,
@@ -522,9 +597,11 @@
, selectedDeviceId
, ENUM_EXTRACT(callerNameForStats)
, ENUM_EXTRACT(sourceForStats)
+ , logSessionIdForStats.c_str()
);
ALOGV("%s: statsd %s", __func__, str.c_str());
- mAudioAnalytics.mStatsdLog.log("%s", str.c_str());
+ mAudioAnalytics.mStatsdLog->log(
+ android::util::MEDIAMETRICS_AUDIORECORDDEVICEUSAGE_REPORTED, str);
}
} break;
case THREAD: {
@@ -572,7 +649,8 @@
, ENUM_EXTRACT(typeForStats)
);
ALOGV("%s: statsd %s", __func__, str.c_str());
- mAudioAnalytics.mStatsdLog.log("%s", str.c_str());
+ mAudioAnalytics.mStatsdLog->log(
+ android::util::MEDIAMETRICS_AUDIOTHREADDEVICEUSAGE_REPORTED, str);
}
} break;
case TRACK: {
@@ -622,6 +700,10 @@
std::string usage;
mAudioAnalytics.mAnalyticsState->timeMachine().get(
key, AMEDIAMETRICS_PROP_USAGE, &usage);
+ // Android S
+ std::string logSessionId;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_LOGSESSIONID, &logSessionId);
const auto callerNameForStats =
types::lookup<types::CALLER_NAME, short_enum_type_t>(callerName);
@@ -634,6 +716,8 @@
const auto traitsForStats =
types::lookup<types::TRACK_TRAITS, short_enum_type_t>(traits);
const auto usageForStats = types::lookup<types::USAGE, short_enum_type_t>(usage);
+ // Android S
+ const auto logSessionIdForStats = stringutils::sanitizeLogSessionId(logSessionId);
LOG(LOG_LEVEL) << "key:" << key
<< " id:" << id
@@ -658,6 +742,7 @@
<< " streamType:" << streamType << "(" << streamTypeForStats
<< ") traits:" << traits << "(" << traitsForStats
<< ") usage:" << usage << "(" << usageForStats
+ << ") logSessionId:" << logSessionId << "(" << logSessionIdForStats
<< ")";
if (clientCalled // only log if client app called AudioTracks
&& mAudioAnalytics.mDeliverStatistics) {
@@ -682,9 +767,11 @@
, ENUM_EXTRACT(contentTypeForStats)
, ENUM_EXTRACT(callerNameForStats)
, ENUM_EXTRACT(traitsForStats)
+ , logSessionIdForStats.c_str()
);
ALOGV("%s: statsd %s", __func__, str.c_str());
- mAudioAnalytics.mStatsdLog.log("%s", str.c_str());
+ mAudioAnalytics.mStatsdLog->log(
+ android::util::MEDIAMETRICS_AUDIOTRACKDEVICEUSAGE_REPORTED, str);
}
} break;
}
@@ -760,7 +847,8 @@
, /* connection_count */ 1
);
ALOGV("%s: statsd %s", __func__, str.c_str());
- mAudioAnalytics.mStatsdLog.log("%s", str.c_str());
+ mAudioAnalytics.mStatsdLog->log(
+ android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED, str);
}
}
}
@@ -813,7 +901,8 @@
, /* connection_count */ 1
);
ALOGV("%s: statsd %s", __func__, str.c_str());
- mAudioAnalytics.mStatsdLog.log("%s", str.c_str());
+ mAudioAnalytics.mStatsdLog->log(
+ android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED, str);
}
return;
}
@@ -839,7 +928,166 @@
, /* connection_count */ 1
);
ALOGV("%s: statsd %s", __func__, str.c_str());
- mAudioAnalytics.mStatsdLog.log("%s", str.c_str());
+ mAudioAnalytics.mStatsdLog->log(
+ android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED, str);
+ }
+}
+
+void AudioAnalytics::AAudioStreamInfo::endAAudioStream(
+ const std::shared_ptr<const android::mediametrics::Item> &item, CallerPath path) const {
+ const std::string& key = item->getKey();
+
+ std::string directionStr;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_DIRECTION, &directionStr);
+ const auto direction = types::lookup<types::AAUDIO_DIRECTION, int32_t>(directionStr);
+
+ int32_t framesPerBurst = -1;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_BURSTFRAMES, &framesPerBurst);
+
+ int32_t bufferSizeInFrames = -1;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, &bufferSizeInFrames);
+
+ int32_t bufferCapacityInFrames = -1;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_BUFFERCAPACITYFRAMES, &bufferCapacityInFrames);
+
+ int32_t channelCount = -1;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_CHANNELCOUNT, &channelCount);
+
+ int64_t totalFramesTransferred = -1;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_FRAMESTRANSFERRED, &totalFramesTransferred);
+
+ std::string perfModeRequestedStr;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_PERFORMANCEMODE, &perfModeRequestedStr);
+ const auto perfModeRequested =
+ types::lookup<types::AAUDIO_PERFORMANCE_MODE, int32_t>(perfModeRequestedStr);
+
+ std::string perfModeActualStr;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_PERFORMANCEMODEACTUAL, &perfModeActualStr);
+ const auto perfModeActual =
+ types::lookup<types::AAUDIO_PERFORMANCE_MODE, int32_t>(perfModeActualStr);
+
+ std::string sharingModeActualStr;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_SHARINGMODEACTUAL, &sharingModeActualStr);
+ const auto sharingModeActual =
+ types::lookup<types::AAUDIO_SHARING_MODE, int32_t>(sharingModeActualStr);
+
+ int32_t xrunCount = -1;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_UNDERRUN, &xrunCount);
+
+ std::string serializedDeviceTypes;
+ // TODO: only routed device id is logged, but no device type
+
+ std::string formatAppStr;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_ENCODINGCLIENT, &formatAppStr);
+ const auto formatApp = types::lookup<types::ENCODING, int32_t>(formatAppStr);
+
+ std::string formatDeviceStr;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_ENCODING, &formatDeviceStr);
+ const auto formatDevice = types::lookup<types::ENCODING, int32_t>(formatDeviceStr);
+
+ std::string logSessionId;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_LOGSESSIONID, &logSessionId);
+
+ int32_t sampleRate = 0;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_SAMPLERATE, &sampleRate);
+
+ std::string contentTypeStr;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_CONTENTTYPE, &contentTypeStr);
+ const auto contentType = types::lookup<types::CONTENT_TYPE, int32_t>(contentTypeStr);
+
+ std::string sharingModeRequestedStr;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_SHARINGMODE, &sharingModeRequestedStr);
+ const auto sharingModeRequested =
+ types::lookup<types::AAUDIO_SHARING_MODE, int32_t>(sharingModeRequestedStr);
+
+ LOG(LOG_LEVEL) << "key:" << key
+ << " path:" << path
+ << " direction:" << direction << "(" << directionStr << ")"
+ << " frames_per_burst:" << framesPerBurst
+ << " buffer_size:" << bufferSizeInFrames
+ << " buffer_capacity:" << bufferCapacityInFrames
+ << " channel_count:" << channelCount
+ << " total_frames_transferred:" << totalFramesTransferred
+ << " perf_mode_requested:" << perfModeRequested << "(" << perfModeRequestedStr << ")"
+ << " perf_mode_actual:" << perfModeActual << "(" << perfModeActualStr << ")"
+ << " sharing:" << sharingModeActual << "(" << sharingModeActualStr << ")"
+ << " xrun_count:" << xrunCount
+ << " device_type:" << serializedDeviceTypes
+ << " format_app:" << formatApp << "(" << formatAppStr << ")"
+ << " format_device: " << formatDevice << "(" << formatDeviceStr << ")"
+ << " log_session_id: " << logSessionId
+ << " sample_rate: " << sampleRate
+ << " content_type: " << contentType << "(" << contentTypeStr << ")"
+ << " sharing_requested:" << sharingModeRequested
+ << "(" << sharingModeRequestedStr << ")";
+
+ if (mAudioAnalytics.mDeliverStatistics) {
+ android::util::BytesField bf_serialized(
+ serializedDeviceTypes.c_str(), serializedDeviceTypes.size());
+ const auto result = sendToStatsd(
+ CONDITION(android::util::MEDIAMETRICS_AAUDIOSTREAM_REPORTED)
+ , path
+ , direction
+ , framesPerBurst
+ , bufferSizeInFrames
+ , bufferCapacityInFrames
+ , channelCount
+ , totalFramesTransferred
+ , perfModeRequested
+ , perfModeActual
+ , sharingModeActual
+ , xrunCount
+ , bf_serialized
+ , formatApp
+ , formatDevice
+ , logSessionId.c_str()
+ , sampleRate
+ , contentType
+ , sharingModeRequested
+ );
+ std::stringstream ss;
+ ss << "result:" << result;
+ const auto fieldsStr = printFields(AAudioStreamFields,
+ CONDITION(android::util::MEDIAMETRICS_AAUDIOSTREAM_REPORTED)
+ , path
+ , direction
+ , framesPerBurst
+ , bufferSizeInFrames
+ , bufferCapacityInFrames
+ , channelCount
+ , totalFramesTransferred
+ , perfModeRequested
+ , perfModeActual
+ , sharingModeActual
+ , xrunCount
+ , serializedDeviceTypes.c_str()
+ , formatApp
+ , formatDevice
+ , logSessionId.c_str()
+ , sampleRate
+ , contentType
+ , sharingModeRequested
+ );
+ ss << " " << fieldsStr;
+ std::string str = ss.str();
+ ALOGV("%s: statsd %s", __func__, str.c_str());
+ mAudioAnalytics.mStatsdLog->log(android::util::MEDIAMETRICS_AAUDIOSTREAM_REPORTED, str);
}
}
diff --git a/services/mediametrics/AudioAnalytics.h b/services/mediametrics/AudioAnalytics.h
index df097b1..2b41a95 100644
--- a/services/mediametrics/AudioAnalytics.h
+++ b/services/mediametrics/AudioAnalytics.h
@@ -17,10 +17,10 @@
#pragma once
#include <android-base/thread_annotations.h>
-#include <audio_utils/SimpleLog.h>
#include "AnalyticsActions.h"
#include "AnalyticsState.h"
#include "AudioPowerUsage.h"
+#include "StatsdLog.h"
#include "TimedAction.h"
#include "Wrap.h"
@@ -32,7 +32,7 @@
friend AudioPowerUsage;
public:
- AudioAnalytics();
+ explicit AudioAnalytics(const std::shared_ptr<StatsdLog>& statsdLog);
~AudioAnalytics();
/**
@@ -122,8 +122,7 @@
SharedPtrWrap<AnalyticsState> mPreviousAnalyticsState;
TimedAction mTimedAction; // locked internally
-
- SimpleLog mStatsdLog{16 /* log lines */}; // locked internally
+ const std::shared_ptr<StatsdLog> mStatsdLog; // locked internally, ok for multiple threads.
// DeviceUse is a nested class which handles audio device usage accounting.
// We define this class at the end to ensure prior variables all properly constructed.
@@ -189,7 +188,30 @@
int32_t mA2dpConnectionUnknowns GUARDED_BY(mLock) = 0;
} mDeviceConnection{*this};
- AudioPowerUsage mAudioPowerUsage{this};
+ // AAudioStreamInfo is a nested class which collect aaudio stream info from both client and
+ // server side.
+ class AAudioStreamInfo {
+ public:
+ // All the enum here must be kept the same as the ones defined in atoms.proto
+ enum CallerPath {
+ CALLER_PATH_UNKNOWN = 0,
+ CALLER_PATH_LEGACY = 1,
+ CALLER_PATH_MMAP = 2,
+ };
+
+ explicit AAudioStreamInfo(AudioAnalytics &audioAnalytics)
+ : mAudioAnalytics(audioAnalytics) {}
+
+ void endAAudioStream(
+ const std::shared_ptr<const android::mediametrics::Item> &item,
+ CallerPath path) const;
+
+ private:
+
+ AudioAnalytics &mAudioAnalytics;
+ } mAAudioStreamInfo{*this};
+
+ AudioPowerUsage mAudioPowerUsage;
};
} // namespace android::mediametrics
diff --git a/services/mediametrics/AudioPowerUsage.cpp b/services/mediametrics/AudioPowerUsage.cpp
index e584f12..ab74c8e 100644
--- a/services/mediametrics/AudioPowerUsage.cpp
+++ b/services/mediametrics/AudioPowerUsage.cpp
@@ -127,14 +127,13 @@
return deviceMask;
}
-/* static */
-void AudioPowerUsage::sendItem(const std::shared_ptr<const mediametrics::Item>& item)
+void AudioPowerUsage::sendItem(const std::shared_ptr<const mediametrics::Item>& item) const
{
int32_t type;
if (!item->getInt32(AUDIO_POWER_USAGE_PROP_TYPE, &type)) return;
- int32_t device;
- if (!item->getInt32(AUDIO_POWER_USAGE_PROP_DEVICE, &device)) return;
+ int32_t audio_device;
+ if (!item->getInt32(AUDIO_POWER_USAGE_PROP_DEVICE, &audio_device)) return;
int64_t duration_ns;
if (!item->getInt64(AUDIO_POWER_USAGE_PROP_DURATION_NS, &duration_ns)) return;
@@ -142,11 +141,24 @@
double volume;
if (!item->getDouble(AUDIO_POWER_USAGE_PROP_VOLUME, &volume)) return;
- (void)android::util::stats_write(android::util::AUDIO_POWER_USAGE_DATA_REPORTED,
- device,
- (int32_t)(duration_ns / NANOS_PER_SECOND),
- (float)volume,
+ const int32_t duration_secs = (int32_t)(duration_ns / NANOS_PER_SECOND);
+ const float average_volume = (float)volume;
+ const int result = android::util::stats_write(android::util::AUDIO_POWER_USAGE_DATA_REPORTED,
+ audio_device,
+ duration_secs,
+ average_volume,
type);
+
+ std::stringstream log;
+ log << "result:" << result << " {"
+ << " mediametrics_audio_power_usage_data_reported:"
+ << android::util::AUDIO_POWER_USAGE_DATA_REPORTED
+ << " audio_device:" << audio_device
+ << " duration_secs:" << duration_secs
+ << " average_volume:" << average_volume
+ << " type:" << type
+ << " }";
+ mStatsdLog->log(android::util::AUDIO_POWER_USAGE_DATA_REPORTED, log.str());
}
bool AudioPowerUsage::saveAsItem_l(
@@ -360,8 +372,10 @@
mPrimaryDevice = device;
}
-AudioPowerUsage::AudioPowerUsage(AudioAnalytics *audioAnalytics)
+AudioPowerUsage::AudioPowerUsage(
+ AudioAnalytics *audioAnalytics, const std::shared_ptr<StatsdLog>& statsdLog)
: mAudioAnalytics(audioAnalytics)
+ , mStatsdLog(statsdLog)
, mDisabled(property_get_bool(PROP_AUDIO_METRICS_DISABLED, AUDIO_METRICS_DISABLED_DEFAULT))
, mIntervalHours(property_get_int32(PROP_AUDIO_METRICS_INTERVAL_HR, INTERVAL_HR_DEFAULT))
{
diff --git a/services/mediametrics/AudioPowerUsage.h b/services/mediametrics/AudioPowerUsage.h
index b705a6a..7021902 100644
--- a/services/mediametrics/AudioPowerUsage.h
+++ b/services/mediametrics/AudioPowerUsage.h
@@ -22,13 +22,15 @@
#include <mutex>
#include <thread>
+#include "StatsdLog.h"
+
namespace android::mediametrics {
class AudioAnalytics;
class AudioPowerUsage {
public:
- explicit AudioPowerUsage(AudioAnalytics *audioAnalytics);
+ AudioPowerUsage(AudioAnalytics *audioAnalytics, const std::shared_ptr<StatsdLog>& statsdLog);
~AudioPowerUsage();
void checkTrackRecord(const std::shared_ptr<const mediametrics::Item>& item, bool isTrack);
@@ -83,12 +85,13 @@
private:
bool saveAsItem_l(int32_t device, int64_t duration, int32_t type, double average_vol)
REQUIRES(mLock);
- static void sendItem(const std::shared_ptr<const mediametrics::Item>& item);
+ void sendItem(const std::shared_ptr<const mediametrics::Item>& item) const;
void collect();
bool saveAsItems_l(int32_t device, int64_t duration, int32_t type, double average_vol)
REQUIRES(mLock);
AudioAnalytics * const mAudioAnalytics;
+ const std::shared_ptr<StatsdLog> mStatsdLog; // mStatsdLog is internally locked
const bool mDisabled;
const int32_t mIntervalHours;
diff --git a/services/mediametrics/AudioTypes.cpp b/services/mediametrics/AudioTypes.cpp
index aa44447..838cdd5 100644
--- a/services/mediametrics/AudioTypes.cpp
+++ b/services/mediametrics/AudioTypes.cpp
@@ -76,6 +76,8 @@
{"AUDIO_DEVICE_IN_ECHO_REFERENCE", 1LL << 27},
{"AUDIO_DEVICE_IN_DEFAULT", 1LL << 28},
// R values above.
+ {"AUDIO_DEVICE_IN_BLE_HEADSET", 1LL << 29},
+ {"AUDIO_DEVICE_IN_HDMI_EARC", 1LL << 30},
};
return map;
}
@@ -121,6 +123,9 @@
{"AUDIO_DEVICE_OUT_ECHO_CANCELLER", 1LL << 29},
{"AUDIO_DEVICE_OUT_DEFAULT", 1LL << 30},
// R values above.
+ {"AUDIO_DEVICE_OUT_BLE_HEADSET", 1LL << 31},
+ {"AUDIO_DEVICE_OUT_BLE_SPEAKER", 1LL << 32},
+ {"AUDIO_DEVICE_OUT_HDMI_EARC", 1LL << 33},
};
return map;
}
@@ -151,6 +156,40 @@
return map;
}
+const std::unordered_map<std::string, int32_t>& getAAudioDirection() {
+ // DO NOT MODIFY VALUES(OK to add new ones).
+ // This may be found in frameworks/av/media/libaaudio/include/aaudio/AAudio.h
+ static std::unordered_map<std::string, int32_t> map {
+ // UNKNOWN is 0
+ {"AAUDIO_DIRECTION_OUTPUT", 1 /* AAUDIO_DIRECTION_OUTPUT + 1 */},
+ {"AAUDIO_DIRECTION_INPUT", 2 /* AAUDIO_DIRECTION_INPUT + 1*/},
+ };
+ return map;
+}
+
+const std::unordered_map<std::string, int32_t>& getAAudioPerformanceMode() {
+ // DO NOT MODIFY VALUES(OK to add new ones).
+ // This may be found in frameworks/av/media/libaaudio/include/aaudio/AAudio.h
+ static std::unordered_map<std::string, int32_t> map {
+ // UNKNOWN is 0
+ {"AAUDIO_PERFORMANCE_MODE_NONE", 10},
+ {"AAUDIO_PERFORMANCE_MODE_POWER_SAVING", 11},
+ {"AAUDIO_PERFORMANCE_MODE_LOW_LATENCY", 12},
+ };
+ return map;
+}
+
+const std::unordered_map<std::string, int32_t>& getAAudioSharingMode() {
+ // DO NOT MODIFY VALUES(OK to add new ones).
+ // This may be found in frameworks/av/media/libaaudio/include/aaudio/AAudio.h
+ static std::unordered_map<std::string, int32_t> map {
+ // UNKNOWN is 0
+ {"AAUDIO_SHARING_MODE_EXCLUSIVE", 1 /* AAUDIO_SHARING_MODE_EXCLUSIVE + 1 */},
+ {"AAUDIO_SHARING_MODE_SHARED", 2 /* AAUDIO_SHARING_MODE_SHARED + 1 */},
+ };
+ return map;
+}
+
// Helper: Create the corresponding int32 from string flags split with '|'.
template <typename Traits>
int32_t int32FromFlags(const std::string &flags)
@@ -430,4 +469,70 @@
return flagsFromMap(traits, getAudioTrackTraitsMap());
}
+template <>
+std::string lookup<AAUDIO_DIRECTION>(const std::string &direction)
+{
+ auto& map = getAAudioDirection();
+ auto it = map.find(direction);
+ if (it == map.end()) {
+ return "";
+ }
+ return direction;
+}
+
+template <>
+int32_t lookup<AAUDIO_DIRECTION>(const std::string &direction)
+{
+ auto& map = getAAudioDirection();
+ auto it = map.find(direction);
+ if (it == map.end()) {
+ return 0; // return unknown
+ }
+ return it->second;
+}
+
+template <>
+std::string lookup<AAUDIO_PERFORMANCE_MODE>(const std::string &performanceMode)
+{
+ auto& map = getAAudioPerformanceMode();
+ auto it = map.find(performanceMode);
+ if (it == map.end()) {
+ return "";
+ }
+ return performanceMode;
+}
+
+template <>
+int32_t lookup<AAUDIO_PERFORMANCE_MODE>(const std::string &performanceMode)
+{
+ auto& map = getAAudioPerformanceMode();
+ auto it = map.find(performanceMode);
+ if (it == map.end()) {
+ return 0; // return unknown
+ }
+ return it->second;
+}
+
+template <>
+std::string lookup<AAUDIO_SHARING_MODE>(const std::string &sharingMode)
+{
+ auto& map = getAAudioSharingMode();
+ auto it = map.find(sharingMode);
+ if (it == map.end()) {
+ return "";
+ }
+ return sharingMode;
+}
+
+template <>
+int32_t lookup<AAUDIO_SHARING_MODE>(const std::string &sharingMode)
+{
+ auto& map = getAAudioSharingMode();
+ auto it = map.find(sharingMode);
+ if (it == map.end()) {
+ return 0; // return unknown
+ }
+ return it->second;
+}
+
} // namespace android::mediametrics::types
diff --git a/services/mediametrics/AudioTypes.h b/services/mediametrics/AudioTypes.h
index e1deeb1..4394d79 100644
--- a/services/mediametrics/AudioTypes.h
+++ b/services/mediametrics/AudioTypes.h
@@ -40,6 +40,9 @@
// Enumeration for all the string translations to integers (generally int32_t) unless noted.
enum AudioEnumCategory {
+ AAUDIO_DIRECTION,
+ AAUDIO_PERFORMANCE_MODE,
+ AAUDIO_SHARING_MODE,
CALLER_NAME,
CONTENT_TYPE,
ENCODING,
diff --git a/services/mediametrics/MediaMetricsService.cpp b/services/mediametrics/MediaMetricsService.cpp
index 0e89054..1d64878 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -19,19 +19,26 @@
#include <utils/Log.h>
#include "MediaMetricsService.h"
+#include "iface_statsd.h"
#include <pwd.h> //getpwuid
+#include <android-base/stringprintf.h>
#include <android/content/pm/IPackageManagerNative.h> // package info
#include <audio_utils/clock.h> // clock conversions
#include <binder/IPCThreadState.h> // get calling uid
+#include <binder/IServiceManager.h> // checkCallingPermission
#include <cutils/properties.h> // for property_get
#include <mediautils/MemoryLeakTrackUtil.h>
#include <memunreachable/memunreachable.h>
#include <private/android_filesystem_config.h> // UID
+#include <statslog.h>
+
+#include <set>
namespace android {
+using base::StringPrintf;
using mediametrics::Item;
using mediametrics::startsWith;
@@ -199,22 +206,19 @@
(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.
+ (void)dump2Statsd(sitem, mStatsdLog); // 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: "
+ const std::string result = StringPrintf("Permission Denial: "
"can't dump MediaMetricsService from pid=%d, uid=%d\n",
IPCThreadState::self()->getCallingPid(),
IPCThreadState::self()->getCallingUid());
- write(fd, result.string(), result.size());
+ write(fd, result.c_str(), result.size());
return NO_ERROR;
}
@@ -246,17 +250,18 @@
// dumpsys media.metrics audiotrack,codec
// or dumpsys media.metrics audiotrack codec
- result.append("Recognized parameters:\n");
- result.append("--all show all records\n");
- result.append("--clear clear out saved records\n");
- result.append("--heap show heap usage (top 100)\n");
- result.append("--help display help\n");
- result.append("--prefix X process records for component X\n");
- result.append("--since X X < 0: records from -X seconds in the past\n");
- result.append(" X = 0: ignore\n");
- result.append(" X > 0: records from X seconds since Unix epoch\n");
- result.append("--unreachable show unreachable memory (leaks)\n");
- write(fd, result.string(), result.size());
+ static constexpr char result[] =
+ "Recognized parameters:\n"
+ "--all show all records\n"
+ "--clear clear out saved records\n"
+ "--heap show heap usage (top 100)\n"
+ "--help display help\n"
+ "--prefix X process records for component X\n"
+ "--since X X < 0: records from -X seconds in the past\n"
+ " X = 0: ignore\n"
+ " X > 0: records from X seconds since Unix epoch\n"
+ "--unreachable show unreachable memory (leaks)\n";
+ write(fd, result, std::size(result));
return NO_ERROR;
} else if (args[i] == prefixOption) {
++i;
@@ -282,7 +287,7 @@
unreachable = true;
}
}
-
+ std::stringstream result;
{
std::lock_guard _l(mLock);
@@ -291,21 +296,27 @@
mItems.clear();
mAudioAnalytics.clear();
} else {
- result.appendFormat("Dump of the %s process:\n", kServiceName);
+ result << StringPrintf("Dump of the %s process:\n", kServiceName);
const char *prefixptr = prefix.size() > 0 ? prefix.c_str() : nullptr;
- dumpHeaders(result, sinceNs, prefixptr);
- dumpQueue(result, sinceNs, prefixptr);
+ result << dumpHeaders(sinceNs, prefixptr);
+ result << dumpQueue(sinceNs, prefixptr);
// TODO: maybe consider a better way of dumping audio analytics info.
const int32_t linesToDump = all ? INT32_MAX : 1000;
auto [ dumpString, lines ] = mAudioAnalytics.dump(linesToDump, sinceNs, prefixptr);
- result.append(dumpString.c_str());
+ result << dumpString;
if (lines == linesToDump) {
- result.append("-- some lines may be truncated --\n");
+ result << "-- some lines may be truncated --\n";
}
+
+ // Dump the statsd atoms we sent out.
+ result << "Statsd atoms:\n"
+ << mStatsdLog->dumpToString(" " /* prefix */,
+ all ? STATSD_LOG_LINES_MAX : STATSD_LOG_LINES_DUMP);
}
}
- write(fd, result.string(), result.size());
+ const std::string str = result.str();
+ write(fd, str.c_str(), str.size());
// Check heap and unreachable memory outside of lock.
if (heap) {
@@ -323,38 +334,37 @@
}
// dump headers
-void MediaMetricsService::dumpHeaders(String8 &result, int64_t sinceNs, const char* prefix)
+std::string MediaMetricsService::dumpHeaders(int64_t sinceNs, const char* prefix)
{
+ std::stringstream result;
if (mediametrics::Item::isEnabled()) {
- result.append("Metrics gathering: enabled\n");
+ result << "Metrics gathering: enabled\n";
} else {
- result.append("Metrics gathering: DISABLED via property\n");
+ result << "Metrics gathering: DISABLED via property\n";
}
- result.appendFormat(
+ result << StringPrintf(
"Since Boot: Submissions: %lld Accepted: %lld\n",
(long long)mItemsSubmitted.load(), (long long)mItemsFinalized);
- result.appendFormat(
+ result << StringPrintf(
"Records Discarded: %lld (by Count: %lld by Expiration: %lld)\n",
(long long)mItemsDiscarded, (long long)mItemsDiscardedCount,
(long long)mItemsDiscardedExpire);
if (prefix != nullptr) {
- result.appendFormat("Restricting to prefix %s", prefix);
+ result << "Restricting to prefix " << prefix << "\n";
}
if (sinceNs != 0) {
- result.appendFormat(
- "Emitting Queue entries more recent than: %lld\n",
- (long long)sinceNs);
+ result << "Emitting Queue entries more recent than: " << sinceNs << "\n";
}
+ return result.str();
}
// TODO: should prefix be a set<string>?
-void MediaMetricsService::dumpQueue(String8 &result, int64_t sinceNs, const char* prefix)
+std::string MediaMetricsService::dumpQueue(int64_t sinceNs, const char* prefix)
{
if (mItems.empty()) {
- result.append("empty\n");
- return;
+ return "empty\n";
}
-
+ std::stringstream result;
int slot = 0;
for (const auto &item : mItems) { // TODO: consider std::lower_bound() on mItems
if (item->getTimestamp() < sinceNs) { // sinceNs == 0 means all items shown
@@ -365,9 +375,10 @@
__func__, item->getKey().c_str(), prefix);
continue;
}
- result.appendFormat("%5d: %s\n", slot, item->toString().c_str());
+ result << StringPrintf("%5d: %s\n", slot, item->toString().c_str());
slot++;
}
+ return result.str();
}
//
@@ -439,6 +450,10 @@
std::lock_guard _l(mLock);
// we assume the items are roughly in time order.
mItems.emplace_back(item);
+ if (isPullable(item->getKey())) {
+ registerStatsdCallbacksIfNeeded();
+ mPullableItems[item->getKey()].emplace_back(item);
+ }
++mItemsFinalized;
if (expirations(item)
&& (!mExpireFuture.valid()
@@ -485,4 +500,58 @@
return false;
}
+void MediaMetricsService::registerStatsdCallbacksIfNeeded()
+{
+ if (mStatsdRegistered.test_and_set()) {
+ return;
+ }
+ auto tag = android::util::MEDIA_DRM_ACTIVITY_INFO;
+ auto cb = MediaMetricsService::pullAtomCallback;
+ AStatsManager_setPullAtomCallback(tag, /* metadata */ nullptr, cb, this);
+}
+
+/* static */
+bool MediaMetricsService::isPullable(const std::string &key)
+{
+ static const std::set<std::string> pullableKeys{
+ "mediadrm",
+ };
+ return pullableKeys.count(key);
+}
+
+/* static */
+std::string MediaMetricsService::atomTagToKey(int32_t atomTag)
+{
+ switch (atomTag) {
+ case android::util::MEDIA_DRM_ACTIVITY_INFO:
+ return "mediadrm";
+ }
+ return {};
+}
+
+/* static */
+AStatsManager_PullAtomCallbackReturn MediaMetricsService::pullAtomCallback(
+ int32_t atomTag, AStatsEventList* data, void* cookie)
+{
+ MediaMetricsService* svc = reinterpret_cast<MediaMetricsService*>(cookie);
+ return svc->pullItems(atomTag, data);
+}
+
+AStatsManager_PullAtomCallbackReturn MediaMetricsService::pullItems(
+ int32_t atomTag, AStatsEventList* data)
+{
+ const std::string key(atomTagToKey(atomTag));
+ if (key.empty()) {
+ return AStatsManager_PULL_SKIP;
+ }
+ std::lock_guard _l(mLock);
+ bool dumped = false;
+ for (auto &item : mPullableItems[key]) {
+ if (const auto sitem = item.lock()) {
+ dumped |= dump2Statsd(sitem, data, mStatsdLog);
+ }
+ }
+ mPullableItems[key].clear();
+ return dumped ? AStatsManager_PULL_SUCCESS : AStatsManager_PULL_SKIP;
+}
} // namespace android
diff --git a/services/mediametrics/MediaMetricsService.h b/services/mediametrics/MediaMetricsService.h
index 792b7f0..8d0b1cf 100644
--- a/services/mediametrics/MediaMetricsService.h
+++ b/services/mediametrics/MediaMetricsService.h
@@ -24,20 +24,27 @@
// IMediaMetricsService must include Vector, String16, Errors
#include <android-base/thread_annotations.h>
-#include <media/IMediaMetricsService.h>
+#include <android/media/BnMediaMetricsService.h>
#include <mediautils/ServiceUtilities.h>
+#include <stats_pull_atom_callback.h>
#include <utils/String8.h>
#include "AudioAnalytics.h"
namespace android {
-class MediaMetricsService : public BnMediaMetricsService
+class MediaMetricsService : public media::BnMediaMetricsService
{
public:
MediaMetricsService();
~MediaMetricsService() override;
+ // AIDL interface
+ binder::Status submitBuffer(const std::vector<uint8_t>& buffer) override {
+ status_t status = submitBuffer((char *)buffer.data(), buffer.size());
+ return binder::Status::fromStatusT(status);
+ }
+
/**
* Submits the indicated record to the mediaanalytics service.
*
@@ -45,11 +52,11 @@
* \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 {
+ status_t submit(mediametrics::Item *item) {
return submitInternal(item, false /* release */);
}
- status_t submitBuffer(const char *buffer, size_t length) override {
+ status_t submitBuffer(const char *buffer, size_t length) {
mediametrics::Item *item = new mediametrics::Item();
return item->readFromByteString(buffer, length)
?: submitInternal(item, true /* release */);
@@ -81,7 +88,7 @@
// 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;
+ status_t submitInternal(mediametrics::Item *item, bool release);
private:
void processExpirations();
@@ -93,8 +100,17 @@
bool expirations(const std::shared_ptr<const mediametrics::Item>& item) REQUIRES(mLock);
// support for generating output
- void dumpQueue(String8 &result, int64_t sinceNs, const char* prefix) REQUIRES(mLock);
- void dumpHeaders(String8 &result, int64_t sinceNs, const char* prefix) REQUIRES(mLock);
+ std::string dumpQueue(int64_t sinceNs, const char* prefix) REQUIRES(mLock);
+ std::string dumpHeaders(int64_t sinceNs, const char* prefix) REQUIRES(mLock);
+
+ // support statsd pushed atoms
+ static bool isPullable(const std::string &key);
+ static std::string atomTagToKey(int32_t atomTag);
+ static AStatsManager_PullAtomCallbackReturn pullAtomCallback(
+ int32_t atomTag, AStatsEventList* data, void* cookie);
+ AStatsManager_PullAtomCallbackReturn pullItems(int32_t atomTag, AStatsEventList* data);
+ void registerStatsdCallbacksIfNeeded();
+ std::atomic_flag mStatsdRegistered = ATOMIC_FLAG_INIT;
// The following variables accessed without mLock
@@ -108,7 +124,14 @@
std::atomic<int64_t> mItemsSubmitted{}; // accessed outside of lock.
- mediametrics::AudioAnalytics mAudioAnalytics; // mAudioAnalytics is locked internally.
+ // mStatsdLog is locked internally (thread-safe) and shows the last atoms logged
+ static constexpr size_t STATSD_LOG_LINES_MAX = 30; // recent log lines to keep
+ static constexpr size_t STATSD_LOG_LINES_DUMP = 4; // normal amount of lines to dump
+ const std::shared_ptr<mediametrics::StatsdLog> mStatsdLog{
+ std::make_shared<mediametrics::StatsdLog>(STATSD_LOG_LINES_MAX)};
+
+ // mAudioAnalytics is locked internally.
+ mediametrics::AudioAnalytics mAudioAnalytics{mStatsdLog};
std::mutex mLock;
// statistics about our analytics
@@ -124,6 +147,12 @@
// 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);
+
+ // Queues per item key, pending to be pulled by statsd.
+ // Use weak_ptr such that a pullable item can still expire.
+ using ItemKey = std::string;
+ using WeakItemQueue = std::deque<std::weak_ptr<const mediametrics::Item>>;
+ std::unordered_map<ItemKey, WeakItemQueue> mPullableItems GUARDED_BY(mLock);
};
} // namespace android
diff --git a/services/mediametrics/StatsdLog.h b/services/mediametrics/StatsdLog.h
new file mode 100644
index 0000000..e207bac
--- /dev/null
+++ b/services/mediametrics/StatsdLog.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <audio_utils/SimpleLog.h>
+#include <map>
+#include <mutex>
+#include <sstream>
+
+namespace android::mediametrics {
+
+class StatsdLog {
+public:
+ explicit StatsdLog(size_t lines) : mSimpleLog(lines) {}
+
+ void log(int atom, const std::string& string) {
+ {
+ std::lock_guard lock(mLock);
+ ++mCountMap[atom];
+ }
+ mSimpleLog.log("%s", string.c_str());
+ }
+
+ std::string dumpToString(const char *prefix = "", size_t logLines = 0) const {
+ std::stringstream ss;
+
+ { // first print out the atom counts
+ std::lock_guard lock(mLock);
+
+ size_t col = 0;
+ for (const auto& count : mCountMap) {
+ if (col == 8) {
+ col = 0;
+ ss << "\n" << prefix;
+ } else {
+ ss << " ";
+ }
+ ss << "[ " << count.first << " : " << count.second << " ]";
+ ++col;
+ }
+ ss << "\n";
+ }
+
+ // then print out the log lines
+ ss << mSimpleLog.dumpToString(prefix, logLines);
+ return ss.str();
+ }
+
+private:
+ SimpleLog mSimpleLog; // internally locked
+ std::map<int /* atom */, size_t /* count */> mCountMap GUARDED_BY(mLock); // sorted
+ mutable std::mutex mLock;
+};
+
+} // namespace android::mediametrics
diff --git a/services/mediametrics/StringUtils.h b/services/mediametrics/StringUtils.h
index 7a8bbee..01034d9 100644
--- a/services/mediametrics/StringUtils.h
+++ b/services/mediametrics/StringUtils.h
@@ -16,6 +16,8 @@
#pragma once
+#include <iomanip>
+#include <sstream>
#include <string>
#include <vector>
@@ -68,4 +70,101 @@
*/
size_t replace(std::string &str, const char *targetChars, const char replaceChar);
+// RFC 1421, 2045, 2152, 4648(4), 4880
+inline constexpr char Base64Table[] =
+ // 0000000000111111111122222222223333333333444444444455555555556666
+ // 0123456789012345678901234567890123456789012345678901234567890123
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+// RFC 4648(5) URL-safe Base64 encoding
+inline constexpr char Base64UrlTable[] =
+ // 0000000000111111111122222222223333333333444444444455555555556666
+ // 0123456789012345678901234567890123456789012345678901234567890123
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+
+// An constexpr struct that transposes/inverts a string conversion table.
+struct Transpose {
+ // constexpr bug, returning char still means -1 == 0xff, so we use unsigned char.
+ using base_char_t = unsigned char;
+ static inline constexpr base_char_t INVALID_CHAR = 0xff;
+
+ template <size_t N>
+ explicit constexpr Transpose(const char(&string)[N]) {
+ for (auto& e : mMap) {
+ e = INVALID_CHAR;
+ }
+ for (size_t i = 0; string[i] != 0; ++i) {
+ mMap[static_cast<size_t>(string[i]) & 0xff] = i;
+ }
+ }
+
+ constexpr base_char_t operator[] (size_t n) const {
+ return n < sizeof(mMap) ? mMap[n] : INVALID_CHAR;
+ }
+
+ constexpr const auto& get() const {
+ return mMap;
+ }
+
+private:
+ base_char_t mMap[256]; // construct an inverse character mapping.
+};
+
+// This table is used to convert an input char to a 6 bit (0 - 63) value.
+// If the input char is not in the Base64Url charset, Transpose::INVALID_CHAR is returned.
+inline constexpr Transpose InverseBase64UrlTable(Base64UrlTable);
+
+// Returns true if s consists of only valid Base64Url characters (no padding chars allowed).
+inline constexpr bool isBase64Url(const char *s) {
+ for (; *s != 0; ++s) {
+ if (InverseBase64UrlTable[(unsigned char)*s] == Transpose::INVALID_CHAR) return false;
+ }
+ return true;
+}
+
+// Returns true if s is a valid log session id: exactly 16 Base64Url characters.
+//
+// logSessionIds are a web-safe Base64Url RFC 4648(5) encoded string of 16 characters
+// (representing 96 unique bits 16 * 6).
+//
+// The string version is considered the reference representation. However, for ease of
+// manipulation and comparison, it may be converted to an int128.
+//
+// For int128 conversion, some common interpretations exist - for example
+// (1) the 16 Base64 chars can be converted 6 bits per char to a 96 bit value
+// (with the most significant 32 bits as zero) as there are only 12 unique bytes worth of data
+// or (2) the 16 Base64 chars can be used to directly fill the 128 bits of int128 assuming
+// the 16 chars are 16 bytes, filling the layout of the int128 variable.
+// Endianness of the data may follow whatever is convenient in the interpretation as long
+// as it is applied to each such conversion of string to int128 identically.
+//
+inline constexpr bool isLogSessionId(const char *s) {
+ return std::char_traits<std::decay_t<decltype(*s)>>::length(s) == 16 && isBase64Url(s);
+}
+
+// Returns either the original string or an empty string if isLogSessionId check fails.
+inline std::string sanitizeLogSessionId(const std::string& string) {
+ if (isLogSessionId(string.c_str())) return string;
+ return {}; // if not a logSessionId, return an empty string.
+}
+
+inline std::string bytesToString(const std::vector<uint8_t>& bytes, size_t maxSize = SIZE_MAX) {
+ if (bytes.size() == 0) {
+ return "{}";
+ }
+ std::stringstream ss;
+ ss << "{";
+ ss << std::hex << std::setfill('0');
+ maxSize = std::min(maxSize, bytes.size());
+ for (size_t i = 0; i < maxSize; ++i) {
+ ss << " " << std::setw(2) << (int)bytes[i];
+ }
+ if (maxSize != bytes.size()) {
+ ss << " ... }";
+ } else {
+ ss << " }";
+ }
+ return ss.str();
+}
+
} // namespace android::mediametrics::stringutils
diff --git a/services/mediametrics/fuzzer/Android.bp b/services/mediametrics/fuzzer/Android.bp
index 3053666..b03e518 100644
--- a/services/mediametrics/fuzzer/Android.bp
+++ b/services/mediametrics/fuzzer/Android.bp
@@ -50,8 +50,12 @@
"libmemunreachable",
"libprotobuf-cpp-lite",
"libstagefright",
+ "libstagefright_foundation",
"libstatslog",
+ "libstatspull",
+ "libstatssocket",
"libutils",
+ "mediametricsservice-aidl-cpp",
],
include_dirs: [
diff --git a/services/mediametrics/fuzzer/mediametrics_service_fuzzer.cpp b/services/mediametrics/fuzzer/mediametrics_service_fuzzer.cpp
index 0cb2594..8b0b479 100644
--- a/services/mediametrics/fuzzer/mediametrics_service_fuzzer.cpp
+++ b/services/mediametrics/fuzzer/mediametrics_service_fuzzer.cpp
@@ -320,7 +320,9 @@
void MediaMetricsServiceFuzzer::invokeAudioAnalytics(const uint8_t *data, size_t size) {
FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
- android::mediametrics::AudioAnalytics audioAnalytics;
+ std::shared_ptr<android::mediametrics::StatsdLog> statsdLog =
+ std::make_shared<android::mediametrics::StatsdLog>(10);
+ android::mediametrics::AudioAnalytics audioAnalytics{statsdLog};
while (fdp.remaining_bytes()) {
auto item = std::make_shared<mediametrics::Item>(fdp.ConsumeRandomLengthString().c_str());
diff --git a/services/mediametrics/iface_statsd.cpp b/services/mediametrics/iface_statsd.cpp
index 16204de..776f878 100644
--- a/services/mediametrics/iface_statsd.cpp
+++ b/services/mediametrics/iface_statsd.cpp
@@ -27,7 +27,10 @@
#include <pthread.h>
#include <unistd.h>
+#include <map>
#include <memory>
+#include <string>
+#include <vector>
#include <string.h>
#include <pwd.h>
@@ -45,33 +48,12 @@
// has its own routine to handle this.
//
-bool enabled_statsd = true;
+static bool enabled_statsd = true;
-struct statsd_hooks {
- const char *key;
- bool (*handler)(const mediametrics::Item *);
-};
-
-// keep this sorted, so we can do binary searches
-static constexpr struct statsd_hooks statsd_handlers[] =
-{
- { "audiopolicy", statsd_audiopolicy },
- { "audiorecord", statsd_audiorecord },
- { "audiothread", statsd_audiothread },
- { "audiotrack", statsd_audiotrack },
- { "codec", statsd_codec},
- { "drm.vendor.Google.WidevineCDM", statsd_widevineCDM },
- { "drmmanager", statsd_drmmanager },
- { "extractor", statsd_extractor },
- { "mediadrm", statsd_mediadrm },
- { "mediaparser", statsd_mediaparser },
- { "nuplayer", statsd_nuplayer },
- { "nuplayer2", statsd_nuplayer },
- { "recorder", statsd_recorder },
-};
-
-// give me a record, i'll look at the type and upload appropriately
-bool dump2Statsd(const std::shared_ptr<const mediametrics::Item>& item) {
+namespace {
+template<typename Handler, typename... Args>
+bool dump2StatsdInternal(const std::map<std::string, Handler>& handlers,
+ const std::shared_ptr<const mediametrics::Item>& item, Args... args) {
if (item == nullptr) return false;
// get the key
@@ -82,12 +64,42 @@
return false;
}
- for (const auto &statsd_handler : statsd_handlers) {
- if (key == statsd_handler.key) {
- return statsd_handler.handler(item.get());
- }
+ if (handlers.count(key)) {
+ return (handlers.at(key))(item, args...);
}
return false;
}
+} // namespace
+
+// give me a record, I'll look at the type and upload appropriately
+bool dump2Statsd(
+ const std::shared_ptr<const mediametrics::Item>& item,
+ const std::shared_ptr<mediametrics::StatsdLog>& statsdLog) {
+ static const std::map<std::string, statsd_pusher*> statsd_pushers =
+ {
+ { "audiopolicy", statsd_audiopolicy },
+ { "audiorecord", statsd_audiorecord },
+ { "audiothread", statsd_audiothread },
+ { "audiotrack", statsd_audiotrack },
+ { "codec", statsd_codec},
+ { "drmmanager", statsd_drmmanager },
+ { "extractor", statsd_extractor },
+ { "mediadrm", statsd_mediadrm },
+ { "mediaparser", statsd_mediaparser },
+ { "nuplayer", statsd_nuplayer },
+ { "nuplayer2", statsd_nuplayer },
+ { "recorder", statsd_recorder },
+ };
+ return dump2StatsdInternal(statsd_pushers, item, statsdLog);
+}
+
+bool dump2Statsd(const std::shared_ptr<const mediametrics::Item>& item, AStatsEventList* out,
+ const std::shared_ptr<mediametrics::StatsdLog>& statsdLog) {
+ static const std::map<std::string, statsd_puller*> statsd_pullers =
+ {
+ { "mediadrm", statsd_mediadrm_puller },
+ };
+ return dump2StatsdInternal(statsd_pullers, item, out, statsdLog);
+}
} // namespace android
diff --git a/services/mediametrics/iface_statsd.h b/services/mediametrics/iface_statsd.h
index 9b49556..c2a8b3c 100644
--- a/services/mediametrics/iface_statsd.h
+++ b/services/mediametrics/iface_statsd.h
@@ -14,23 +14,37 @@
* limitations under the License.
*/
+#include <memory>
+#include <stats_event.h>
+
namespace android {
+namespace mediametrics {
+class Item;
+}
-extern bool enabled_statsd;
-
+using statsd_pusher = bool (const std::shared_ptr<const mediametrics::Item>& item,
+ const std::shared_ptr<mediametrics::StatsdLog>& statsdLog);
// 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_mediaparser(const mediametrics::Item *);
-extern bool statsd_nuplayer(const mediametrics::Item *);
-extern bool statsd_recorder(const mediametrics::Item *);
+extern statsd_pusher statsd_audiopolicy;
+extern statsd_pusher statsd_audiorecord;
+extern statsd_pusher statsd_audiothread;
+extern statsd_pusher statsd_audiotrack;
+extern statsd_pusher statsd_codec;
+extern statsd_pusher statsd_extractor;
+extern statsd_pusher statsd_mediaparser;
-extern bool statsd_mediadrm(const mediametrics::Item *);
-extern bool statsd_widevineCDM(const mediametrics::Item *);
-extern bool statsd_drmmanager(const mediametrics::Item *);
+extern statsd_pusher statsd_nuplayer;
+extern statsd_pusher statsd_recorder;
+extern statsd_pusher statsd_mediadrm;
+extern statsd_pusher statsd_drmmanager;
+using statsd_puller = bool (const std::shared_ptr<const mediametrics::Item>& item,
+ AStatsEventList *, const std::shared_ptr<mediametrics::StatsdLog>& statsdLog);
+// component specific pullers
+extern statsd_puller statsd_mediadrm_puller;
+
+bool dump2Statsd(const std::shared_ptr<const mediametrics::Item>& item,
+ const std::shared_ptr<mediametrics::StatsdLog>& statsdLog);
+bool dump2Statsd(const std::shared_ptr<const mediametrics::Item>& item, AStatsEventList* out,
+ const std::shared_ptr<mediametrics::StatsdLog>& statsdLog);
} // namespace android
diff --git a/services/mediametrics/statsd_audiopolicy.cpp b/services/mediametrics/statsd_audiopolicy.cpp
index 6ef2f2c..f44b7c4 100644
--- a/services/mediametrics/statsd_audiopolicy.cpp
+++ b/services/mediametrics/statsd_audiopolicy.cpp
@@ -37,16 +37,16 @@
namespace android {
-bool statsd_audiopolicy(const mediametrics::Item *item)
+bool statsd_audiopolicy(const std::shared_ptr<const mediametrics::Item>& item,
+ const std::shared_ptr<mediametrics::StatsdLog>& statsdLog)
{
if (item == nullptr) return false;
// these go into the statsd wrapper
- const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
- std::string pkgName = item->getPkgName();
- int64_t pkgVersionCode = item->getPkgVersionCode();
- int64_t mediaApexVersion = 0;
-
+ const nsecs_t timestamp_nanos = MediaMetricsService::roundTime(item->getTimestamp());
+ const std::string package_name = item->getPkgName();
+ const int64_t package_version_code = item->getPkgVersionCode();
+ const int64_t media_apex_version = 0;
// the rest into our own proto
//
@@ -60,35 +60,35 @@
metrics_proto.set_status(status);
}
//string char kAudioPolicyRqstSrc[] = "android.media.audiopolicy.rqst.src";
- std::string rqst_src;
- if (item->getString("android.media.audiopolicy.rqst.src", &rqst_src)) {
- metrics_proto.set_request_source(std::move(rqst_src));
+ std::string request_source;
+ if (item->getString("android.media.audiopolicy.rqst.src", &request_source)) {
+ metrics_proto.set_request_source(request_source);
}
//string char kAudioPolicyRqstPkg[] = "android.media.audiopolicy.rqst.pkg";
- std::string rqst_pkg;
- if (item->getString("android.media.audiopolicy.rqst.pkg", &rqst_pkg)) {
- metrics_proto.set_request_package(std::move(rqst_pkg));
+ std::string request_package;
+ if (item->getString("android.media.audiopolicy.rqst.pkg", &request_package)) {
+ metrics_proto.set_request_package(request_package);
}
//int32 char kAudioPolicyRqstSession[] = "android.media.audiopolicy.rqst.session";
- int32_t rqst_session = -1;
- if (item->getInt32("android.media.audiopolicy.rqst.session", &rqst_session)) {
- metrics_proto.set_request_session(rqst_session);
+ int32_t request_session = -1;
+ if (item->getInt32("android.media.audiopolicy.rqst.session", &request_session)) {
+ metrics_proto.set_request_session(request_session);
}
//string char kAudioPolicyRqstDevice[] = "android.media.audiopolicy.rqst.device";
- std::string rqst_device;
- if (item->getString("android.media.audiopolicy.rqst.device", &rqst_device)) {
- metrics_proto.set_request_device(std::move(rqst_device));
+ std::string request_device;
+ if (item->getString("android.media.audiopolicy.rqst.device", &request_device)) {
+ metrics_proto.set_request_device(request_device);
}
//string char kAudioPolicyActiveSrc[] = "android.media.audiopolicy.active.src";
- std::string active_src;
- if (item->getString("android.media.audiopolicy.active.src", &active_src)) {
- metrics_proto.set_active_source(std::move(active_src));
+ std::string active_source;
+ if (item->getString("android.media.audiopolicy.active.src", &active_source)) {
+ metrics_proto.set_active_source(active_source);
}
//string char kAudioPolicyActivePkg[] = "android.media.audiopolicy.active.pkg";
- std::string active_pkg;
- if (item->getString("android.media.audiopolicy.active.pkg", &active_pkg)) {
- metrics_proto.set_active_package(std::move(active_pkg));
+ std::string active_package;
+ if (item->getString("android.media.audiopolicy.active.pkg", &active_package)) {
+ metrics_proto.set_active_package(active_package);
}
//int32 char kAudioPolicyActiveSession[] = "android.media.audiopolicy.active.session";
int32_t active_session = -1;
@@ -98,27 +98,40 @@
//string char kAudioPolicyActiveDevice[] = "android.media.audiopolicy.active.device";
std::string active_device;
if (item->getString("android.media.audiopolicy.active.device", &active_device)) {
- metrics_proto.set_active_device(std::move(active_device));
+ metrics_proto.set_active_device(active_device);
}
-
std::string serialized;
if (!metrics_proto.SerializeToString(&serialized)) {
ALOGE("Failed to serialize audipolicy metrics");
return false;
}
- if (enabled_statsd) {
- android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
- (void)android::util::stats_write(android::util::MEDIAMETRICS_AUDIOPOLICY_REPORTED,
- timestamp, pkgName.c_str(), pkgVersionCode,
- mediaApexVersion,
- bf_serialized);
+ android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
+ int result = android::util::stats_write(android::util::MEDIAMETRICS_AUDIOPOLICY_REPORTED,
+ timestamp_nanos, package_name.c_str(), package_version_code,
+ media_apex_version,
+ bf_serialized);
+ std::stringstream log;
+ log << "result:" << result << " {"
+ << " mediametrics_audiopolicy_reported:"
+ << android::util::MEDIAMETRICS_AUDIOPOLICY_REPORTED
+ << " timestamp_nanos:" << timestamp_nanos
+ << " package_name:" << package_name
+ << " package_version_code:" << package_version_code
+ << " media_apex_version:" << media_apex_version
- } else {
- ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str()));
- }
-
+ << " status:" << status
+ << " request_source:" << request_source
+ << " request_package:" << request_package
+ << " request_session:" << request_session
+ << " request_device:" << request_device
+ << " active_source:" << active_source
+ << " active_package:" << active_package
+ << " active_session:" << active_session
+ << " active_device:" << active_device
+ << " }";
+ statsdLog->log(android::util::MEDIAMETRICS_AUDIOPOLICY_REPORTED, log.str());
return true;
}
diff --git a/services/mediametrics/statsd_audiorecord.cpp b/services/mediametrics/statsd_audiorecord.cpp
index 76f4b59..70a67ae 100644
--- a/services/mediametrics/statsd_audiorecord.cpp
+++ b/services/mediametrics/statsd_audiorecord.cpp
@@ -32,21 +32,21 @@
#include <statslog.h>
#include "MediaMetricsService.h"
+#include "StringUtils.h"
#include "frameworks/proto_logging/stats/enums/stats/mediametrics/mediametrics.pb.h"
#include "iface_statsd.h"
namespace android {
-bool statsd_audiorecord(const mediametrics::Item *item)
-{
+bool statsd_audiorecord(const std::shared_ptr<const mediametrics::Item>& item,
+ const std::shared_ptr<mediametrics::StatsdLog>& statsdLog) {
if (item == nullptr) return false;
// these go into the statsd wrapper
- const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
- std::string pkgName = item->getPkgName();
- int64_t pkgVersionCode = item->getPkgVersionCode();
- int64_t mediaApexVersion = 0;
-
+ const nsecs_t timestamp_nanos = MediaMetricsService::roundTime(item->getTimestamp());
+ const std::string package_name = item->getPkgName();
+ const int64_t package_version_code = item->getPkgVersionCode();
+ const int64_t media_apex_version = 0;
// the rest into our own proto
//
@@ -56,12 +56,12 @@
//
std::string encoding;
if (item->getString("android.media.audiorecord.encoding", &encoding)) {
- metrics_proto.set_encoding(std::move(encoding));
+ metrics_proto.set_encoding(encoding);
}
std::string source;
if (item->getString("android.media.audiorecord.source", &source)) {
- metrics_proto.set_source(std::move(source));
+ metrics_proto.set_source(source);
}
int32_t latency = -1;
@@ -79,14 +79,14 @@
metrics_proto.set_channels(channels);
}
- int64_t createdMs = -1;
- if (item->getInt64("android.media.audiorecord.createdMs", &createdMs)) {
- metrics_proto.set_created_millis(createdMs);
+ int64_t created_millis = -1;
+ if (item->getInt64("android.media.audiorecord.createdMs", &created_millis)) {
+ metrics_proto.set_created_millis(created_millis);
}
- int64_t durationMs = -1;
- if (item->getInt64("android.media.audiorecord.durationMs", &durationMs)) {
- metrics_proto.set_duration_millis(durationMs);
+ int64_t duration_millis = -1;
+ if (item->getInt64("android.media.audiorecord.durationMs", &duration_millis)) {
+ metrics_proto.set_duration_millis(duration_millis);
}
int32_t count = -1;
@@ -94,46 +94,44 @@
metrics_proto.set_count(count);
}
- int32_t errcode = -1;
- if (item->getInt32("android.media.audiorecord.errcode", &errcode)) {
- metrics_proto.set_error_code(errcode);
- } else if (item->getInt32("android.media.audiorecord.lastError.code", &errcode)) {
- metrics_proto.set_error_code(errcode);
+ int32_t error_code = -1;
+ if (item->getInt32("android.media.audiorecord.errcode", &error_code)) {
+ metrics_proto.set_error_code(error_code);
+ } else if (item->getInt32("android.media.audiorecord.lastError.code", &error_code)) {
+ metrics_proto.set_error_code(error_code);
}
- std::string errfunc;
- if (item->getString("android.media.audiorecord.errfunc", &errfunc)) {
- metrics_proto.set_error_function(std::move(errfunc));
- } else if (item->getString("android.media.audiorecord.lastError.at", &errfunc)) {
- metrics_proto.set_error_function(std::move(errfunc));
+ std::string error_function;
+ if (item->getString("android.media.audiorecord.errfunc", &error_function)) {
+ metrics_proto.set_error_function(error_function);
+ } else if (item->getString("android.media.audiorecord.lastError.at", &error_function)) {
+ metrics_proto.set_error_function(error_function);
}
- // portId (int32)
int32_t port_id = -1;
if (item->getInt32("android.media.audiorecord.portId", &port_id)) {
metrics_proto.set_port_id(count);
}
- // frameCount (int32)
- int32_t frameCount = -1;
- if (item->getInt32("android.media.audiorecord.frameCount", &frameCount)) {
- metrics_proto.set_frame_count(frameCount);
- }
- // attributes (string)
- std::string attributes;
- if (item->getString("android.media.audiorecord.attributes", &attributes)) {
- metrics_proto.set_attributes(std::move(attributes));
- }
- // channelMask (int64)
- int64_t channelMask = -1;
- if (item->getInt64("android.media.audiorecord.channelMask", &channelMask)) {
- metrics_proto.set_channel_mask(channelMask);
- }
- // startcount (int64)
- int64_t startcount = -1;
- if (item->getInt64("android.media.audiorecord.startcount", &startcount)) {
- metrics_proto.set_start_count(startcount);
+
+ int32_t frame_count = -1;
+ if (item->getInt32("android.media.audiorecord.frameCount", &frame_count)) {
+ metrics_proto.set_frame_count(frame_count);
}
+ std::string attributes;
+ if (item->getString("android.media.audiorecord.attributes", &attributes)) {
+ metrics_proto.set_attributes(attributes);
+ }
+
+ int64_t channel_mask = -1;
+ if (item->getInt64("android.media.audiorecord.channelMask", &channel_mask)) {
+ metrics_proto.set_channel_mask(channel_mask);
+ }
+
+ int64_t start_count = -1;
+ if (item->getInt64("android.media.audiorecord.startcount", &start_count)) {
+ metrics_proto.set_start_count(start_count);
+ }
std::string serialized;
if (!metrics_proto.SerializeToString(&serialized)) {
@@ -141,17 +139,48 @@
return false;
}
- if (enabled_statsd) {
- android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
- (void)android::util::stats_write(android::util::MEDIAMETRICS_AUDIORECORD_REPORTED,
- timestamp, pkgName.c_str(), pkgVersionCode,
- mediaApexVersion,
- bf_serialized);
+ // Android S
+ // log_session_id (string)
+ std::string logSessionId;
+ (void)item->getString("android.media.audiorecord.logSessionId", &logSessionId);
+ const auto log_session_id =
+ mediametrics::stringutils::sanitizeLogSessionId(logSessionId);
- } else {
- ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str()));
- }
+ android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
+ int result = android::util::stats_write(android::util::MEDIAMETRICS_AUDIORECORD_REPORTED,
+ timestamp_nanos, package_name.c_str(), package_version_code,
+ media_apex_version,
+ bf_serialized,
+ log_session_id.c_str());
+ std::stringstream log;
+ log << "result:" << result << " {"
+ << " mediametrics_audiorecord_reported:"
+ << android::util::MEDIAMETRICS_AUDIORECORD_REPORTED
+ << " timestamp_nanos:" << timestamp_nanos
+ << " package_name:" << package_name
+ << " package_version_code:" << package_version_code
+ << " media_apex_version:" << media_apex_version
+ << " encoding:" << encoding
+ << " source:" << source
+ << " latency:" << latency
+ << " samplerate:" << samplerate
+ << " channels:" << channels
+ << " created_millis:" << created_millis
+ << " duration_millis:" << duration_millis
+ << " count:" << count
+ << " error_code:" << error_code
+ << " error_function:" << error_function
+
+ << " port_id:" << port_id
+ << " frame_count:" << frame_count
+ << " attributes:" << attributes
+ << " channel_mask:" << channel_mask
+ << " start_count:" << start_count
+
+ << " log_session_id:" << log_session_id
+ << " }";
+ statsdLog->log(android::util::MEDIAMETRICS_AUDIORECORD_REPORTED, log.str());
return true;
}
diff --git a/services/mediametrics/statsd_audiothread.cpp b/services/mediametrics/statsd_audiothread.cpp
index 2ad2562..34cc923 100644
--- a/services/mediametrics/statsd_audiothread.cpp
+++ b/services/mediametrics/statsd_audiothread.cpp
@@ -37,16 +37,16 @@
namespace android {
-bool statsd_audiothread(const mediametrics::Item *item)
+bool statsd_audiothread(const std::shared_ptr<const mediametrics::Item>& item,
+ const std::shared_ptr<mediametrics::StatsdLog>& statsdLog)
{
if (item == nullptr) return false;
// these go into the statsd wrapper
- const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
- std::string pkgName = item->getPkgName();
- int64_t pkgVersionCode = item->getPkgVersionCode();
- int64_t mediaApexVersion = 0;
-
+ const nsecs_t timestamp_nanos = MediaMetricsService::roundTime(item->getTimestamp());
+ const std::string package_name = item->getPkgName();
+ const int64_t package_version_code = item->getPkgVersionCode();
+ const int64_t media_apex_version = 0;
// the rest into our own proto
//
@@ -68,17 +68,17 @@
if (item->getInt32(MM_PREFIX "samplerate", &samplerate)) {
metrics_proto.set_samplerate(samplerate);
}
- std::string workhist;
- if (item->getString(MM_PREFIX "workMs.hist", &workhist)) {
- metrics_proto.set_work_millis_hist(std::move(workhist));
+ std::string work_millis_hist;
+ if (item->getString(MM_PREFIX "workMs.hist", &work_millis_hist)) {
+ metrics_proto.set_work_millis_hist(work_millis_hist);
}
- std::string latencyhist;
- if (item->getString(MM_PREFIX "latencyMs.hist", &latencyhist)) {
- metrics_proto.set_latency_millis_hist(std::move(latencyhist));
+ std::string latency_millis_hist;
+ if (item->getString(MM_PREFIX "latencyMs.hist", &latency_millis_hist)) {
+ metrics_proto.set_latency_millis_hist(latency_millis_hist);
}
- std::string warmuphist;
- if (item->getString(MM_PREFIX "warmupMs.hist", &warmuphist)) {
- metrics_proto.set_warmup_millis_hist(std::move(warmuphist));
+ std::string warmup_millis_hist;
+ if (item->getString(MM_PREFIX "warmupMs.hist", &warmup_millis_hist)) {
+ metrics_proto.set_warmup_millis_hist(warmup_millis_hist);
}
int64_t underruns = -1;
if (item->getInt64(MM_PREFIX "underruns", &underruns)) {
@@ -88,101 +88,99 @@
if (item->getInt64(MM_PREFIX "overruns", &overruns)) {
metrics_proto.set_overruns(overruns);
}
- int64_t activeMs = -1;
- if (item->getInt64(MM_PREFIX "activeMs", &activeMs)) {
- metrics_proto.set_active_millis(activeMs);
+ int64_t active_millis = -1;
+ if (item->getInt64(MM_PREFIX "activeMs", &active_millis)) {
+ metrics_proto.set_active_millis(active_millis);
}
- int64_t durationMs = -1;
- if (item->getInt64(MM_PREFIX "durationMs", &durationMs)) {
- metrics_proto.set_duration_millis(durationMs);
+ int64_t duration_millis = -1;
+ if (item->getInt64(MM_PREFIX "durationMs", &duration_millis)) {
+ metrics_proto.set_duration_millis(duration_millis);
}
- // item->setInt32(MM_PREFIX "id", (int32_t)mId); // IO handle
int32_t id = -1;
if (item->getInt32(MM_PREFIX "id", &id)) {
metrics_proto.set_id(id);
}
- // item->setInt32(MM_PREFIX "portId", (int32_t)mPortId);
+
int32_t port_id = -1;
- if (item->getInt32(MM_PREFIX "portId", &id)) {
+ if (item->getInt32(MM_PREFIX "portId", &port_id)) {
metrics_proto.set_port_id(port_id);
}
// item->setCString(MM_PREFIX "type", threadTypeToString(mType));
std::string type;
if (item->getString(MM_PREFIX "type", &type)) {
- metrics_proto.set_type(std::move(type));
+ metrics_proto.set_type(type);
}
- // item->setInt32(MM_PREFIX "sampleRate", (int32_t)mSampleRate);
+
int32_t sample_rate = -1;
if (item->getInt32(MM_PREFIX "sampleRate", &sample_rate)) {
metrics_proto.set_sample_rate(sample_rate);
}
- // item->setInt64(MM_PREFIX "channelMask", (int64_t)mChannelMask);
+
int32_t channel_mask = -1;
if (item->getInt32(MM_PREFIX "channelMask", &channel_mask)) {
metrics_proto.set_channel_mask(channel_mask);
}
- // item->setCString(MM_PREFIX "encoding", toString(mFormat).c_str());
+
std::string encoding;
if (item->getString(MM_PREFIX "encoding", &encoding)) {
- metrics_proto.set_encoding(std::move(encoding));
+ metrics_proto.set_encoding(encoding);
}
- // item->setInt32(MM_PREFIX "frameCount", (int32_t)mFrameCount);
+
int32_t frame_count = -1;
if (item->getInt32(MM_PREFIX "frameCount", &frame_count)) {
metrics_proto.set_frame_count(frame_count);
}
- // item->setCString(MM_PREFIX "outDevice", toString(mOutDevice).c_str());
- std::string outDevice;
- if (item->getString(MM_PREFIX "outDevice", &outDevice)) {
- metrics_proto.set_output_device(std::move(outDevice));
- }
- // item->setCString(MM_PREFIX "inDevice", toString(mInDevice).c_str());
- std::string inDevice;
- if (item->getString(MM_PREFIX "inDevice", &inDevice)) {
- metrics_proto.set_input_device(std::move(inDevice));
- }
- // item->setDouble(MM_PREFIX "ioJitterMs.mean", mIoJitterMs.getMean());
- double iojitters_ms_mean = -1;
- if (item->getDouble(MM_PREFIX "ioJitterMs.mean", &iojitters_ms_mean)) {
- metrics_proto.set_io_jitter_mean_millis(iojitters_ms_mean);
- }
- // item->setDouble(MM_PREFIX "ioJitterMs.std", mIoJitterMs.getStdDev());
- double iojitters_ms_std = -1;
- if (item->getDouble(MM_PREFIX "ioJitterMs.std", &iojitters_ms_std)) {
- metrics_proto.set_io_jitter_stddev_millis(iojitters_ms_std);
- }
- // item->setDouble(MM_PREFIX "processTimeMs.mean", mProcessTimeMs.getMean());
- double process_time_ms_mean = -1;
- if (item->getDouble(MM_PREFIX "processTimeMs.mean", &process_time_ms_mean)) {
- metrics_proto.set_process_time_mean_millis(process_time_ms_mean);
- }
- // item->setDouble(MM_PREFIX "processTimeMs.std", mProcessTimeMs.getStdDev());
- double process_time_ms_std = -1;
- if (item->getDouble(MM_PREFIX "processTimeMs.std", &process_time_ms_std)) {
- metrics_proto.set_process_time_stddev_millis(process_time_ms_std);
- }
- // item->setDouble(MM_PREFIX "timestampJitterMs.mean", tsjitter.getMean());
- double timestamp_jitter_ms_mean = -1;
- if (item->getDouble(MM_PREFIX "timestampJitterMs.mean", ×tamp_jitter_ms_mean)) {
- metrics_proto.set_timestamp_jitter_mean_millis(timestamp_jitter_ms_mean);
- }
- // item->setDouble(MM_PREFIX "timestampJitterMs.std", tsjitter.getStdDev());
- double timestamp_jitter_ms_stddev = -1;
- if (item->getDouble(MM_PREFIX "timestampJitterMs.std", ×tamp_jitter_ms_stddev)) {
- metrics_proto.set_timestamp_jitter_stddev_millis(timestamp_jitter_ms_stddev);
- }
- // item->setDouble(MM_PREFIX "latencyMs.mean", mLatencyMs.getMean());
- double latency_ms_mean = -1;
- if (item->getDouble(MM_PREFIX "latencyMs.mean", &latency_ms_mean)) {
- metrics_proto.set_latency_mean_millis(latency_ms_mean);
- }
- // item->setDouble(MM_PREFIX "latencyMs.std", mLatencyMs.getStdDev());
- double latency_ms_stddev = -1;
- if (item->getDouble(MM_PREFIX "latencyMs.std", &latency_ms_stddev)) {
- metrics_proto.set_latency_stddev_millis(latency_ms_stddev);
+
+ std::string output_device;
+ if (item->getString(MM_PREFIX "outDevice", &output_device)) {
+ metrics_proto.set_output_device(output_device);
}
+ std::string input_device;
+ if (item->getString(MM_PREFIX "inDevice", &input_device)) {
+ metrics_proto.set_input_device(input_device);
+ }
+
+ double io_jitter_mean_millis = -1;
+ if (item->getDouble(MM_PREFIX "ioJitterMs.mean", &io_jitter_mean_millis)) {
+ metrics_proto.set_io_jitter_mean_millis(io_jitter_mean_millis);
+ }
+
+ double io_jitter_stddev_millis = -1;
+ if (item->getDouble(MM_PREFIX "ioJitterMs.std", &io_jitter_stddev_millis)) {
+ metrics_proto.set_io_jitter_stddev_millis(io_jitter_stddev_millis);
+ }
+
+ double process_time_mean_millis = -1;
+ if (item->getDouble(MM_PREFIX "processTimeMs.mean", &process_time_mean_millis)) {
+ metrics_proto.set_process_time_mean_millis(process_time_mean_millis);
+ }
+
+ double process_time_stddev_millis = -1;
+ if (item->getDouble(MM_PREFIX "processTimeMs.std", &process_time_stddev_millis)) {
+ metrics_proto.set_process_time_stddev_millis(process_time_stddev_millis);
+ }
+
+ double timestamp_jitter_mean_millis = -1;
+ if (item->getDouble(MM_PREFIX "timestampJitterMs.mean", ×tamp_jitter_mean_millis)) {
+ metrics_proto.set_timestamp_jitter_mean_millis(timestamp_jitter_mean_millis);
+ }
+
+ double timestamp_jitter_stddev_millis = -1;
+ if (item->getDouble(MM_PREFIX "timestampJitterMs.std", ×tamp_jitter_stddev_millis)) {
+ metrics_proto.set_timestamp_jitter_stddev_millis(timestamp_jitter_stddev_millis);
+ }
+
+ double latency_mean_millis = -1;
+ if (item->getDouble(MM_PREFIX "latencyMs.mean", &latency_mean_millis)) {
+ metrics_proto.set_latency_mean_millis(latency_mean_millis);
+ }
+
+ double latency_stddev_millis = -1;
+ if (item->getDouble(MM_PREFIX "latencyMs.std", &latency_stddev_millis)) {
+ metrics_proto.set_latency_stddev_millis(latency_stddev_millis);
+ }
std::string serialized;
if (!metrics_proto.SerializeToString(&serialized)) {
@@ -190,17 +188,50 @@
return false;
}
- if (enabled_statsd) {
- android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
- (void)android::util::stats_write(android::util::MEDIAMETRICS_AUDIOTHREAD_REPORTED,
- timestamp, pkgName.c_str(), pkgVersionCode,
- mediaApexVersion,
- bf_serialized);
+ android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
+ int result = android::util::stats_write(android::util::MEDIAMETRICS_AUDIOTHREAD_REPORTED,
+ timestamp_nanos, package_name.c_str(), package_version_code,
+ media_apex_version,
+ bf_serialized);
+ std::stringstream log;
+ log << "result:" << result << " {"
+ << " mediametrics_audiothread_reported:"
+ << android::util::MEDIAMETRICS_AUDIOTHREAD_REPORTED
+ << " timestamp_nanos:" << timestamp_nanos
+ << " package_name:" << package_name
+ << " package_version_code:" << package_version_code
+ << " media_apex_version:" << media_apex_version
- } else {
- ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str()));
- }
+ << " type:" << type
+ << " framecount:" << framecount
+ << " samplerate:" << samplerate
+ << " work_millis_hist:" << work_millis_hist
+ << " latency_millis_hist:" << latency_millis_hist
+ << " warmup_millis_hist:" << warmup_millis_hist
+ << " underruns:" << underruns
+ << " overruns:" << overruns
+ << " active_millis:" << active_millis
+ << " duration_millis:" << duration_millis
+ << " id:" << id
+ << " port_id:" << port_id
+ << " sample_rate:" << sample_rate
+ << " channel_mask:" << channel_mask
+ << " encoding:" << encoding
+ << " frame_count:" << frame_count
+ << " output_device:" << output_device
+ << " input_device:" << input_device
+ << " io_jitter_mean_millis:" << io_jitter_mean_millis
+ << " io_jitter_stddev_millis:" << io_jitter_stddev_millis
+
+ << " process_time_mean_millis:" << process_time_mean_millis
+ << " process_time_stddev_millis:" << process_time_stddev_millis
+ << " timestamp_jitter_mean_millis:" << timestamp_jitter_mean_millis
+ << " timestamp_jitter_stddev_millis:" << timestamp_jitter_stddev_millis
+ << " latency_mean_millis:" << latency_mean_millis
+ << " latency_stddev_millis:" << latency_stddev_millis
+ << " }";
+ statsdLog->log(android::util::MEDIAMETRICS_AUDIOTHREAD_REPORTED, log.str());
return true;
}
diff --git a/services/mediametrics/statsd_audiotrack.cpp b/services/mediametrics/statsd_audiotrack.cpp
index 6b08a78..fe269a1 100644
--- a/services/mediametrics/statsd_audiotrack.cpp
+++ b/services/mediametrics/statsd_audiotrack.cpp
@@ -32,21 +32,22 @@
#include <statslog.h>
#include "MediaMetricsService.h"
+#include "StringUtils.h"
#include "frameworks/proto_logging/stats/enums/stats/mediametrics/mediametrics.pb.h"
#include "iface_statsd.h"
namespace android {
-bool statsd_audiotrack(const mediametrics::Item *item)
+bool statsd_audiotrack(const std::shared_ptr<const mediametrics::Item>& item,
+ const std::shared_ptr<mediametrics::StatsdLog>& statsdLog)
{
if (item == nullptr) return false;
// these go into the statsd wrapper
- const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
- std::string pkgName = item->getPkgName();
- int64_t pkgVersionCode = item->getPkgVersionCode();
- int64_t mediaApexVersion = 0;
-
+ const nsecs_t timestamp_nanos = MediaMetricsService::roundTime(item->getTimestamp());
+ const std::string package_name = item->getPkgName();
+ const int64_t package_version_code = item->getPkgVersionCode();
+ const int64_t media_apex_version = 0;
// the rest into our own proto
//
@@ -57,52 +58,52 @@
// static constexpr char kAudioTrackStreamType[] = "android.media.audiotrack.streamtype";
// optional string streamType;
- std::string streamtype;
- if (item->getString("android.media.audiotrack.streamtype", &streamtype)) {
- metrics_proto.set_stream_type(std::move(streamtype));
+ std::string stream_type;
+ if (item->getString("android.media.audiotrack.streamtype", &stream_type)) {
+ metrics_proto.set_stream_type(stream_type);
}
// static constexpr char kAudioTrackContentType[] = "android.media.audiotrack.type";
// optional string contentType;
- std::string contenttype;
- if (item->getString("android.media.audiotrack.type", &contenttype)) {
- metrics_proto.set_content_type(std::move(contenttype));
+ std::string content_type;
+ if (item->getString("android.media.audiotrack.type", &content_type)) {
+ metrics_proto.set_content_type(content_type);
}
// static constexpr char kAudioTrackUsage[] = "android.media.audiotrack.usage";
// optional string trackUsage;
- std::string trackusage;
- if (item->getString("android.media.audiotrack.usage", &trackusage)) {
- metrics_proto.set_track_usage(std::move(trackusage));
+ std::string track_usage;
+ if (item->getString("android.media.audiotrack.usage", &track_usage)) {
+ metrics_proto.set_track_usage(track_usage);
}
// static constexpr char kAudioTrackSampleRate[] = "android.media.audiotrack.samplerate";
// optional int32 samplerate;
- int32_t samplerate = -1;
- if (item->getInt32("android.media.audiotrack.samplerate", &samplerate)) {
- metrics_proto.set_sample_rate(samplerate);
+ int32_t sample_rate = -1;
+ if (item->getInt32("android.media.audiotrack.samplerate", &sample_rate)) {
+ metrics_proto.set_sample_rate(sample_rate);
}
// static constexpr char kAudioTrackChannelMask[] = "android.media.audiotrack.channelmask";
// optional int64 channelMask;
- int64_t channelMask = -1;
- if (item->getInt64("android.media.audiotrack.channelmask", &channelMask)) {
- metrics_proto.set_channel_mask(channelMask);
+ int64_t channel_mask = -1;
+ if (item->getInt64("android.media.audiotrack.channelmask", &channel_mask)) {
+ metrics_proto.set_channel_mask(channel_mask);
}
// NB: These are not yet exposed as public Java API constants.
// static constexpr char kAudioTrackUnderrunFrames[] = "android.media.audiotrack.underrunframes";
// optional int32 underrunframes;
- int32_t underrunframes = -1;
- if (item->getInt32("android.media.audiotrack.underrunframes", &underrunframes)) {
- metrics_proto.set_underrun_frames(underrunframes);
+ int32_t underrun_frames = -1;
+ if (item->getInt32("android.media.audiotrack.underrunframes", &underrun_frames)) {
+ metrics_proto.set_underrun_frames(underrun_frames);
}
// static constexpr char kAudioTrackStartupGlitch[] = "android.media.audiotrack.glitch.startup";
// optional int32 startupglitch;
- int32_t startupglitch = -1;
- if (item->getInt32("android.media.audiotrack.glitch.startup", &startupglitch)) {
- metrics_proto.set_startup_glitch(startupglitch);
+ int32_t startup_glitch = -1;
+ if (item->getInt32("android.media.audiotrack.glitch.startup", &startup_glitch)) {
+ metrics_proto.set_startup_glitch(startup_glitch);
}
// portId (int32)
@@ -113,7 +114,7 @@
// encoding (string)
std::string encoding;
if (item->getString("android.media.audiotrack.encoding", &encoding)) {
- metrics_proto.set_encoding(std::move(encoding));
+ metrics_proto.set_encoding(encoding);
}
// frameCount (int32)
int32_t frame_count = -1;
@@ -123,7 +124,7 @@
// attributes (string)
std::string attributes;
if (item->getString("android.media.audiotrack.attributes", &attributes)) {
- metrics_proto.set_attributes(std::move(attributes));
+ metrics_proto.set_attributes(attributes);
}
std::string serialized;
@@ -132,17 +133,44 @@
return false;
}
- if (enabled_statsd) {
- android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
- (void)android::util::stats_write(android::util::MEDIAMETRICS_AUDIOTRACK_REPORTED,
- timestamp, pkgName.c_str(), pkgVersionCode,
- mediaApexVersion,
- bf_serialized);
+ // Android S
+ // log_session_id (string)
+ std::string logSessionId;
+ (void)item->getString("android.media.audiotrack.logSessionId", &logSessionId);
+ const auto log_session_id =
+ mediametrics::stringutils::sanitizeLogSessionId(logSessionId);
- } else {
- ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str()));
- }
+ android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
+ int result = android::util::stats_write(android::util::MEDIAMETRICS_AUDIOTRACK_REPORTED,
+ timestamp_nanos, package_name.c_str(), package_version_code,
+ media_apex_version,
+ bf_serialized,
+ log_session_id.c_str());
+ std::stringstream log;
+ log << "result:" << result << " {"
+ << " mediametrics_audiotrack_reported:"
+ << android::util::MEDIAMETRICS_AUDIOTRACK_REPORTED
+ << " timestamp_nanos:" << timestamp_nanos
+ << " package_name:" << package_name
+ << " package_version_code:" << package_version_code
+ << " media_apex_version:" << media_apex_version
+ << " stream_type:" << stream_type
+ << " content_type:" << content_type
+ << " track_usage:" << track_usage
+ << " sample_rate:" << sample_rate
+ << " channel_mask:" << channel_mask
+ << " underrun_frames:" << underrun_frames
+ << " startup_glitch:" << startup_glitch
+ << " port_id:" << port_id
+ << " encoding:" << encoding
+ << " frame_count:" << frame_count
+
+ << " attributes:" << attributes
+
+ << " log_session_id:" << log_session_id
+ << " }";
+ statsdLog->log(android::util::MEDIAMETRICS_AUDIOTRACK_REPORTED, log.str());
return true;
}
diff --git a/services/mediametrics/statsd_codec.cpp b/services/mediametrics/statsd_codec.cpp
index d502b30..2cfdf24 100644
--- a/services/mediametrics/statsd_codec.cpp
+++ b/services/mediametrics/statsd_codec.cpp
@@ -33,157 +33,271 @@
#include "cleaner.h"
#include "MediaMetricsService.h"
-#include "frameworks/proto_logging/stats/enums/stats/mediametrics/mediametrics.pb.h"
+#include "frameworks/proto_logging/stats/message/mediametrics_message.pb.h"
#include "iface_statsd.h"
namespace android {
-bool statsd_codec(const mediametrics::Item *item)
+bool statsd_codec(const std::shared_ptr<const mediametrics::Item>& item,
+ const std::shared_ptr<mediametrics::StatsdLog>& statsdLog)
{
if (item == nullptr) return false;
// these go into the statsd wrapper
- const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
- std::string pkgName = item->getPkgName();
- int64_t pkgVersionCode = item->getPkgVersionCode();
- int64_t mediaApexVersion = 0;
-
+ const nsecs_t timestamp_nanos = MediaMetricsService::roundTime(item->getTimestamp());
+ const std::string package_name = item->getPkgName();
+ const int64_t package_version_code = item->getPkgVersionCode();
+ const int64_t media_apex_version = 0;
// the rest into our own proto
//
- ::android::stats::mediametrics::CodecData metrics_proto;
+ ::android::stats::mediametrics_message::CodecData metrics_proto;
// flesh out the protobuf we'll hand off with our data
//
// android.media.mediacodec.codec string
std::string codec;
if (item->getString("android.media.mediacodec.codec", &codec)) {
- metrics_proto.set_codec(std::move(codec));
+ metrics_proto.set_codec(codec);
}
- // android.media.mediacodec.mime string
+
std::string mime;
if (item->getString("android.media.mediacodec.mime", &mime)) {
- metrics_proto.set_mime(std::move(mime));
+ metrics_proto.set_mime(mime);
}
- // android.media.mediacodec.mode string
+
std::string mode;
if ( item->getString("android.media.mediacodec.mode", &mode)) {
- metrics_proto.set_mode(std::move(mode));
+ metrics_proto.set_mode(mode);
}
- // android.media.mediacodec.encoder int32
+
int32_t encoder = -1;
if ( item->getInt32("android.media.mediacodec.encoder", &encoder)) {
metrics_proto.set_encoder(encoder);
}
- // android.media.mediacodec.secure int32
+
int32_t secure = -1;
if ( item->getInt32("android.media.mediacodec.secure", &secure)) {
metrics_proto.set_secure(secure);
}
- // android.media.mediacodec.width int32
+
int32_t width = -1;
if ( item->getInt32("android.media.mediacodec.width", &width)) {
metrics_proto.set_width(width);
}
- // android.media.mediacodec.height int32
+
int32_t height = -1;
if ( item->getInt32("android.media.mediacodec.height", &height)) {
metrics_proto.set_height(height);
}
- // android.media.mediacodec.rotation-degrees int32
+
int32_t rotation = -1;
if ( item->getInt32("android.media.mediacodec.rotation-degrees", &rotation)) {
metrics_proto.set_rotation(rotation);
}
- // android.media.mediacodec.crypto int32 (although missing if not needed
+ // android.media.mediacodec.crypto int32 (although missing if not needed)
int32_t crypto = -1;
if ( item->getInt32("android.media.mediacodec.crypto", &crypto)) {
metrics_proto.set_crypto(crypto);
}
- // android.media.mediacodec.profile int32
+
int32_t profile = -1;
if ( item->getInt32("android.media.mediacodec.profile", &profile)) {
metrics_proto.set_profile(profile);
}
- // android.media.mediacodec.level int32
+
int32_t level = -1;
if ( item->getInt32("android.media.mediacodec.level", &level)) {
metrics_proto.set_level(level);
}
- // android.media.mediacodec.maxwidth int32
- int32_t maxwidth = -1;
- if ( item->getInt32("android.media.mediacodec.maxwidth", &maxwidth)) {
- metrics_proto.set_max_width(maxwidth);
+
+ int32_t max_width = -1;
+ if ( item->getInt32("android.media.mediacodec.maxwidth", &max_width)) {
+ metrics_proto.set_max_width(max_width);
}
- // android.media.mediacodec.maxheight int32
- int32_t maxheight = -1;
- if ( item->getInt32("android.media.mediacodec.maxheight", &maxheight)) {
- metrics_proto.set_max_height(maxheight);
+
+ int32_t max_height = -1;
+ if ( item->getInt32("android.media.mediacodec.maxheight", &max_height)) {
+ metrics_proto.set_max_height(max_height);
}
- // android.media.mediacodec.errcode int32
- int32_t errcode = -1;
- if ( item->getInt32("android.media.mediacodec.errcode", &errcode)) {
- metrics_proto.set_error_code(errcode);
+
+ int32_t error_code = -1;
+ if ( item->getInt32("android.media.mediacodec.errcode", &error_code)) {
+ metrics_proto.set_error_code(error_code);
}
- // android.media.mediacodec.errstate string
- std::string errstate;
- if ( item->getString("android.media.mediacodec.errstate", &errstate)) {
- metrics_proto.set_error_state(std::move(errstate));
+
+ std::string error_state;
+ if ( item->getString("android.media.mediacodec.errstate", &error_state)) {
+ metrics_proto.set_error_state(error_state);
}
- // android.media.mediacodec.latency.max int64
+
int64_t latency_max = -1;
if ( item->getInt64("android.media.mediacodec.latency.max", &latency_max)) {
metrics_proto.set_latency_max(latency_max);
}
- // android.media.mediacodec.latency.min int64
+
int64_t latency_min = -1;
if ( item->getInt64("android.media.mediacodec.latency.min", &latency_min)) {
metrics_proto.set_latency_min(latency_min);
}
- // android.media.mediacodec.latency.avg int64
+
int64_t latency_avg = -1;
if ( item->getInt64("android.media.mediacodec.latency.avg", &latency_avg)) {
metrics_proto.set_latency_avg(latency_avg);
}
- // android.media.mediacodec.latency.n int64
+
int64_t latency_count = -1;
if ( item->getInt64("android.media.mediacodec.latency.n", &latency_count)) {
metrics_proto.set_latency_count(latency_count);
}
- // android.media.mediacodec.latency.unknown int64
+
int64_t latency_unknown = -1;
if ( item->getInt64("android.media.mediacodec.latency.unknown", &latency_unknown)) {
metrics_proto.set_latency_unknown(latency_unknown);
}
- // android.media.mediacodec.queueSecureInputBufferError int32
- if (int32_t queueSecureInputBufferError = -1;
- item->getInt32("android.media.mediacodec.queueSecureInputBufferError",
- &queueSecureInputBufferError)) {
- metrics_proto.set_queue_secure_input_buffer_error(queueSecureInputBufferError);
+
+ int32_t queue_secure_input_buffer_error = -1;
+ if (item->getInt32("android.media.mediacodec.queueSecureInputBufferError",
+ &queue_secure_input_buffer_error)) {
+ metrics_proto.set_queue_secure_input_buffer_error(queue_secure_input_buffer_error);
}
- // android.media.mediacodec.queueInputBufferError int32
- if (int32_t queueInputBufferError = -1;
- item->getInt32("android.media.mediacodec.queueInputBufferError",
- &queueInputBufferError)) {
- metrics_proto.set_queue_input_buffer_error(queueInputBufferError);
+
+ int32_t queue_input_buffer_error = -1;
+ if (item->getInt32("android.media.mediacodec.queueInputBufferError",
+ &queue_input_buffer_error)) {
+ metrics_proto.set_queue_input_buffer_error(queue_input_buffer_error);
}
// android.media.mediacodec.latency.hist NOT EMITTED
- // android.media.mediacodec.bitrate_mode string
std::string bitrate_mode;
if (item->getString("android.media.mediacodec.bitrate_mode", &bitrate_mode)) {
- metrics_proto.set_bitrate_mode(std::move(bitrate_mode));
+ metrics_proto.set_bitrate_mode(bitrate_mode);
}
- // android.media.mediacodec.bitrate int32
+
int32_t bitrate = -1;
if (item->getInt32("android.media.mediacodec.bitrate", &bitrate)) {
metrics_proto.set_bitrate(bitrate);
}
- // android.media.mediacodec.lifetimeMs int64
- int64_t lifetimeMs = -1;
- if ( item->getInt64("android.media.mediacodec.lifetimeMs", &lifetimeMs)) {
- lifetimeMs = mediametrics::bucket_time_minutes(lifetimeMs);
- metrics_proto.set_lifetime_millis(lifetimeMs);
+
+ int64_t lifetime_millis = -1;
+ if (item->getInt64("android.media.mediacodec.lifetimeMs", &lifetime_millis)) {
+ lifetime_millis = mediametrics::bucket_time_minutes(lifetime_millis);
+ metrics_proto.set_lifetime_millis(lifetime_millis);
+ }
+
+ // android.media.mediacodec.channelCount
+ int32_t channelCount = -1;
+ if ( item->getInt32("android.media.mediacodec.channelCount", &channelCount)) {
+ metrics_proto.set_channel_count(channelCount);
+ }
+
+ // android.media.mediacodec.sampleRate
+ int32_t sampleRate = -1;
+ if ( item->getInt32("android.media.mediacodec.sampleRate", &sampleRate)) {
+ metrics_proto.set_sample_rate(sampleRate);
+ }
+
+ // TODO PWG may want these fuzzed up a bit to obscure some precision
+ // android.media.mediacodec.vencode.bytes
+ int64_t bytes = -1;
+ if ( item->getInt64("android.media.mediacodec.vencode.bytes", &bytes)) {
+ metrics_proto.set_video_encode_bytes(bytes);
+ }
+
+ // android.media.mediacodec.vencode.frames
+ int64_t frames = -1;
+ if ( item->getInt64("android.media.mediacodec.vencode.frames", &frames)) {
+ metrics_proto.set_video_encode_frames(frames);
+ }
+
+ // android.media.mediacodec.vencode.durationUs
+ int64_t durationUs = -1;
+ if ( item->getInt64("android.media.mediacodec.vencode.durationUs", &durationUs)) {
+ metrics_proto.set_video_encode_duration_us(durationUs);
+ }
+
+ // android.media.mediacodec.color-format
+ int32_t colorFormat = -1;
+ if ( item->getInt32("android.media.mediacodec.color-format", &colorFormat)) {
+ metrics_proto.set_color_format(colorFormat);
+ }
+
+ // android.media.mediacodec.frame-rate
+ double frameRate = -1.0;
+ if ( item->getDouble("android.media.mediacodec.frame-rate", &frameRate)) {
+ metrics_proto.set_frame_rate(frameRate);
+ }
+
+ // android.media.mediacodec.capture-rate
+ double captureRate = -1.0;
+ if ( item->getDouble("android.media.mediacodec.capture-rate", &captureRate)) {
+ metrics_proto.set_capture_rate(captureRate);
+ }
+
+ // android.media.mediacodec.operating-rate
+ double operatingRate = -1.0;
+ if ( item->getDouble("android.media.mediacodec.operating-rate", &operatingRate)) {
+ metrics_proto.set_operating_rate(operatingRate);
+ }
+
+ // android.media.mediacodec.priority
+ int32_t priority = -1;
+ if ( item->getInt32("android.media.mediacodec.priority", &priority)) {
+ metrics_proto.set_priority(priority);
+ }
+
+ // android.media.mediacodec.video-qp-i-min
+ int32_t qpIMin = -1;
+ if ( item->getInt32("android.media.mediacodec.video-qp-i-min", &qpIMin)) {
+ metrics_proto.set_video_qp_i_min(qpIMin);
+ }
+
+ // android.media.mediacodec.video-qp-i-max
+ int32_t qpIMax = -1;
+ if ( item->getInt32("android.media.mediacodec.video-qp-i-max", &qpIMax)) {
+ metrics_proto.set_video_qp_i_max(qpIMax);
+ }
+
+ // android.media.mediacodec.video-qp-p-min
+ int32_t qpPMin = -1;
+ if ( item->getInt32("android.media.mediacodec.video-qp-p-min", &qpPMin)) {
+ metrics_proto.set_video_qp_p_min(qpPMin);
+ }
+
+ // android.media.mediacodec.video-qp-p-max
+ int32_t qpPMax = -1;
+ if ( item->getInt32("android.media.mediacodec.video-qp-p-max", &qpPMax)) {
+ metrics_proto.set_video_qp_p_max(qpPMax);
+ }
+
+ // android.media.mediacodec.video-qp-b-min
+ int32_t qpBMin = -1;
+ if ( item->getInt32("android.media.mediacodec.video-qp-b-min", &qpBMin)) {
+ metrics_proto.set_video_qp_b_min(qpIMin);
+ }
+
+ // android.media.mediacodec.video-qp-b-max
+ int32_t qpBMax = -1;
+ if ( item->getInt32("android.media.mediacodec.video-qp-b-max", &qpBMax)) {
+ metrics_proto.set_video_qp_b_max(qpBMax);
+ }
+
+ // android.media.mediacodec.video.input.bytes
+ int64_t inputBytes = -1;
+ if ( item->getInt64("android.media.mediacodec.video.input.bytes", &inputBytes)) {
+ metrics_proto.set_video_input_bytes(inputBytes);
+ }
+
+ // android.media.mediacodec.video.input.frames
+ int64_t inputFrames = -1;
+ if ( item->getInt64("android.media.mediacodec.video.input.frames", &inputFrames)) {
+ metrics_proto.set_video_input_frames(inputFrames);
+ }
+
+ // android.media.mediacodec.original.bitrate
+ int32_t originalBitrate = -1;
+ if ( item->getInt32("android.media.mediacodec.original.bitrate", &originalBitrate)) {
+ metrics_proto.set_original_bitrate(originalBitrate);
}
std::string serialized;
@@ -191,18 +305,51 @@
ALOGE("Failed to serialize codec metrics");
return false;
}
+ android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
+ int result = android::util::stats_write(android::util::MEDIAMETRICS_CODEC_REPORTED,
+ timestamp_nanos, package_name.c_str(), package_version_code,
+ media_apex_version,
+ bf_serialized);
+ std::stringstream log;
+ log << "result:" << result << " {"
+ << " mediametrics_codec_reported:"
+ << android::util::MEDIAMETRICS_CODEC_REPORTED
+ << " timestamp_nanos:" << timestamp_nanos
+ << " package_name:" << package_name
+ << " package_version_code:" << package_version_code
+ << " media_apex_version:" << media_apex_version
- if (enabled_statsd) {
- android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
- (void)android::util::stats_write(android::util::MEDIAMETRICS_CODEC_REPORTED,
- timestamp, pkgName.c_str(), pkgVersionCode,
- mediaApexVersion,
- bf_serialized);
+ << " codec:" << codec
+ << " mime:" << mime
+ << " mode:" << mode
+ << " encoder:" << encoder
+ << " secure:" << secure
+ << " width:" << width
+ << " height:" << height
+ << " rotation:" << rotation
+ << " crypto:" << crypto
+ << " profile:" << profile
- } else {
- ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str()));
- }
+ << " level:" << level
+ << " max_width:" << max_width
+ << " max_height:" << max_height
+ << " error_code:" << error_code
+ << " error_state:" << error_state
+ << " latency_max:" << latency_max
+ << " latency_min:" << latency_min
+ << " latency_avg:" << latency_avg
+ << " latency_count:" << latency_count
+ << " latency_unknown:" << latency_unknown
+ << " queue_input_buffer_error:" << queue_input_buffer_error
+ << " queue_secure_input_buffer_error:" << queue_secure_input_buffer_error
+ << " bitrate_mode:" << bitrate_mode
+ << " bitrate:" << bitrate
+ << " lifetime_millis:" << lifetime_millis
+ // TODO: add when log_session_id is merged.
+ // << " log_session_id:" << log_session_id
+ << " }";
+ statsdLog->log(android::util::MEDIAMETRICS_CODEC_REPORTED, log.str());
return true;
}
diff --git a/services/mediametrics/statsd_drm.cpp b/services/mediametrics/statsd_drm.cpp
index ac58929..287fb8d 100644
--- a/services/mediametrics/statsd_drm.cpp
+++ b/services/mediametrics/statsd_drm.cpp
@@ -17,6 +17,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "statsd_drm"
#include <utils/Log.h>
+#include <media/stagefright/foundation/base64.h>
#include <stdint.h>
#include <inttypes.h>
@@ -31,90 +32,79 @@
#include <pwd.h>
#include "MediaMetricsService.h"
+#include "StringUtils.h"
#include "iface_statsd.h"
#include <statslog.h>
#include <array>
#include <string>
+#include <vector>
namespace android {
// mediadrm
-bool statsd_mediadrm(const mediametrics::Item *item)
+bool statsd_mediadrm(const std::shared_ptr<const mediametrics::Item>& item,
+ const std::shared_ptr<mediametrics::StatsdLog>& statsdLog)
{
if (item == nullptr) return false;
- const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
- std::string pkgName = item->getPkgName();
- int64_t pkgVersionCode = item->getPkgVersionCode();
- int64_t mediaApexVersion = 0;
+ const nsecs_t timestamp_nanos = MediaMetricsService::roundTime(item->getTimestamp());
+ const std::string package_name = item->getPkgName();
+ const int64_t package_version_code = item->getPkgVersionCode();
+ const int64_t media_apex_version = 0;
std::string vendor;
(void) item->getString("vendor", &vendor);
std::string description;
(void) item->getString("description", &description);
- std::string serialized_metrics;
- (void) item->getString("serialized_metrics", &serialized_metrics);
-
- if (enabled_statsd) {
- android::util::BytesField bf_serialized(serialized_metrics.c_str(),
- serialized_metrics.size());
- android::util::stats_write(android::util::MEDIAMETRICS_MEDIADRM_REPORTED,
- timestamp, pkgName.c_str(), pkgVersionCode,
- mediaApexVersion,
- vendor.c_str(),
- description.c_str(),
- bf_serialized);
- } else {
- ALOGV("NOT sending: mediadrm private data (len=%zu)", serialized_metrics.size());
- }
-
- return true;
-}
-
-// widevineCDM
-bool statsd_widevineCDM(const mediametrics::Item *item)
-{
- if (item == nullptr) return false;
-
- const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
- std::string pkgName = item->getPkgName();
- int64_t pkgVersionCode = item->getPkgVersionCode();
- int64_t mediaApexVersion = 0;
std::string serialized_metrics;
(void) item->getString("serialized_metrics", &serialized_metrics);
-
- if (enabled_statsd) {
- android::util::BytesField bf_serialized(serialized_metrics.c_str(),
- serialized_metrics.size());
- android::util::stats_write(android::util::MEDIAMETRICS_DRM_WIDEVINE_REPORTED,
- timestamp, pkgName.c_str(), pkgVersionCode,
- mediaApexVersion,
- bf_serialized);
- } else {
- ALOGV("NOT sending: widevine private data (len=%zu)", serialized_metrics.size());
+ if (serialized_metrics.empty()) {
+ ALOGD("statsd_mediadrm skipping empty entry");
+ return false;
}
+ // This field is left here for backward compatibility.
+ // This field is not used anymore.
+ const std::string kUnusedField("");
+ android::util::BytesField bf_serialized(kUnusedField.c_str(), kUnusedField.size());
+ int result = android::util::stats_write(android::util::MEDIAMETRICS_MEDIADRM_REPORTED,
+ timestamp_nanos, package_name.c_str(), package_version_code,
+ media_apex_version,
+ vendor.c_str(),
+ description.c_str(),
+ bf_serialized);
+
+ std::stringstream log;
+ log << "result:" << result << " {"
+ << " mediametrics_mediadrm_reported:"
+ << android::util::MEDIAMETRICS_MEDIADRM_REPORTED
+ << " timestamp_nanos:" << timestamp_nanos
+ << " package_name:" << package_name
+ << " package_version_code:" << package_version_code
+ << " media_apex_version:" << media_apex_version
+
+ << " vendor:" << vendor
+ << " description:" << description
+ // omitting serialized
+ << " }";
+ statsdLog->log(android::util::MEDIAMETRICS_MEDIADRM_REPORTED, log.str());
return true;
}
// drmmanager
-bool statsd_drmmanager(const mediametrics::Item *item)
+bool statsd_drmmanager(const std::shared_ptr<const mediametrics::Item>& item,
+ const std::shared_ptr<mediametrics::StatsdLog>& statsdLog)
{
using namespace std::string_literals;
if (item == nullptr) return false;
- if (!enabled_statsd) {
- ALOGV("NOT sending: drmmanager data");
- return true;
- }
-
- const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
- std::string pkgName = item->getPkgName();
- int64_t pkgVersionCode = item->getPkgVersionCode();
- int64_t mediaApexVersion = 0;
+ const nsecs_t timestamp_nanos = MediaMetricsService::roundTime(item->getTimestamp());
+ const std::string package_name = item->getPkgName();
+ const int64_t package_version_code = item->getPkgVersionCode();
+ const int64_t media_apex_version = 0;
std::string plugin_id;
(void) item->getString("plugin_id", &plugin_id);
@@ -132,8 +122,9 @@
item->getInt64(("method"s + std::to_string(i)).c_str(), &methodCounts[i]);
}
- android::util::stats_write(android::util::MEDIAMETRICS_DRMMANAGER_REPORTED,
- timestamp, pkgName.c_str(), pkgVersionCode, mediaApexVersion,
+ const int result = android::util::stats_write(android::util::MEDIAMETRICS_DRMMANAGER_REPORTED,
+ timestamp_nanos, package_name.c_str(), package_version_code,
+ media_apex_version,
plugin_id.c_str(), description.c_str(),
method_id, mime_types.c_str(),
methodCounts[0], methodCounts[1], methodCounts[2],
@@ -142,6 +133,101 @@
methodCounts[9], methodCounts[10], methodCounts[11],
methodCounts[12]);
+ std::stringstream log;
+ log << "result:" << result << " {"
+ << " mediametrics_drmmanager_reported:"
+ << android::util::MEDIAMETRICS_DRMMANAGER_REPORTED
+ << " timestamp_nanos:" << timestamp_nanos
+ << " package_name:" << package_name
+ << " package_version_code:" << package_version_code
+ << " media_apex_version:" << media_apex_version
+
+ << " plugin_id:" << plugin_id
+ << " description:" << description
+ << " method_id:" << method_id
+ << " mime_types:" << mime_types;
+
+ for (size_t i = 0; i < methodCounts.size(); ++i) {
+ log << " method_" << i << ":" << methodCounts[i];
+ }
+ log << " }";
+ statsdLog->log(android::util::MEDIAMETRICS_DRMMANAGER_REPORTED, log.str());
+ return true;
+}
+
+namespace {
+std::vector<uint8_t> base64DecodeNoPad(std::string& str) {
+ if (str.empty()) {
+ return {};
+ }
+
+ switch (str.length() % 4) {
+ case 3: str += "="; break;
+ case 2: str += "=="; break;
+ case 1: str += "==="; break;
+ case 0: /* unchanged */ break;
+ }
+
+ std::vector<uint8_t> buf(str.length() / 4 * 3, 0);
+ size_t size = buf.size();
+ if (decodeBase64(buf.data(), &size, str.c_str()) && size <= buf.size()) {
+ buf.erase(buf.begin() + size, buf.end());
+ return buf;
+ }
+ return {};
+}
+} // namespace
+
+// |out| and its contents are memory-managed by statsd.
+bool statsd_mediadrm_puller(
+ const std::shared_ptr<const mediametrics::Item>& item, AStatsEventList* out,
+ const std::shared_ptr<mediametrics::StatsdLog>& statsdLog)
+{
+ if (item == nullptr) {
+ return false;
+ }
+
+ std::string serialized_metrics;
+ (void) item->getString("serialized_metrics", &serialized_metrics);
+ const auto framework_raw(base64DecodeNoPad(serialized_metrics));
+
+ std::string plugin_metrics;
+ (void) item->getString("plugin_metrics", &plugin_metrics);
+ const auto plugin_raw(base64DecodeNoPad(plugin_metrics));
+
+ if (serialized_metrics.size() == 0 && plugin_metrics.size() == 0) {
+ ALOGD("statsd_mediadrm_puller skipping empty entry");
+ return false;
+ }
+
+ std::string vendor;
+ (void) item->getString("vendor", &vendor);
+ std::string description;
+ (void) item->getString("description", &description);
+
+ // Memory for |event| is internally managed by statsd.
+ AStatsEvent* event = AStatsEventList_addStatsEvent(out);
+ AStatsEvent_setAtomId(event, android::util::MEDIA_DRM_ACTIVITY_INFO);
+ AStatsEvent_writeString(event, item->getPkgName().c_str());
+ AStatsEvent_writeInt64(event, item->getPkgVersionCode());
+ AStatsEvent_writeString(event, vendor.c_str());
+ AStatsEvent_writeString(event, description.c_str());
+ AStatsEvent_writeByteArray(event, framework_raw.data(), framework_raw.size());
+ AStatsEvent_writeByteArray(event, plugin_raw.data(), plugin_raw.size());
+ AStatsEvent_build(event);
+
+ std::stringstream log;
+ log << "pulled:" << " {"
+ << " media_drm_activity_info:"
+ << android::util::MEDIA_DRM_ACTIVITY_INFO
+ << " package_name:" << item->getPkgName()
+ << " package_version_code:" << item->getPkgVersionCode()
+ << " vendor:" << vendor
+ << " description:" << description
+ << " framework_metrics:" << mediametrics::stringutils::bytesToString(framework_raw, 8)
+ << " vendor_metrics:" << mediametrics::stringutils::bytesToString(plugin_raw, 8)
+ << " }";
+ statsdLog->log(android::util::MEDIA_DRM_ACTIVITY_INFO, log.str());
return true;
}
diff --git a/services/mediametrics/statsd_extractor.cpp b/services/mediametrics/statsd_extractor.cpp
index 4180e0c..2378f33 100644
--- a/services/mediametrics/statsd_extractor.cpp
+++ b/services/mediametrics/statsd_extractor.cpp
@@ -37,44 +37,40 @@
namespace android {
-bool statsd_extractor(const mediametrics::Item *item)
+bool statsd_extractor(const std::shared_ptr<const mediametrics::Item>& item,
+ const std::shared_ptr<mediametrics::StatsdLog>& statsdLog)
{
if (item == nullptr) return false;
// these go into the statsd wrapper
- const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
- std::string pkgName = item->getPkgName();
- int64_t pkgVersionCode = item->getPkgVersionCode();
- int64_t mediaApexVersion = 0;
-
+ const nsecs_t timestamp_nanos = MediaMetricsService::roundTime(item->getTimestamp());
+ const std::string package_name = item->getPkgName();
+ const int64_t package_version_code = item->getPkgVersionCode();
+ const int64_t media_apex_version = 0;
// the rest into our own proto
//
::android::stats::mediametrics::ExtractorData metrics_proto;
- // flesh out the protobuf we'll hand off with our data
- //
-
- // android.media.mediaextractor.fmt string
- std::string fmt;
- if (item->getString("android.media.mediaextractor.fmt", &fmt)) {
- metrics_proto.set_format(std::move(fmt));
+ std::string format;
+ if (item->getString("android.media.mediaextractor.fmt", &format)) {
+ metrics_proto.set_format(format);
}
- // android.media.mediaextractor.mime string
+
std::string mime;
if (item->getString("android.media.mediaextractor.mime", &mime)) {
- metrics_proto.set_mime(std::move(mime));
- }
- // android.media.mediaextractor.ntrk int32
- int32_t ntrk = -1;
- if (item->getInt32("android.media.mediaextractor.ntrk", &ntrk)) {
- metrics_proto.set_tracks(ntrk);
+ metrics_proto.set_mime(mime);
}
- // android.media.mediaextractor.entry string
+ int32_t tracks = -1;
+ if (item->getInt32("android.media.mediaextractor.ntrk", &tracks)) {
+ metrics_proto.set_tracks(tracks);
+ }
+
std::string entry_point_string;
+ stats::mediametrics::ExtractorData::EntryPoint entry_point =
+ stats::mediametrics::ExtractorData_EntryPoint_OTHER;
if (item->getString("android.media.mediaextractor.entry", &entry_point_string)) {
- stats::mediametrics::ExtractorData::EntryPoint entry_point;
if (entry_point_string == "sdk") {
entry_point = stats::mediametrics::ExtractorData_EntryPoint_SDK;
} else if (entry_point_string == "ndk-with-jvm") {
@@ -87,23 +83,38 @@
metrics_proto.set_entry_point(entry_point);
}
+ std::string log_session_id;
+ if (item->getString("android.media.mediaextractor.logSessionId", &log_session_id)) {
+ metrics_proto.set_log_session_id(log_session_id);
+ }
+
std::string serialized;
if (!metrics_proto.SerializeToString(&serialized)) {
ALOGE("Failed to serialize extractor metrics");
return false;
}
- if (enabled_statsd) {
- android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
- (void)android::util::stats_write(android::util::MEDIAMETRICS_EXTRACTOR_REPORTED,
- timestamp, pkgName.c_str(), pkgVersionCode,
- mediaApexVersion,
- bf_serialized);
+ android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
+ int result = android::util::stats_write(android::util::MEDIAMETRICS_EXTRACTOR_REPORTED,
+ timestamp_nanos, package_name.c_str(), package_version_code,
+ media_apex_version,
+ bf_serialized);
+ std::stringstream log;
+ log << "result:" << result << " {"
+ << " mediametrics_extractor_reported:"
+ << android::util::MEDIAMETRICS_EXTRACTOR_REPORTED
+ << " timestamp_nanos:" << timestamp_nanos
+ << " package_name:" << package_name
+ << " package_version_code:" << package_version_code
+ << " media_apex_version:" << media_apex_version
- } else {
- ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str()));
- }
-
+ << " format:" << format
+ << " mime:" << mime
+ << " tracks:" << tracks
+ << " entry_point:" << entry_point_string << "(" << entry_point << ")"
+ << " log_session_id:" << log_session_id
+ << " }";
+ statsdLog->log(android::util::MEDIAMETRICS_EXTRACTOR_REPORTED, log.str());
return true;
}
diff --git a/services/mediametrics/statsd_mediaparser.cpp b/services/mediametrics/statsd_mediaparser.cpp
index 262b2ae..af2946b 100644
--- a/services/mediametrics/statsd_mediaparser.cpp
+++ b/services/mediametrics/statsd_mediaparser.cpp
@@ -36,16 +36,14 @@
namespace android {
-bool statsd_mediaparser(const mediametrics::Item *item)
+bool statsd_mediaparser(const std::shared_ptr<const mediametrics::Item>& item,
+ const std::shared_ptr<mediametrics::StatsdLog>& statsdLog)
{
- if (item == nullptr) {
- return false;
- }
+ if (item == nullptr) return false;
- // statsd wrapper data.
- const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
- std::string pkgName = item->getPkgName();
- int64_t pkgVersionCode = item->getPkgVersionCode();
+ const nsecs_t timestamp_nanos = MediaMetricsService::roundTime(item->getTimestamp());
+ const std::string package_name = item->getPkgName();
+ const int64_t package_version_code = item->getPkgVersionCode();
std::string parserName;
item->getString("android.media.mediaparser.parserName", &parserName);
@@ -80,26 +78,47 @@
int32_t videoHeight = -1;
item->getInt32("android.media.mediaparser.videoHeight", &videoHeight);
- if (enabled_statsd) {
- (void) android::util::stats_write(android::util::MEDIAMETRICS_MEDIAPARSER_REPORTED,
- timestamp,
- pkgName.c_str(),
- pkgVersionCode,
- parserName.c_str(),
- createdByName,
- parserPool.c_str(),
- lastException.c_str(),
- resourceByteCount,
- durationMillis,
- trackMimeTypes.c_str(),
- trackCodecs.c_str(),
- alteredParameters.c_str(),
- videoWidth,
- videoHeight);
- } else {
- ALOGV("NOT sending MediaParser media metrics.");
- }
+ std::string logSessionId;
+ item->getString("android.media.mediaparser.logSessionId", &logSessionId);
+ int result = android::util::stats_write(android::util::MEDIAMETRICS_MEDIAPARSER_REPORTED,
+ timestamp_nanos,
+ package_name.c_str(),
+ package_version_code,
+ parserName.c_str(),
+ createdByName,
+ parserPool.c_str(),
+ lastException.c_str(),
+ resourceByteCount,
+ durationMillis,
+ trackMimeTypes.c_str(),
+ trackCodecs.c_str(),
+ alteredParameters.c_str(),
+ videoWidth,
+ videoHeight,
+ logSessionId.c_str());
+
+ std::stringstream log;
+ log << "result:" << result << " {"
+ << " mediametrics_mediaparser_reported:"
+ << android::util::MEDIAMETRICS_MEDIAPARSER_REPORTED
+ << " timestamp_nanos:" << timestamp_nanos
+ << " package_name:" << package_name
+ << " package_version_code:" << package_version_code
+ << " parser_name:" << parserName
+ << " created_by_name:" << createdByName
+ << " parser_pool:" << parserPool
+ << " last_exception:" << lastException
+ << " resource_byte_count:" << resourceByteCount
+ << " duration_millis:" << durationMillis
+ << " track_mime_types:" << trackMimeTypes
+ << " track_codecs:" << trackCodecs
+ << " altered_parameters:" << alteredParameters
+ << " video_width:" << videoWidth
+ << " video_height:" << videoHeight
+ << " log_session_id:" << logSessionId
+ << " }";
+ statsdLog->log(android::util::MEDIAMETRICS_MEDIAPARSER_REPORTED, log.str());
return true;
}
diff --git a/services/mediametrics/statsd_nuplayer.cpp b/services/mediametrics/statsd_nuplayer.cpp
index a8d0f55..33da81e 100644
--- a/services/mediametrics/statsd_nuplayer.cpp
+++ b/services/mediametrics/statsd_nuplayer.cpp
@@ -41,16 +41,16 @@
* handles nuplayer AND nuplayer2
* checks for the union of what the two players generate
*/
-bool statsd_nuplayer(const mediametrics::Item *item)
+bool statsd_nuplayer(const std::shared_ptr<const mediametrics::Item>& item,
+ const std::shared_ptr<mediametrics::StatsdLog>& statsdLog)
{
if (item == nullptr) return false;
// these go into the statsd wrapper
- const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
- std::string pkgName = item->getPkgName();
- int64_t pkgVersionCode = item->getPkgVersionCode();
- int64_t mediaApexVersion = 0;
-
+ const nsecs_t timestamp_nanos = MediaMetricsService::roundTime(item->getTimestamp());
+ const std::string package_name = item->getPkgName();
+ const int64_t package_version_code = item->getPkgVersionCode();
+ const int64_t media_apex_version = 0;
// the rest into our own proto
//
@@ -60,15 +60,16 @@
//
// differentiate between nuplayer and nuplayer2
- metrics_proto.set_whichplayer(item->getKey().c_str());
+ std::string whichPlayer = item->getKey();
+ metrics_proto.set_whichplayer(whichPlayer.c_str());
std::string video_mime;
if (item->getString("android.media.mediaplayer.video.mime", &video_mime)) {
- metrics_proto.set_video_mime(std::move(video_mime));
+ metrics_proto.set_video_mime(video_mime);
}
std::string video_codec;
if (item->getString("android.media.mediaplayer.video.codec", &video_codec)) {
- metrics_proto.set_video_codec(std::move(video_codec));
+ metrics_proto.set_video_codec(video_codec);
}
int32_t width = -1;
@@ -92,32 +93,32 @@
if (item->getInt64("android.media.mediaplayer.startupdropped", &frames_dropped_startup)) {
metrics_proto.set_frames_dropped_startup(frames_dropped_startup);
}
- double fps = -1.0;
- if (item->getDouble("android.media.mediaplayer.fps", &fps)) {
- metrics_proto.set_framerate(fps);
+ double framerate = -1.0;
+ if (item->getDouble("android.media.mediaplayer.fps", &framerate)) {
+ metrics_proto.set_framerate(framerate);
}
std::string audio_mime;
if (item->getString("android.media.mediaplayer.audio.mime", &audio_mime)) {
- metrics_proto.set_audio_mime(std::move(audio_mime));
+ metrics_proto.set_audio_mime(audio_mime);
}
std::string audio_codec;
if (item->getString("android.media.mediaplayer.audio.codec", &audio_codec)) {
- metrics_proto.set_audio_codec(std::move(audio_codec));
+ metrics_proto.set_audio_codec(audio_codec);
}
- int64_t duration_ms = -1;
- if (item->getInt64("android.media.mediaplayer.durationMs", &duration_ms)) {
- metrics_proto.set_duration_millis(duration_ms);
+ int64_t duration_millis = -1;
+ if (item->getInt64("android.media.mediaplayer.durationMs", &duration_millis)) {
+ metrics_proto.set_duration_millis(duration_millis);
}
- int64_t playing_ms = -1;
- if (item->getInt64("android.media.mediaplayer.playingMs", &playing_ms)) {
- metrics_proto.set_playing_millis(playing_ms);
+ int64_t playing_millis = -1;
+ if (item->getInt64("android.media.mediaplayer.playingMs", &playing_millis)) {
+ metrics_proto.set_playing_millis(playing_millis);
}
- int32_t err = -1;
- if (item->getInt32("android.media.mediaplayer.err", &err)) {
- metrics_proto.set_error(err);
+ int32_t error = -1;
+ if (item->getInt32("android.media.mediaplayer.err", &error)) {
+ metrics_proto.set_error(error);
}
int32_t error_code = -1;
if (item->getInt32("android.media.mediaplayer.errcode", &error_code)) {
@@ -125,45 +126,74 @@
}
std::string error_state;
if (item->getString("android.media.mediaplayer.errstate", &error_state)) {
- metrics_proto.set_error_state(std::move(error_state));
+ metrics_proto.set_error_state(error_state);
}
std::string data_source_type;
if (item->getString("android.media.mediaplayer.dataSource", &data_source_type)) {
- metrics_proto.set_data_source_type(std::move(data_source_type));
+ metrics_proto.set_data_source_type(data_source_type);
}
- int64_t rebufferingMs = -1;
- if (item->getInt64("android.media.mediaplayer.rebufferingMs", &rebufferingMs)) {
- metrics_proto.set_rebuffering_millis(rebufferingMs);
+ int64_t rebuffering_millis = -1;
+ if (item->getInt64("android.media.mediaplayer.rebufferingMs", &rebuffering_millis)) {
+ metrics_proto.set_rebuffering_millis(rebuffering_millis);
}
int32_t rebuffers = -1;
if (item->getInt32("android.media.mediaplayer.rebuffers", &rebuffers)) {
metrics_proto.set_rebuffers(rebuffers);
}
- int32_t rebufferExit = -1;
- if (item->getInt32("android.media.mediaplayer.rebufferExit", &rebufferExit)) {
- metrics_proto.set_rebuffer_at_exit(rebufferExit);
+ int32_t rebuffer_at_exit = -1;
+ if (item->getInt32("android.media.mediaplayer.rebufferExit", &rebuffer_at_exit)) {
+ metrics_proto.set_rebuffer_at_exit(rebuffer_at_exit);
}
-
std::string serialized;
if (!metrics_proto.SerializeToString(&serialized)) {
ALOGE("Failed to serialize nuplayer metrics");
return false;
}
- if (enabled_statsd) {
- android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
- (void)android::util::stats_write(android::util::MEDIAMETRICS_NUPLAYER_REPORTED,
- timestamp, pkgName.c_str(), pkgVersionCode,
- mediaApexVersion,
- bf_serialized);
+ android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
+ int result = android::util::stats_write(android::util::MEDIAMETRICS_NUPLAYER_REPORTED,
+ timestamp_nanos, package_name.c_str(), package_version_code,
+ media_apex_version,
+ bf_serialized);
- } else {
- ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str()));
- }
+ std::stringstream log;
+ log << "result:" << result << " {"
+ << " mediametrics_nuplayer_reported:"
+ << android::util::MEDIAMETRICS_NUPLAYER_REPORTED
+ << " timestamp_nanos:" << timestamp_nanos
+ << " package_name:" << package_name
+ << " package_version_code:" << package_version_code
+ << " media_apex_version:" << media_apex_version
+ << " whichPlayer:" << whichPlayer
+ << " video_mime:" << video_mime
+ << " video_codec:" << video_codec
+ << " width:" << width
+ << " height:" << height
+ << " frames:" << frames
+ << " frames_dropped:" << frames_dropped
+ << " framerate:" << framerate
+ << " audio_mime:" << audio_mime
+ << " audio_codec:" << media_apex_version
+
+ << " duration_millis:" << duration_millis
+ << " playing_millis:" << playing_millis
+ << " error:" << error
+ << " error_code:" << error_code
+ << " error_state:" << error_state
+ << " data_source_type:" << data_source_type
+ << " rebuffering_millis:" << rebuffering_millis
+ << " rebuffers:" << rebuffers
+ << " rebuffer_at_exit:" << rebuffer_at_exit
+ << " frames_dropped_startup:" << frames_dropped_startup
+
+ // TODO NuPlayer - add log_session_id
+ // << " log_session_id:" << log_session_id
+ << " }";
+ statsdLog->log(android::util::MEDIAMETRICS_NUPLAYER_REPORTED, log.str());
return true;
}
diff --git a/services/mediametrics/statsd_recorder.cpp b/services/mediametrics/statsd_recorder.cpp
index 2e5ada4..6edad7c 100644
--- a/services/mediametrics/statsd_recorder.cpp
+++ b/services/mediametrics/statsd_recorder.cpp
@@ -32,48 +32,53 @@
#include <statslog.h>
#include "MediaMetricsService.h"
-#include "frameworks/proto_logging/stats/enums/stats/mediametrics/mediametrics.pb.h"
+#include "frameworks/proto_logging/stats/message/mediametrics_message.pb.h"
#include "iface_statsd.h"
namespace android {
-bool statsd_recorder(const mediametrics::Item *item)
+bool statsd_recorder(const std::shared_ptr<const mediametrics::Item>& item,
+ const std::shared_ptr<mediametrics::StatsdLog>& statsdLog)
{
if (item == nullptr) return false;
// these go into the statsd wrapper
- const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
- std::string pkgName = item->getPkgName();
- int64_t pkgVersionCode = item->getPkgVersionCode();
- int64_t mediaApexVersion = 0;
-
+ const nsecs_t timestamp_nanos = MediaMetricsService::roundTime(item->getTimestamp());
+ const std::string package_name = item->getPkgName();
+ const int64_t package_version_code = item->getPkgVersionCode();
+ const int64_t media_apex_version = 0;
// the rest into our own proto
//
- ::android::stats::mediametrics::RecorderData metrics_proto;
+ ::android::stats::mediametrics_message::RecorderData metrics_proto;
// flesh out the protobuf we'll hand off with our data
//
+ // string kRecorderLogSessionId = "android.media.mediarecorder.log-session-id";
+ std::string log_session_id;
+ if (item->getString("android.media.mediarecorder.log_session_id", &log_session_id)) {
+ metrics_proto.set_log_session_id(std::move(log_session_id));
+ }
// string kRecorderAudioMime = "android.media.mediarecorder.audio.mime";
std::string audio_mime;
if (item->getString("android.media.mediarecorder.audio.mime", &audio_mime)) {
- metrics_proto.set_audio_mime(std::move(audio_mime));
+ metrics_proto.set_audio_mime(audio_mime);
}
// string kRecorderVideoMime = "android.media.mediarecorder.video.mime";
std::string video_mime;
if (item->getString("android.media.mediarecorder.video.mime", &video_mime)) {
- metrics_proto.set_video_mime(std::move(video_mime));
+ metrics_proto.set_video_mime(video_mime);
}
// int32 kRecorderVideoProfile = "android.media.mediarecorder.video-encoder-profile";
- int32_t videoProfile = -1;
- if (item->getInt32("android.media.mediarecorder.video-encoder-profile", &videoProfile)) {
- metrics_proto.set_video_profile(videoProfile);
+ int32_t video_profile = -1;
+ if (item->getInt32("android.media.mediarecorder.video-encoder-profile", &video_profile)) {
+ metrics_proto.set_video_profile(video_profile);
}
// int32 kRecorderVideoLevel = "android.media.mediarecorder.video-encoder-level";
- int32_t videoLevel = -1;
- if (item->getInt32("android.media.mediarecorder.video-encoder-level", &videoLevel)) {
- metrics_proto.set_video_level(videoLevel);
+ int32_t video_level = -1;
+ if (item->getInt32("android.media.mediarecorder.video-encoder-level", &video_level)) {
+ metrics_proto.set_video_level(video_level);
}
// int32 kRecorderWidth = "android.media.mediarecorder.width";
int32_t width = -1;
@@ -97,73 +102,73 @@
}
// int32 kRecorderCaptureFps = "android.media.mediarecorder.capture-fps";
- int32_t captureFps = -1;
- if (item->getInt32("android.media.mediarecorder.capture-fps", &captureFps)) {
- metrics_proto.set_capture_fps(captureFps);
+ int32_t capture_fps = -1;
+ if (item->getInt32("android.media.mediarecorder.capture-fps", &capture_fps)) {
+ metrics_proto.set_capture_fps(capture_fps);
}
// double kRecorderCaptureFpsEnable = "android.media.mediarecorder.capture-fpsenable";
- double captureFpsEnable = -1;
- if (item->getDouble("android.media.mediarecorder.capture-fpsenable", &captureFpsEnable)) {
- metrics_proto.set_capture_fps_enable(captureFpsEnable);
+ double capture_fps_enable = -1;
+ if (item->getDouble("android.media.mediarecorder.capture-fpsenable", &capture_fps_enable)) {
+ metrics_proto.set_capture_fps_enable(capture_fps_enable);
}
// int64 kRecorderDurationMs = "android.media.mediarecorder.durationMs";
- int64_t durationMs = -1;
- if (item->getInt64("android.media.mediarecorder.durationMs", &durationMs)) {
- metrics_proto.set_duration_millis(durationMs);
+ int64_t duration_millis = -1;
+ if (item->getInt64("android.media.mediarecorder.durationMs", &duration_millis)) {
+ metrics_proto.set_duration_millis(duration_millis);
}
// int64 kRecorderPaused = "android.media.mediarecorder.pausedMs";
- int64_t pausedMs = -1;
- if (item->getInt64("android.media.mediarecorder.pausedMs", &pausedMs)) {
- metrics_proto.set_paused_millis(pausedMs);
+ int64_t paused_millis = -1;
+ if (item->getInt64("android.media.mediarecorder.pausedMs", &paused_millis)) {
+ metrics_proto.set_paused_millis(paused_millis);
}
// int32 kRecorderNumPauses = "android.media.mediarecorder.NPauses";
- int32_t pausedCount = -1;
- if (item->getInt32("android.media.mediarecorder.NPauses", &pausedCount)) {
- metrics_proto.set_paused_count(pausedCount);
+ int32_t paused_count = -1;
+ if (item->getInt32("android.media.mediarecorder.NPauses", &paused_count)) {
+ metrics_proto.set_paused_count(paused_count);
}
// int32 kRecorderAudioBitrate = "android.media.mediarecorder.audio-bitrate";
- int32_t audioBitrate = -1;
- if (item->getInt32("android.media.mediarecorder.audio-bitrate", &audioBitrate)) {
- metrics_proto.set_audio_bitrate(audioBitrate);
+ int32_t audio_bitrate = -1;
+ if (item->getInt32("android.media.mediarecorder.audio-bitrate", &audio_bitrate)) {
+ metrics_proto.set_audio_bitrate(audio_bitrate);
}
// int32 kRecorderAudioChannels = "android.media.mediarecorder.audio-channels";
- int32_t audioChannels = -1;
- if (item->getInt32("android.media.mediarecorder.audio-channels", &audioChannels)) {
- metrics_proto.set_audio_channels(audioChannels);
+ int32_t audio_channels = -1;
+ if (item->getInt32("android.media.mediarecorder.audio-channels", &audio_channels)) {
+ metrics_proto.set_audio_channels(audio_channels);
}
// int32 kRecorderAudioSampleRate = "android.media.mediarecorder.audio-samplerate";
- int32_t audioSampleRate = -1;
- if (item->getInt32("android.media.mediarecorder.audio-samplerate", &audioSampleRate)) {
- metrics_proto.set_audio_samplerate(audioSampleRate);
+ int32_t audio_samplerate = -1;
+ if (item->getInt32("android.media.mediarecorder.audio-samplerate", &audio_samplerate)) {
+ metrics_proto.set_audio_samplerate(audio_samplerate);
}
// int32 kRecorderMovieTimescale = "android.media.mediarecorder.movie-timescale";
- int32_t movieTimescale = -1;
- if (item->getInt32("android.media.mediarecorder.movie-timescale", &movieTimescale)) {
- metrics_proto.set_movie_timescale(movieTimescale);
+ int32_t movie_timescale = -1;
+ if (item->getInt32("android.media.mediarecorder.movie-timescale", &movie_timescale)) {
+ metrics_proto.set_movie_timescale(movie_timescale);
}
// int32 kRecorderAudioTimescale = "android.media.mediarecorder.audio-timescale";
- int32_t audioTimescale = -1;
- if (item->getInt32("android.media.mediarecorder.audio-timescale", &audioTimescale)) {
- metrics_proto.set_audio_timescale(audioTimescale);
+ int32_t audio_timescale = -1;
+ if (item->getInt32("android.media.mediarecorder.audio-timescale", &audio_timescale)) {
+ metrics_proto.set_audio_timescale(audio_timescale);
}
// int32 kRecorderVideoTimescale = "android.media.mediarecorder.video-timescale";
- int32_t videoTimescale = -1;
- if (item->getInt32("android.media.mediarecorder.video-timescale", &videoTimescale)) {
- metrics_proto.set_video_timescale(videoTimescale);
+ int32_t video_timescale = -1;
+ if (item->getInt32("android.media.mediarecorder.video-timescale", &video_timescale)) {
+ metrics_proto.set_video_timescale(video_timescale);
}
// int32 kRecorderVideoBitrate = "android.media.mediarecorder.video-bitrate";
- int32_t videoBitRate = -1;
- if (item->getInt32("android.media.mediarecorder.video-bitrate", &videoBitRate)) {
- metrics_proto.set_video_bitrate(videoBitRate);
+ int32_t video_bitrate = -1;
+ if (item->getInt32("android.media.mediarecorder.video-bitrate", &video_bitrate)) {
+ metrics_proto.set_video_bitrate(video_bitrate);
}
// int32 kRecorderVideoIframeInterval = "android.media.mediarecorder.video-iframe-interval";
- int32_t iFrameInterval = -1;
- if (item->getInt32("android.media.mediarecorder.video-iframe-interval", &iFrameInterval)) {
- metrics_proto.set_iframe_interval(iFrameInterval);
+ int32_t iframe_interval = -1;
+ if (item->getInt32("android.media.mediarecorder.video-iframe-interval", &iframe_interval)) {
+ metrics_proto.set_iframe_interval(iframe_interval);
}
std::string serialized;
@@ -172,17 +177,47 @@
return false;
}
- if (enabled_statsd) {
- android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
- (void)android::util::stats_write(android::util::MEDIAMETRICS_RECORDER_REPORTED,
- timestamp, pkgName.c_str(), pkgVersionCode,
- mediaApexVersion,
- bf_serialized);
+ android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
+ int result = android::util::stats_write(android::util::MEDIAMETRICS_RECORDER_REPORTED,
+ timestamp_nanos, package_name.c_str(), package_version_code,
+ media_apex_version,
+ bf_serialized);
+ std::stringstream log;
+ log << "result:" << result << " {"
+ << " mediametrics_recorder_reported:"
+ << android::util::MEDIAMETRICS_RECORDER_REPORTED
+ << " timestamp_nanos:" << timestamp_nanos
+ << " package_name:" << package_name
+ << " package_version_code:" << package_version_code
+ << " media_apex_version:" << media_apex_version
- } else {
- ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str()));
- }
+ << " audio_mime:" << audio_mime
+ << " video_mime:" << video_mime
+ << " video_profile:" << video_profile
+ << " video_level:" << video_level
+ << " width:" << width
+ << " height:" << height
+ << " rotation:" << rotation
+ << " framerate:" << framerate
+ << " capture_fps:" << capture_fps
+ << " capture_fps_enable:" << capture_fps_enable
+ << " duration_millis:" << duration_millis
+ << " paused_millis:" << paused_millis
+ << " paused_count:" << paused_count
+ << " audio_bitrate:" << audio_bitrate
+ << " audio_channels:" << audio_channels
+ << " audio_samplerate:" << audio_samplerate
+ << " movie_timescale:" << movie_timescale
+ << " audio_timescale:" << audio_timescale
+ << " video_timescale:" << video_timescale
+ << " video_bitrate:" << video_bitrate
+
+ << " iframe_interval:" << iframe_interval
+ // TODO Recorder - add log_session_id
+ // << " log_session_id:" << log_session_id
+ << " }";
+ statsdLog->log(android::util::MEDIAMETRICS_RECORDER_REPORTED, log.str());
return true;
}
diff --git a/services/mediametrics/tests/Android.bp b/services/mediametrics/tests/Android.bp
index 3aee209..3baf739 100644
--- a/services/mediametrics/tests/Android.bp
+++ b/services/mediametrics/tests/Android.bp
@@ -11,6 +11,10 @@
name: "mediametrics_tests",
test_suites: ["device-tests"],
+ // not all shared libraries are populated in the 2nd architecture in
+ // particular, libmediametricsservice we use to have a tame copy of the service
+ compile_multilib: "first",
+
cflags: [
"-Wall",
"-Werror",
@@ -28,6 +32,7 @@
"libmediametricsservice",
"libmediautils",
"libutils",
+ "mediametricsservice-aidl-cpp",
],
header_libs: [
diff --git a/services/mediametrics/tests/mediametrics_tests.cpp b/services/mediametrics/tests/mediametrics_tests.cpp
index 478355b..2336d6f 100644
--- a/services/mediametrics/tests/mediametrics_tests.cpp
+++ b/services/mediametrics/tests/mediametrics_tests.cpp
@@ -809,7 +809,9 @@
(*item3).set("four", (int32_t)4)
.setTimestamp(12);
- android::mediametrics::AudioAnalytics audioAnalytics;
+ std::shared_ptr<mediametrics::StatsdLog> statsdLog =
+ std::make_shared<mediametrics::StatsdLog>(10);
+ android::mediametrics::AudioAnalytics audioAnalytics{statsdLog};
// untrusted entities cannot create a new key.
ASSERT_EQ(PERMISSION_DENIED, audioAnalytics.submit(item, false /* isTrusted */));
@@ -817,7 +819,7 @@
// TODO: Verify contents of AudioAnalytics.
// Currently there is no getter API in AudioAnalytics besides dump.
- ASSERT_EQ(11, audioAnalytics.dump(1000).second /* lines */);
+ ASSERT_EQ(10, audioAnalytics.dump(1000).second /* lines */);
ASSERT_EQ(NO_ERROR, audioAnalytics.submit(item, true /* isTrusted */));
// untrusted entities can add to an existing key
@@ -845,7 +847,9 @@
(*item3).set("four", (int32_t)4)
.setTimestamp(12);
- android::mediametrics::AudioAnalytics audioAnalytics;
+ std::shared_ptr<mediametrics::StatsdLog> statsdLog =
+ std::make_shared<mediametrics::StatsdLog>(10);
+ android::mediametrics::AudioAnalytics audioAnalytics{statsdLog};
// untrusted entities cannot create a new key.
ASSERT_EQ(PERMISSION_DENIED, audioAnalytics.submit(item, false /* isTrusted */));
@@ -853,7 +857,7 @@
// TODO: Verify contents of AudioAnalytics.
// Currently there is no getter API in AudioAnalytics besides dump.
- ASSERT_EQ(11, audioAnalytics.dump(1000).second /* lines */);
+ ASSERT_EQ(10, audioAnalytics.dump(1000).second /* lines */);
ASSERT_EQ(NO_ERROR, audioAnalytics.submit(item, true /* isTrusted */));
// untrusted entities can add to an existing key
@@ -877,7 +881,9 @@
(*item3).set("four", (int32_t)4)
.setTimestamp(12);
- android::mediametrics::AudioAnalytics audioAnalytics;
+ std::shared_ptr<mediametrics::StatsdLog> statsdLog =
+ std::make_shared<mediametrics::StatsdLog>(10);
+ android::mediametrics::AudioAnalytics audioAnalytics{statsdLog};
ASSERT_EQ(NO_ERROR, audioAnalytics.submit(item, true /* isTrusted */));
// untrusted entities can add to an existing key
@@ -1082,3 +1088,42 @@
//mediaMetrics->dump(fileno(stdout), {} /* args */);
}
#endif
+
+// Base64Url and isLogSessionId string utilities can be tested by static asserts.
+static_assert(mediametrics::stringutils::isBase64Url("abc"));
+static_assert(mediametrics::stringutils::InverseBase64UrlTable['A'] == 0);
+static_assert(mediametrics::stringutils::InverseBase64UrlTable['a'] == 26);
+static_assert(mediametrics::stringutils::InverseBase64UrlTable['!'] ==
+ mediametrics::stringutils::Transpose::INVALID_CHAR);
+static_assert(mediametrics::stringutils::InverseBase64UrlTable['@'] ==
+ mediametrics::stringutils::Transpose::INVALID_CHAR);
+static_assert(mediametrics::stringutils::InverseBase64UrlTable['#'] ==
+ mediametrics::stringutils::Transpose::INVALID_CHAR);
+static_assert(!mediametrics::stringutils::isBase64Url("!@#"));
+
+static_assert(mediametrics::stringutils::isLogSessionId("0123456789abcdef"));
+static_assert(!mediametrics::stringutils::isLogSessionId("abc"));
+static_assert(!mediametrics::stringutils::isLogSessionId("!@#"));
+static_assert(!mediametrics::stringutils::isLogSessionId("0123456789abcde!"));
+
+TEST(mediametrics_tests, sanitizeLogSessionId) {
+ // invalid id returns empty string.
+ ASSERT_EQ("", mediametrics::stringutils::sanitizeLogSessionId("abc"));
+
+ // valid id passes through.
+ std::string validId = "fedcba9876543210";
+ ASSERT_EQ(validId, mediametrics::stringutils::sanitizeLogSessionId(validId));
+
+ // one more char makes the id invalid
+ ASSERT_EQ("", mediametrics::stringutils::sanitizeLogSessionId(validId + "A"));
+
+ std::string validId2 = "ZYXWVUT123456789";
+ ASSERT_EQ(validId2, mediametrics::stringutils::sanitizeLogSessionId(validId2));
+
+ // one fewer char makes the id invalid
+ ASSERT_EQ("", mediametrics::stringutils::sanitizeLogSessionId(validId.c_str() + 1));
+
+ // replacing one character with an invalid character makes an invalid id.
+ validId2[3] = '!';
+ ASSERT_EQ("", mediametrics::stringutils::sanitizeLogSessionId(validId2));
+}
diff --git a/services/mediaresourcemanager/Android.bp b/services/mediaresourcemanager/Android.bp
index e67720c..db61061 100644
--- a/services/mediaresourcemanager/Android.bp
+++ b/services/mediaresourcemanager/Android.bp
@@ -7,11 +7,72 @@
default_applicable_licenses: ["frameworks_av_license"],
}
+filegroup {
+ name: "resourcemanager_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",
+ ],
+ path: "aidl",
+}
+
+filegroup {
+ name: "resourceobserver_aidl",
+ srcs: [
+ "aidl/android/media/IResourceObserver.aidl",
+ "aidl/android/media/IResourceObserverService.aidl",
+ "aidl/android/media/MediaObservableEvent.aidl",
+ "aidl/android/media/MediaObservableFilter.aidl",
+ "aidl/android/media/MediaObservableType.aidl",
+ "aidl/android/media/MediaObservableParcel.aidl",
+ ],
+ path: "aidl",
+}
+
+aidl_interface {
+ name: "resourcemanager_aidl_interface",
+ unstable: true,
+ local_include_dir: "aidl",
+ srcs: [
+ ":resourcemanager_aidl",
+ ],
+}
+
+aidl_interface {
+ name: "resourceobserver_aidl_interface",
+ local_include_dir: "aidl",
+ srcs: [
+ ":resourceobserver_aidl",
+ ],
+ backend: {
+ java: {
+ enabled: false,
+ },
+ cpp: {
+ enabled: false,
+ },
+ ndk: {
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media",
+ "test_com.android.media",
+ ],
+ min_sdk_version: "29",
+ enabled: true,
+ },
+ },
+}
+
cc_library {
name: "libresourcemanagerservice",
srcs: [
"ResourceManagerService.cpp",
+ "ResourceObserverService.cpp",
"ServiceLog.cpp",
// TODO: convert to AIDL?
@@ -27,6 +88,10 @@
"liblog",
],
+ static_libs: [
+ "resourceobserver_aidl_interface-V1-ndk_platform",
+ ],
+
include_dirs: ["frameworks/av/include"],
cflags: [
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index 1695228..953686b 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -37,18 +37,54 @@
#include "IMediaResourceMonitor.h"
#include "ResourceManagerService.h"
+#include "ResourceObserverService.h"
#include "ServiceLog.h"
namespace android {
+//static
+std::mutex ResourceManagerService::sCookieLock;
+//static
+uintptr_t ResourceManagerService::sCookieCounter = 0;
+//static
+std::map<uintptr_t, sp<DeathNotifier> > ResourceManagerService::sCookieToDeathNotifierMap;
+
+class DeathNotifier : public RefBase {
+public:
+ DeathNotifier(const std::shared_ptr<ResourceManagerService> &service,
+ int pid, int64_t clientId);
+
+ virtual ~DeathNotifier() {}
+
+ // Implement death recipient
+ static void BinderDiedCallback(void* cookie);
+ virtual void binderDied();
+
+protected:
+ std::weak_ptr<ResourceManagerService> mService;
+ int mPid;
+ int64_t mClientId;
+};
+
DeathNotifier::DeathNotifier(const std::shared_ptr<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();
+ sp<DeathNotifier> notifier;
+ {
+ std::scoped_lock lock{ResourceManagerService::sCookieLock};
+ auto it = ResourceManagerService::sCookieToDeathNotifierMap.find(
+ reinterpret_cast<uintptr_t>(cookie));
+ if (it == ResourceManagerService::sCookieToDeathNotifierMap.end()) {
+ return;
+ }
+ notifier = it->second;
+ }
+ if (notifier.get() != nullptr) {
+ notifier->binderDied();
+ }
}
void DeathNotifier::binderDied() {
@@ -61,8 +97,28 @@
service->overridePid(mPid, -1);
// thiz is freed in the call below, so it must be last call referring thiz
- service->removeResource(mPid, mClientId, false);
+ service->removeResource(mPid, mClientId, false /*checkValid*/);
+}
+class OverrideProcessInfoDeathNotifier : public DeathNotifier {
+public:
+ OverrideProcessInfoDeathNotifier(const std::shared_ptr<ResourceManagerService> &service,
+ int pid) : DeathNotifier(service, pid, 0) {}
+
+ virtual ~OverrideProcessInfoDeathNotifier() {}
+
+ virtual void binderDied();
+};
+
+void OverrideProcessInfoDeathNotifier::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;
+ }
+
+ service->removeProcessInfoOverride(mPid);
}
template <typename T>
@@ -117,6 +173,7 @@
info.uid = uid;
info.clientId = clientId;
info.client = client;
+ info.cookie = 0;
info.pendingRemoval = false;
index = infos.add(clientId, info);
@@ -268,6 +325,13 @@
if (status != STATUS_OK) {
return;
}
+
+ std::shared_ptr<ResourceObserverService> observerService =
+ ResourceObserverService::instantiate();
+
+ if (observerService != nullptr) {
+ service->setObserverService(observerService);
+ }
// 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.
@@ -276,6 +340,11 @@
ResourceManagerService::~ResourceManagerService() {}
+void ResourceManagerService::setObserverService(
+ const std::shared_ptr<ResourceObserverService>& observerService) {
+ mObserverService = observerService;
+}
+
Status ResourceManagerService::config(const std::vector<MediaResourcePolicyParcel>& policies) {
String8 log = String8::format("config(%s)", getString(policies).string());
mServiceLog->add(log);
@@ -354,11 +423,16 @@
Mutex::Autolock lock(mLock);
if (!mProcessInfo->isValidPid(pid)) {
- ALOGE("Rejected addResource call with invalid pid.");
- return Status::fromServiceSpecificError(BAD_VALUE);
+ pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ ALOGW("%s called with untrusted pid %d, using calling pid %d, uid %d", __FUNCTION__,
+ pid, callingPid, callingUid);
+ pid = callingPid;
+ uid = callingUid;
}
ResourceInfos& infos = getResourceInfosForEdit(pid, mMap);
ResourceInfo& info = getResourceInfoForEdit(uid, clientId, client, infos);
+ ResourceList resourceAdded;
for (size_t i = 0; i < resources.size(); ++i) {
const auto &res = resources[i];
@@ -380,11 +454,20 @@
} else {
mergeResources(info.resources[resType], res);
}
+ // Add it to the list of added resources for observers.
+ auto it = resourceAdded.find(resType);
+ if (it == resourceAdded.end()) {
+ resourceAdded[resType] = res;
+ } else {
+ mergeResources(it->second, res);
+ }
}
- if (info.deathNotifier == nullptr && client != nullptr) {
- info.deathNotifier = new DeathNotifier(ref<ResourceManagerService>(), pid, clientId);
- AIBinder_linkToDeath(client->asBinder().get(),
- mDeathRecipient.get(), info.deathNotifier.get());
+ if (info.cookie == 0 && client != nullptr) {
+ info.cookie = addCookieAndLink_l(client->asBinder(),
+ new DeathNotifier(ref<ResourceManagerService>(), pid, clientId));
+ }
+ if (mObserverService != nullptr && !resourceAdded.empty()) {
+ mObserverService->onResourceAdded(uid, pid, resourceAdded);
}
notifyResourceGranted(pid, resources);
return Status::ok();
@@ -399,8 +482,10 @@
Mutex::Autolock lock(mLock);
if (!mProcessInfo->isValidPid(pid)) {
- ALOGE("Rejected removeResource call with invalid pid.");
- return Status::fromServiceSpecificError(BAD_VALUE);
+ pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ ALOGW("%s called with untrusted pid %d, using calling pid %d", __FUNCTION__,
+ pid, callingPid);
+ pid = callingPid;
}
ssize_t index = mMap.indexOfKey(pid);
if (index < 0) {
@@ -416,7 +501,7 @@
}
ResourceInfo &info = infos.editValueAt(index);
-
+ ResourceList resourceRemoved;
for (size_t i = 0; i < resources.size(); ++i) {
const auto &res = resources[i];
const auto resType = std::tuple(res.type, res.subType, res.id);
@@ -428,19 +513,32 @@
// ignore if we don't have it
if (info.resources.find(resType) != info.resources.end()) {
MediaResourceParcel &resource = info.resources[resType];
+ MediaResourceParcel actualRemoved = res;
if (resource.value > res.value) {
resource.value -= res.value;
} else {
onLastRemoved(res, info);
+ actualRemoved.value = resource.value;
info.resources.erase(resType);
}
+
+ // Add it to the list of removed resources for observers.
+ auto it = resourceRemoved.find(resType);
+ if (it == resourceRemoved.end()) {
+ resourceRemoved[resType] = actualRemoved;
+ } else {
+ mergeResources(it->second, actualRemoved);
+ }
}
}
+ if (mObserverService != nullptr && !resourceRemoved.empty()) {
+ mObserverService->onResourceRemoved(info.uid, pid, resourceRemoved);
+ }
return Status::ok();
}
Status ResourceManagerService::removeClient(int32_t pid, int64_t clientId) {
- removeResource(pid, clientId, true);
+ removeResource(pid, clientId, true /*checkValid*/);
return Status::ok();
}
@@ -452,8 +550,10 @@
Mutex::Autolock lock(mLock);
if (checkValid && !mProcessInfo->isValidPid(pid)) {
- ALOGE("Rejected removeResource call with invalid pid.");
- return Status::fromServiceSpecificError(BAD_VALUE);
+ pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ ALOGW("%s called with untrusted pid %d, using calling pid %d", __FUNCTION__,
+ pid, callingPid);
+ pid = callingPid;
}
ssize_t index = mMap.indexOfKey(pid);
if (index < 0) {
@@ -473,8 +573,11 @@
onLastRemoved(it->second, info);
}
- AIBinder_unlinkToDeath(info.client->asBinder().get(),
- mDeathRecipient.get(), info.deathNotifier.get());
+ removeCookieAndUnlink_l(info.client->asBinder(), info.cookie);
+
+ if (mObserverService != nullptr && !info.resources.empty()) {
+ mObserverService->onResourceRemoved(info.uid, pid, info.resources);
+ }
infos.removeItemsAt(index);
return Status::ok();
@@ -505,8 +608,10 @@
{
Mutex::Autolock lock(mLock);
if (!mProcessInfo->isValidPid(callingPid)) {
- ALOGE("Rejected reclaimResource call with invalid callingPid.");
- return Status::fromServiceSpecificError(BAD_VALUE);
+ pid_t actualCallingPid = IPCThreadState::self()->getCallingPid();
+ ALOGW("%s called with untrusted pid %d, using actual calling pid %d", __FUNCTION__,
+ callingPid, actualCallingPid);
+ callingPid = actualCallingPid;
}
const MediaResourceParcel *secureCodec = NULL;
const MediaResourceParcel *nonSecureCodec = NULL;
@@ -657,6 +762,83 @@
return Status::ok();
}
+Status ResourceManagerService::overrideProcessInfo(
+ const std::shared_ptr<IResourceManagerClient>& client,
+ int pid,
+ int procState,
+ int oomScore) {
+ String8 log = String8::format("overrideProcessInfo(pid %d, procState %d, oomScore %d)",
+ pid, procState, oomScore);
+ mServiceLog->add(log);
+
+ // Only allow the override if the caller already can access process state and oom scores.
+ int callingPid = AIBinder_getCallingPid();
+ if (callingPid != getpid() && (callingPid != pid || !checkCallingPermission(String16(
+ "android.permission.GET_PROCESS_STATE_AND_OOM_SCORE")))) {
+ ALOGE("Permission Denial: overrideProcessInfo method from pid=%d", callingPid);
+ return Status::fromServiceSpecificError(PERMISSION_DENIED);
+ }
+
+ if (client == nullptr) {
+ return Status::fromServiceSpecificError(BAD_VALUE);
+ }
+
+ Mutex::Autolock lock(mLock);
+ removeProcessInfoOverride_l(pid);
+
+ if (!mProcessInfo->overrideProcessInfo(pid, procState, oomScore)) {
+ // Override value is rejected by ProcessInfo.
+ return Status::fromServiceSpecificError(BAD_VALUE);
+ }
+
+ uintptr_t cookie = addCookieAndLink_l(client->asBinder(),
+ new OverrideProcessInfoDeathNotifier(ref<ResourceManagerService>(), pid));
+
+ mProcessInfoOverrideMap.emplace(pid, ProcessInfoOverride{cookie, client});
+
+ return Status::ok();
+}
+
+uintptr_t ResourceManagerService::addCookieAndLink_l(
+ ::ndk::SpAIBinder binder, const sp<DeathNotifier>& notifier) {
+ std::scoped_lock lock{sCookieLock};
+
+ uintptr_t cookie;
+ // Need to skip cookie 0 (if it wraps around). ResourceInfo has cookie initialized to 0
+ // indicating the death notifier is not created yet.
+ while ((cookie = ++sCookieCounter) == 0);
+ AIBinder_linkToDeath(binder.get(), mDeathRecipient.get(), (void*)cookie);
+ sCookieToDeathNotifierMap.emplace(cookie, notifier);
+
+ return cookie;
+}
+
+void ResourceManagerService::removeCookieAndUnlink_l(
+ ::ndk::SpAIBinder binder, uintptr_t cookie) {
+ std::scoped_lock lock{sCookieLock};
+ AIBinder_unlinkToDeath(binder.get(), mDeathRecipient.get(), (void*)cookie);
+ sCookieToDeathNotifierMap.erase(cookie);
+}
+
+void ResourceManagerService::removeProcessInfoOverride(int pid) {
+ Mutex::Autolock lock(mLock);
+
+ removeProcessInfoOverride_l(pid);
+}
+
+void ResourceManagerService::removeProcessInfoOverride_l(int pid) {
+ auto it = mProcessInfoOverrideMap.find(pid);
+ if (it == mProcessInfoOverrideMap.end()) {
+ return;
+ }
+
+ mProcessInfo->removeProcessInfoOverride(pid);
+
+ removeCookieAndUnlink_l(it->second.client->asBinder(), it->second.cookie);
+
+ mProcessInfoOverrideMap.erase(pid);
+}
+
Status ResourceManagerService::markClientForPendingRemoval(int32_t pid, int64_t clientId) {
String8 log = String8::format(
"markClientForPendingRemoval(pid %d, clientId %lld)",
@@ -665,8 +847,10 @@
Mutex::Autolock lock(mLock);
if (!mProcessInfo->isValidPid(pid)) {
- ALOGE("Rejected markClientForPendingRemoval call with invalid pid.");
- return Status::fromServiceSpecificError(BAD_VALUE);
+ pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ ALOGW("%s called with untrusted pid %d, using calling pid %d", __FUNCTION__,
+ pid, callingPid);
+ pid = callingPid;
}
ssize_t index = mMap.indexOfKey(pid);
if (index < 0) {
@@ -695,8 +879,10 @@
{
Mutex::Autolock lock(mLock);
if (!mProcessInfo->isValidPid(pid)) {
- ALOGE("Rejected reclaimResourcesFromClientsPendingRemoval call with invalid pid.");
- return Status::fromServiceSpecificError(BAD_VALUE);
+ pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ ALOGW("%s called with untrusted pid %d, using calling pid %d", __FUNCTION__,
+ pid, callingPid);
+ pid = callingPid;
}
for (MediaResource::Type type : {MediaResource::Type::kSecureCodec,
diff --git a/services/mediaresourcemanager/ResourceManagerService.h b/services/mediaresourcemanager/ResourceManagerService.h
index 7f18ed3..9c2636e 100644
--- a/services/mediaresourcemanager/ResourceManagerService.h
+++ b/services/mediaresourcemanager/ResourceManagerService.h
@@ -19,6 +19,7 @@
#define ANDROID_MEDIA_RESOURCEMANAGERSERVICE_H
#include <map>
+#include <mutex>
#include <aidl/android/media/BnResourceManagerService.h>
#include <arpa/inet.h>
@@ -33,6 +34,7 @@
class DeathNotifier;
class ResourceManagerService;
+class ResourceObserverService;
class ServiceLog;
struct ProcessInfoInterface;
@@ -50,7 +52,7 @@
int64_t clientId;
uid_t uid;
std::shared_ptr<IResourceManagerClient> client;
- sp<DeathNotifier> deathNotifier;
+ uintptr_t cookie{0};
ResourceList resources;
bool pendingRemoval{false};
};
@@ -59,22 +61,6 @@
typedef KeyedVector<int64_t, ResourceInfo> ResourceInfos;
typedef KeyedVector<int, ResourceInfos> PidResourceInfosMap;
-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 {
@@ -95,6 +81,8 @@
const sp<ProcessInfoInterface> &processInfo,
const sp<SystemCallbackInterface> &systemResource);
virtual ~ResourceManagerService();
+ void setObserverService(
+ const std::shared_ptr<ResourceObserverService>& observerService);
// IResourceManagerService interface
Status config(const std::vector<MediaResourcePolicyParcel>& policies) override;
@@ -125,6 +113,12 @@
int originalPid,
int newPid) override;
+ Status overrideProcessInfo(
+ const std::shared_ptr<IResourceManagerClient>& client,
+ int pid,
+ int procState,
+ int oomScore) override;
+
Status markClientForPendingRemoval(int32_t pid, int64_t clientId) override;
Status reclaimResourcesFromClientsPendingRemoval(int32_t pid) override;
@@ -133,6 +127,8 @@
private:
friend class ResourceManagerServiceTest;
+ friend class DeathNotifier;
+ friend class OverrideProcessInfoDeathNotifier;
// Reclaims resources from |clients|. Returns true if reclaim succeeded
// for all clients.
@@ -177,6 +173,12 @@
// Get priority from process's pid
bool getPriority_l(int pid, int* priority);
+ void removeProcessInfoOverride(int pid);
+
+ void removeProcessInfoOverride_l(int pid);
+ uintptr_t addCookieAndLink_l(::ndk::SpAIBinder binder, const sp<DeathNotifier>& notifier);
+ void removeCookieAndUnlink_l(::ndk::SpAIBinder binder, uintptr_t cookie);
+
mutable Mutex mLock;
sp<ProcessInfoInterface> mProcessInfo;
sp<SystemCallbackInterface> mSystemCB;
@@ -186,7 +188,17 @@
bool mSupportsSecureWithNonSecureCodec;
int32_t mCpuBoostCount;
::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
+ struct ProcessInfoOverride {
+ uintptr_t cookie;
+ std::shared_ptr<IResourceManagerClient> client;
+ };
std::map<int, int> mOverridePidMap;
+ std::map<pid_t, ProcessInfoOverride> mProcessInfoOverrideMap;
+ static std::mutex sCookieLock;
+ static uintptr_t sCookieCounter GUARDED_BY(sCookieLock);
+ static std::map<uintptr_t, sp<DeathNotifier> > sCookieToDeathNotifierMap
+ GUARDED_BY(sCookieLock);
+ std::shared_ptr<ResourceObserverService> mObserverService;
};
// ----------------------------------------------------------------------------
diff --git a/services/mediaresourcemanager/ResourceObserverService.cpp b/services/mediaresourcemanager/ResourceObserverService.cpp
new file mode 100644
index 0000000..4e97406
--- /dev/null
+++ b/services/mediaresourcemanager/ResourceObserverService.cpp
@@ -0,0 +1,328 @@
+/**
+ *
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ResourceObserverService"
+#include <utils/Log.h>
+
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <binder/IServiceManager.h>
+#include <utils/String16.h>
+#include <aidl/android/media/MediaResourceParcel.h>
+
+#include "ResourceObserverService.h"
+
+namespace android {
+
+using ::aidl::android::media::MediaResourceParcel;
+using ::aidl::android::media::MediaObservableEvent;
+
+// MediaObservableEvent will be used as uint64_t flags.
+static_assert(sizeof(MediaObservableEvent) == sizeof(uint64_t));
+
+static std::vector<MediaObservableEvent> sEvents = {
+ MediaObservableEvent::kBusy,
+ MediaObservableEvent::kIdle,
+};
+
+static MediaObservableType getObservableType(const MediaResourceParcel& res) {
+ if (res.subType == MediaResourceSubType::kVideoCodec) {
+ if (res.type == MediaResourceType::kNonSecureCodec) {
+ return MediaObservableType::kVideoNonSecureCodec;
+ }
+ if (res.type == MediaResourceType::kSecureCodec) {
+ return MediaObservableType::kVideoSecureCodec;
+ }
+ }
+ return MediaObservableType::kInvalid;
+}
+
+//static
+std::mutex ResourceObserverService::sDeathRecipientLock;
+//static
+std::map<uintptr_t, std::shared_ptr<ResourceObserverService::DeathRecipient> >
+ResourceObserverService::sDeathRecipientMap;
+
+struct ResourceObserverService::DeathRecipient {
+ DeathRecipient(ResourceObserverService* _service,
+ const std::shared_ptr<IResourceObserver>& _observer)
+ : service(_service), observer(_observer) {}
+ ~DeathRecipient() {}
+
+ void binderDied() {
+ if (service != nullptr) {
+ service->unregisterObserver(observer);
+ }
+ }
+
+ ResourceObserverService* service;
+ std::shared_ptr<IResourceObserver> observer;
+};
+
+// static
+void ResourceObserverService::BinderDiedCallback(void* cookie) {
+ uintptr_t id = reinterpret_cast<uintptr_t>(cookie);
+
+ ALOGW("Observer %lld is dead", (long long)id);
+
+ std::shared_ptr<DeathRecipient> recipient;
+
+ {
+ std::scoped_lock lock{sDeathRecipientLock};
+
+ auto it = sDeathRecipientMap.find(id);
+ if (it != sDeathRecipientMap.end()) {
+ recipient = it->second;
+ }
+ }
+
+ if (recipient != nullptr) {
+ recipient->binderDied();
+ }
+}
+
+//static
+std::shared_ptr<ResourceObserverService> ResourceObserverService::instantiate() {
+ std::shared_ptr<ResourceObserverService> observerService =
+ ::ndk::SharedRefBase::make<ResourceObserverService>();
+ binder_status_t status = AServiceManager_addService(observerService->asBinder().get(),
+ ResourceObserverService::getServiceName());
+ if (status != STATUS_OK) {
+ return nullptr;
+ }
+ return observerService;
+}
+
+ResourceObserverService::ResourceObserverService()
+ : mDeathRecipient(AIBinder_DeathRecipient_new(BinderDiedCallback)) {}
+
+binder_status_t ResourceObserverService::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",
+ AIBinder_getCallingPid(),
+ AIBinder_getCallingUid());
+ write(fd, result.string(), result.size());
+ return PERMISSION_DENIED;
+ }
+
+ result.appendFormat("ResourceObserverService: %p\n", this);
+ result.appendFormat(" Registered Observers: %zu\n", mObserverInfoMap.size());
+
+ {
+ std::scoped_lock lock{mObserverLock};
+
+ for (auto &observer : mObserverInfoMap) {
+ result.appendFormat(" Observer %p:\n", observer.second.binder.get());
+ for (auto &observable : observer.second.filters) {
+ String8 enabledEventsStr;
+ for (auto &event : sEvents) {
+ if (((uint64_t)observable.eventFilter & (uint64_t)event) != 0) {
+ if (!enabledEventsStr.isEmpty()) {
+ enabledEventsStr.append("|");
+ }
+ enabledEventsStr.append(toString(event).c_str());
+ }
+ }
+ result.appendFormat(" %s: %s\n",
+ toString(observable.type).c_str(), enabledEventsStr.c_str());
+ }
+ }
+ }
+
+ write(fd, result.string(), result.size());
+ return OK;
+}
+
+Status ResourceObserverService::registerObserver(
+ const std::shared_ptr<IResourceObserver>& in_observer,
+ const std::vector<MediaObservableFilter>& in_filters) {
+ if ((getpid() != AIBinder_getCallingPid()) &&
+ checkCallingPermission(
+ String16("android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER")) == false) {
+ ALOGE("Permission Denial: "
+ "can't registerObserver from pid=%d, uid=%d\n",
+ AIBinder_getCallingPid(),
+ AIBinder_getCallingUid());
+ return Status::fromServiceSpecificError(PERMISSION_DENIED);
+ }
+
+ if (in_observer == nullptr) {
+ return Status::fromServiceSpecificError(BAD_VALUE);
+ }
+
+ ::ndk::SpAIBinder binder = in_observer->asBinder();
+
+ {
+ std::scoped_lock lock{mObserverLock};
+
+ if (mObserverInfoMap.find((uintptr_t)binder.get()) != mObserverInfoMap.end()) {
+ return Status::fromServiceSpecificError(ALREADY_EXISTS);
+ }
+
+ if (in_filters.empty()) {
+ return Status::fromServiceSpecificError(BAD_VALUE);
+ }
+
+ // Add observer info.
+ mObserverInfoMap.emplace((uintptr_t)binder.get(),
+ ObserverInfo{binder, in_observer, in_filters});
+
+ // Add observer to observable->subscribers map.
+ for (auto &filter : in_filters) {
+ for (auto &event : sEvents) {
+ if (!((uint64_t)filter.eventFilter & (uint64_t)event)) {
+ continue;
+ }
+ MediaObservableFilter key{filter.type, event};
+ mObservableToSubscribersMap[key].emplace((uintptr_t)binder.get(), in_observer);
+ }
+ }
+ }
+
+ // Add death binder and link.
+ uintptr_t cookie = (uintptr_t)binder.get();
+ {
+ std::scoped_lock lock{sDeathRecipientLock};
+ sDeathRecipientMap.emplace(
+ cookie, std::make_shared<DeathRecipient>(this, in_observer));
+ }
+
+ AIBinder_linkToDeath(binder.get(), mDeathRecipient.get(),
+ reinterpret_cast<void*>(cookie));
+
+ return Status::ok();
+}
+
+Status ResourceObserverService::unregisterObserver(
+ const std::shared_ptr<IResourceObserver>& in_observer) {
+ if ((getpid() != AIBinder_getCallingPid()) &&
+ checkCallingPermission(
+ String16("android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER")) == false) {
+ ALOGE("Permission Denial: "
+ "can't unregisterObserver from pid=%d, uid=%d\n",
+ AIBinder_getCallingPid(),
+ AIBinder_getCallingUid());
+ return Status::fromServiceSpecificError(PERMISSION_DENIED);
+ }
+
+ if (in_observer == nullptr) {
+ return Status::fromServiceSpecificError(BAD_VALUE);
+ }
+
+ ::ndk::SpAIBinder binder = in_observer->asBinder();
+
+ {
+ std::scoped_lock lock{mObserverLock};
+
+ auto it = mObserverInfoMap.find((uintptr_t)binder.get());
+ if (it == mObserverInfoMap.end()) {
+ return Status::fromServiceSpecificError(NAME_NOT_FOUND);
+ }
+
+ // Remove observer from observable->subscribers map.
+ for (auto &filter : it->second.filters) {
+ for (auto &event : sEvents) {
+ if (!((uint64_t)filter.eventFilter & (uint64_t)event)) {
+ continue;
+ }
+ MediaObservableFilter key{filter.type, event};
+ mObservableToSubscribersMap[key].erase((uintptr_t)binder.get());
+
+ //Remove the entry if there's no more subscribers.
+ if (mObservableToSubscribersMap[key].empty()) {
+ mObservableToSubscribersMap.erase(key);
+ }
+ }
+ }
+
+ // Remove observer info.
+ mObserverInfoMap.erase(it);
+ }
+
+ // Unlink and remove death binder.
+ uintptr_t cookie = (uintptr_t)binder.get();
+ AIBinder_unlinkToDeath(binder.get(), mDeathRecipient.get(),
+ reinterpret_cast<void*>(cookie));
+
+ {
+ std::scoped_lock lock{sDeathRecipientLock};
+ sDeathRecipientMap.erase(cookie);
+ }
+
+ return Status::ok();
+}
+
+void ResourceObserverService::notifyObservers(
+ MediaObservableEvent event, int uid, int pid, const ResourceList &resources) {
+ struct CalleeInfo {
+ std::shared_ptr<IResourceObserver> observer;
+ std::vector<MediaObservableParcel> monitors;
+ };
+ // Build a consolidated list of observers to call with their respective observables.
+ std::map<uintptr_t, CalleeInfo> calleeList;
+
+ {
+ std::scoped_lock lock{mObserverLock};
+
+ for (auto &res : resources) {
+ // Skip if this resource doesn't map to any observable type.
+ MediaObservableType observableType = getObservableType(res.second);
+ if (observableType == MediaObservableType::kInvalid) {
+ continue;
+ }
+ MediaObservableFilter key{observableType, event};
+ // Skip if no one subscribed to this observable.
+ auto observableIt = mObservableToSubscribersMap.find(key);
+ if (observableIt == mObservableToSubscribersMap.end()) {
+ continue;
+ }
+ // Loop through all subsribers.
+ for (auto &subscriber : observableIt->second) {
+ auto calleeIt = calleeList.find(subscriber.first);
+ if (calleeIt == calleeList.end()) {
+ calleeList.emplace(subscriber.first, CalleeInfo{
+ subscriber.second, {{observableType, res.second.value}}});
+ } else {
+ calleeIt->second.monitors.push_back({observableType, res.second.value});
+ }
+ }
+ }
+ }
+
+ // Finally call the observers about the status change.
+ for (auto &calleeInfo : calleeList) {
+ calleeInfo.second.observer->onStatusChanged(
+ event, uid, pid, calleeInfo.second.monitors);
+ }
+}
+
+void ResourceObserverService::onResourceAdded(
+ int uid, int pid, const ResourceList &resources) {
+ notifyObservers(MediaObservableEvent::kBusy, uid, pid, resources);
+}
+
+void ResourceObserverService::onResourceRemoved(
+ int uid, int pid, const ResourceList &resources) {
+ notifyObservers(MediaObservableEvent::kIdle, uid, pid, resources);
+}
+
+} // namespace android
diff --git a/services/mediaresourcemanager/ResourceObserverService.h b/services/mediaresourcemanager/ResourceObserverService.h
new file mode 100644
index 0000000..46bc5fb
--- /dev/null
+++ b/services/mediaresourcemanager/ResourceObserverService.h
@@ -0,0 +1,95 @@
+/**
+ *
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_RESOURCE_OBSERVER_SERVICE_H
+#define ANDROID_MEDIA_RESOURCE_OBSERVER_SERVICE_H
+
+#include <map>
+
+#include <aidl/android/media/BnResourceObserverService.h>
+#include "ResourceManagerService.h"
+
+namespace android {
+
+using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::media::BnResourceObserverService;
+using ::aidl::android::media::IResourceObserver;
+using ::aidl::android::media::MediaObservableFilter;
+using ::aidl::android::media::MediaObservableParcel;
+using ::aidl::android::media::MediaObservableType;
+using ::aidl::android::media::MediaObservableEvent;
+
+class ResourceObserverService : public BnResourceObserverService {
+public:
+
+ static char const *getServiceName() { return "media.resource_observer"; }
+ static std::shared_ptr<ResourceObserverService> instantiate();
+
+ virtual inline binder_status_t dump(
+ int /*fd*/, const char** /*args*/, uint32_t /*numArgs*/);
+
+ ResourceObserverService();
+ virtual ~ResourceObserverService() {}
+
+ // IResourceObserverService interface
+ Status registerObserver(const std::shared_ptr<IResourceObserver>& in_observer,
+ const std::vector<MediaObservableFilter>& in_filters) override;
+
+ Status unregisterObserver(const std::shared_ptr<IResourceObserver>& in_observer) override;
+ // ~IResourceObserverService interface
+
+ // Called by ResourceManagerService when resources are added.
+ void onResourceAdded(int uid, int pid, const ResourceList &resources);
+
+ // Called by ResourceManagerService when resources are removed.
+ void onResourceRemoved(int uid, int pid, const ResourceList &resources);
+
+private:
+ struct ObserverInfo {
+ ::ndk::SpAIBinder binder;
+ std::shared_ptr<IResourceObserver> observer;
+ std::vector<MediaObservableFilter> filters;
+ };
+ struct DeathRecipient;
+
+ // Below maps are all keyed on the observer's binder ptr value.
+ using ObserverInfoMap = std::map<uintptr_t, ObserverInfo>;
+ using SubscriberMap = std::map<uintptr_t, std::shared_ptr<IResourceObserver>>;
+
+ std::mutex mObserverLock;
+ // Binder->ObserverInfo
+ ObserverInfoMap mObserverInfoMap GUARDED_BY(mObserverLock);
+ // Observable(<type,event>)->Subscribers
+ std::map<MediaObservableFilter, SubscriberMap> mObservableToSubscribersMap
+ GUARDED_BY(mObserverLock);
+
+ ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
+
+ // Binder death handling.
+ static std::mutex sDeathRecipientLock;
+ static std::map<uintptr_t, std::shared_ptr<DeathRecipient>> sDeathRecipientMap
+ GUARDED_BY(sDeathRecipientLock);
+ static void BinderDiedCallback(void* cookie);
+
+ void notifyObservers(MediaObservableEvent event,
+ int uid, int pid, const ResourceList &resources);
+};
+
+// ----------------------------------------------------------------------------
+} // namespace android
+
+#endif // ANDROID_MEDIA_RESOURCE_OBSERVER_SERVICE_H
diff --git a/services/mediaresourcemanager/TEST_MAPPING b/services/mediaresourcemanager/TEST_MAPPING
index 418b159..52ad441 100644
--- a/services/mediaresourcemanager/TEST_MAPPING
+++ b/services/mediaresourcemanager/TEST_MAPPING
@@ -5,6 +5,9 @@
},
{
"name": "ServiceLog_test"
+ },
+ {
+ "name": "ResourceObserverService_test"
}
]
}
diff --git a/media/libmedia/aidl/android/media/IResourceManagerClient.aidl b/services/mediaresourcemanager/aidl/android/media/IResourceManagerClient.aidl
similarity index 100%
rename from media/libmedia/aidl/android/media/IResourceManagerClient.aidl
rename to services/mediaresourcemanager/aidl/android/media/IResourceManagerClient.aidl
diff --git a/services/mediaresourcemanager/aidl/android/media/IResourceManagerService.aidl b/services/mediaresourcemanager/aidl/android/media/IResourceManagerService.aidl
new file mode 100644
index 0000000..7a0a50f
--- /dev/null
+++ b/services/mediaresourcemanager/aidl/android/media/IResourceManagerService.aidl
@@ -0,0 +1,134 @@
+/**
+ * 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);
+
+ /**
+ * Override the pid of original calling process with the pid of the process
+ * who actually use the requested resources.
+ *
+ * @param originalPid pid of the original calling process.
+ * @param newPid pid of the actual process who use the resources.
+ * remove existing override on originalPid if newPid is -1.
+ */
+ void overridePid(int originalPid, int newPid);
+
+ /**
+ * Override the process state and OOM score of the calling process with the
+ * the specified values. This is used by native service processes to specify
+ * these values for ResourceManagerService to use. ResourceManagerService usually
+ * gets these values from ActivityManagerService, however, ActivityManagerService
+ * doesn't track native service processes.
+ *
+ * @param client a token for the ResourceManagerService to link to the caller and
+ * receive notification if it goes away. This is needed for clearing
+ * the overrides.
+ * @param pid pid of the calling process.
+ * @param procState the process state value that ResourceManagerService should
+ * use for this pid.
+ * @param oomScore the oom score value that ResourceManagerService should
+ * use for this pid.
+ */
+ void overrideProcessInfo(
+ IResourceManagerClient client,
+ int pid,
+ int procState,
+ int oomScore);
+
+ /**
+ * Mark a client for pending removal
+ *
+ * @param pid pid from which the client's resources will be removed.
+ * @param clientId clientId within the pid that will be removed.
+ */
+ void markClientForPendingRemoval(int pid, long clientId);
+
+ /**
+ * Reclaim resources from clients pending removal, if any.
+ *
+ * @param pid pid from which resources will be reclaimed.
+ */
+ void reclaimResourcesFromClientsPendingRemoval(int pid);
+}
diff --git a/services/mediaresourcemanager/aidl/android/media/IResourceObserver.aidl b/services/mediaresourcemanager/aidl/android/media/IResourceObserver.aidl
new file mode 100644
index 0000000..462009a
--- /dev/null
+++ b/services/mediaresourcemanager/aidl/android/media/IResourceObserver.aidl
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.MediaObservableEvent;
+import android.media.MediaObservableParcel;
+
+/**
+ * IResourceObserver interface for receiving observable resource updates
+ * from IResourceObserverService.
+ *
+ * {@hide}
+ */
+interface IResourceObserver {
+ /**
+ * Called when an observed resource is granted to a client.
+ *
+ * @param event the status change that happened to the resource.
+ * @param uid uid to which the resource is associated.
+ * @param pid pid to which the resource is associated.
+ * @param observables the resources whose status has changed.
+ */
+ oneway void onStatusChanged(MediaObservableEvent event,
+ int uid, int pid, in MediaObservableParcel[] observables);
+}
diff --git a/services/mediaresourcemanager/aidl/android/media/IResourceObserverService.aidl b/services/mediaresourcemanager/aidl/android/media/IResourceObserverService.aidl
new file mode 100644
index 0000000..08f4ca0
--- /dev/null
+++ b/services/mediaresourcemanager/aidl/android/media/IResourceObserverService.aidl
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.IResourceObserver;
+import android.media.MediaObservableFilter;
+
+/**
+ * IResourceObserverService interface for registering an IResourceObserver
+ * callback to receive status updates about observable media resources.
+ *
+ * {@hide}
+ */
+interface IResourceObserverService {
+
+ /**
+ * Register an observer on the IResourceObserverService to receive
+ * status updates for observable resources.
+ *
+ * @param observer the observer to register.
+ * @param filters an array of filters for resources and events to receive
+ * updates for.
+ */
+ void registerObserver(
+ IResourceObserver observer,
+ in MediaObservableFilter[] filters);
+
+ /**
+ * Unregister an observer from the IResourceObserverService.
+ * The observer will stop receiving the status updates.
+ *
+ * @param observer the observer to unregister.
+ */
+ void unregisterObserver(IResourceObserver observer);
+}
diff --git a/services/mediaresourcemanager/aidl/android/media/MediaObservableEvent.aidl b/services/mediaresourcemanager/aidl/android/media/MediaObservableEvent.aidl
new file mode 100644
index 0000000..56ab24d
--- /dev/null
+++ b/services/mediaresourcemanager/aidl/android/media/MediaObservableEvent.aidl
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * Enums for media observable events.
+ *
+ * These values are used as bitmasks to indicate the events that the
+ * observer is interested in in the MediaObservableFilter objects passed to
+ * IResourceObserverService::registerObserver().
+ *
+ * {@hide}
+ */
+@Backing(type="long")
+enum MediaObservableEvent {
+ /**
+ * A media resource is granted to a client and becomes busy.
+ */
+ kBusy = 1,
+
+ /**
+ * A media resource is released by a client and becomes idle.
+ */
+ kIdle = 2,
+
+ /**
+ * A bitmask that covers all observable events defined.
+ */
+ kAll = ~0,
+}
diff --git a/services/mediaresourcemanager/aidl/android/media/MediaObservableFilter.aidl b/services/mediaresourcemanager/aidl/android/media/MediaObservableFilter.aidl
new file mode 100644
index 0000000..38f7e39
--- /dev/null
+++ b/services/mediaresourcemanager/aidl/android/media/MediaObservableFilter.aidl
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.MediaObservableType;
+import android.media.MediaObservableEvent;
+
+/**
+ * Description of an observable resource and its associated events that the
+ * observer is interested in.
+ *
+ * {@hide}
+ */
+parcelable MediaObservableFilter {
+ /**
+ * Type of the observable media resource.
+ */
+ MediaObservableType type;
+
+ /**
+ * Events that the observer is interested in.
+ *
+ * This field is a bitwise-OR of the events in MediaObservableEvent. If a
+ * particular event's bit is set, it means that updates should be sent for
+ * that event. For example, if the observer is only interested in receiving
+ * updates when a resource becomes available, it should only set 'kIdle'.
+ */
+ MediaObservableEvent eventFilter;
+}
diff --git a/services/mediaresourcemanager/aidl/android/media/MediaObservableParcel.aidl b/services/mediaresourcemanager/aidl/android/media/MediaObservableParcel.aidl
new file mode 100644
index 0000000..c4233e1
--- /dev/null
+++ b/services/mediaresourcemanager/aidl/android/media/MediaObservableParcel.aidl
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.MediaObservableType;
+
+/**
+ * Description of an observable resource whose status has changed.
+ *
+ * {@hide}
+ */
+parcelable MediaObservableParcel {
+ /**
+ * Type of the observable media resource.
+ */
+ MediaObservableType type;// = MediaObservableType::kInvalid;
+
+ /**
+ * Number of units of the observable resource (number of codecs, bytes of
+ * graphic memory, etc.).
+ */
+ long value = 0;
+}
diff --git a/services/mediaresourcemanager/aidl/android/media/MediaObservableType.aidl b/services/mediaresourcemanager/aidl/android/media/MediaObservableType.aidl
new file mode 100644
index 0000000..ed202da
--- /dev/null
+++ b/services/mediaresourcemanager/aidl/android/media/MediaObservableType.aidl
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * Type enums of observable media resources.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+enum MediaObservableType {
+ kInvalid = 0,
+
+ //kVideoStart = 1000,
+ kVideoSecureCodec = 1000,
+ kVideoNonSecureCodec = 1001,
+
+ //kAudioStart = 2000,
+
+ //kGraphicMemory = 3000,
+}
diff --git a/media/libmedia/aidl/android/media/MediaResourceParcel.aidl b/services/mediaresourcemanager/aidl/android/media/MediaResourceParcel.aidl
similarity index 100%
rename from media/libmedia/aidl/android/media/MediaResourceParcel.aidl
rename to services/mediaresourcemanager/aidl/android/media/MediaResourceParcel.aidl
diff --git a/media/libmedia/aidl/android/media/MediaResourcePolicyParcel.aidl b/services/mediaresourcemanager/aidl/android/media/MediaResourcePolicyParcel.aidl
similarity index 100%
rename from media/libmedia/aidl/android/media/MediaResourcePolicyParcel.aidl
rename to services/mediaresourcemanager/aidl/android/media/MediaResourcePolicyParcel.aidl
diff --git a/media/libmedia/aidl/android/media/MediaResourceSubType.aidl b/services/mediaresourcemanager/aidl/android/media/MediaResourceSubType.aidl
similarity index 100%
rename from media/libmedia/aidl/android/media/MediaResourceSubType.aidl
rename to services/mediaresourcemanager/aidl/android/media/MediaResourceSubType.aidl
diff --git a/media/libmedia/aidl/android/media/MediaResourceType.aidl b/services/mediaresourcemanager/aidl/android/media/MediaResourceType.aidl
similarity index 100%
rename from media/libmedia/aidl/android/media/MediaResourceType.aidl
rename to services/mediaresourcemanager/aidl/android/media/MediaResourceType.aidl
diff --git a/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/current/android/media/IResourceObserver.aidl b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/current/android/media/IResourceObserver.aidl
new file mode 100644
index 0000000..402704b
--- /dev/null
+++ b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/current/android/media/IResourceObserver.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+interface IResourceObserver {
+ oneway void onStatusChanged(android.media.MediaObservableEvent event, int uid, int pid, in android.media.MediaObservableParcel[] observables);
+}
diff --git a/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/current/android/media/IResourceObserverService.aidl b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/current/android/media/IResourceObserverService.aidl
new file mode 100644
index 0000000..a0ef761
--- /dev/null
+++ b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/current/android/media/IResourceObserverService.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+interface IResourceObserverService {
+ void registerObserver(android.media.IResourceObserver observer, in android.media.MediaObservableFilter[] filters);
+ void unregisterObserver(android.media.IResourceObserver observer);
+}
diff --git a/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/current/android/media/MediaObservableEvent.aidl b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/current/android/media/MediaObservableEvent.aidl
new file mode 100644
index 0000000..fd60bea
--- /dev/null
+++ b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/current/android/media/MediaObservableEvent.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+@Backing(type="long")
+enum MediaObservableEvent {
+ kBusy = 1,
+ kIdle = 2,
+ kAll = -1,
+}
diff --git a/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/current/android/media/MediaObservableFilter.aidl b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/current/android/media/MediaObservableFilter.aidl
new file mode 100644
index 0000000..4045ae0
--- /dev/null
+++ b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/current/android/media/MediaObservableFilter.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+parcelable MediaObservableFilter {
+ android.media.MediaObservableType type;
+ android.media.MediaObservableEvent eventFilter;
+}
diff --git a/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/current/android/media/MediaObservableParcel.aidl b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/current/android/media/MediaObservableParcel.aidl
new file mode 100644
index 0000000..ff5a8e8
--- /dev/null
+++ b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/current/android/media/MediaObservableParcel.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+parcelable MediaObservableParcel {
+ android.media.MediaObservableType type;
+ long value = 0;
+}
diff --git a/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/current/android/media/MediaObservableType.aidl b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/current/android/media/MediaObservableType.aidl
new file mode 100644
index 0000000..ebb8d8e
--- /dev/null
+++ b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/current/android/media/MediaObservableType.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a 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;
+/* @hide */
+@Backing(type="int")
+enum MediaObservableType {
+ kInvalid = 0,
+ kVideoSecureCodec = 1000,
+ kVideoNonSecureCodec = 1001,
+}
diff --git a/services/mediaresourcemanager/test/Android.bp b/services/mediaresourcemanager/test/Android.bp
index 0c5b8a2..ec4ba58 100644
--- a/services/mediaresourcemanager/test/Android.bp
+++ b/services/mediaresourcemanager/test/Android.bp
@@ -49,3 +49,28 @@
"-Wall",
],
}
+
+cc_test {
+ name: "ResourceObserverService_test",
+ srcs: ["ResourceObserverService_test.cpp"],
+ test_suites: ["device-tests"],
+ static_libs: [
+ "libresourcemanagerservice",
+ "resourceobserver_aidl_interface-V1-ndk_platform",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libbinder_ndk",
+ "liblog",
+ "libmedia",
+ "libutils",
+ ],
+ include_dirs: [
+ "frameworks/av/include",
+ "frameworks/av/services/mediaresourcemanager",
+ ],
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+}
diff --git a/services/mediaresourcemanager/test/ResourceManagerServiceTestUtils.h b/services/mediaresourcemanager/test/ResourceManagerServiceTestUtils.h
new file mode 100644
index 0000000..8e29312
--- /dev/null
+++ b/services/mediaresourcemanager/test/ResourceManagerServiceTestUtils.h
@@ -0,0 +1,215 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include "ResourceManagerService.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 android {
+
+using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::media::BnResourceManagerClient;
+using ::aidl::android::media::IResourceManagerService;
+using ::aidl::android::media::IResourceManagerClient;
+using ::aidl::android::media::MediaResourceParcel;
+
+static int64_t getId(const std::shared_ptr<IResourceManagerClient>& client) {
+ return (int64_t) client.get();
+}
+
+struct TestProcessInfo : public ProcessInfoInterface {
+ TestProcessInfo() {}
+ virtual ~TestProcessInfo() {}
+
+ virtual bool getPriority(int pid, int *priority) {
+ // For testing, use pid as priority.
+ // Lower the value higher the priority.
+ *priority = pid;
+ return true;
+ }
+
+ virtual bool isValidPid(int /* pid */) {
+ return true;
+ }
+
+ virtual bool overrideProcessInfo(
+ int /* pid */, int /* procState */, int /* oomScore */) {
+ return true;
+ }
+
+ virtual void removeProcessInfoOverride(int /* pid */) {
+ }
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(TestProcessInfo);
+};
+
+struct TestSystemCallback :
+ public ResourceManagerService::SystemCallbackInterface {
+ TestSystemCallback() :
+ mLastEvent({EventType::INVALID, 0}), mEventCount(0) {}
+
+ enum EventType {
+ INVALID = -1,
+ VIDEO_ON = 0,
+ VIDEO_OFF = 1,
+ VIDEO_RESET = 2,
+ CPUSET_ENABLE = 3,
+ CPUSET_DISABLE = 4,
+ };
+
+ struct EventEntry {
+ EventType type;
+ int arg;
+ };
+
+ virtual void noteStartVideo(int uid) override {
+ mLastEvent = {EventType::VIDEO_ON, uid};
+ mEventCount++;
+ }
+
+ virtual void noteStopVideo(int uid) override {
+ mLastEvent = {EventType::VIDEO_OFF, uid};
+ mEventCount++;
+ }
+
+ virtual void noteResetVideo() override {
+ mLastEvent = {EventType::VIDEO_RESET, 0};
+ mEventCount++;
+ }
+
+ virtual bool requestCpusetBoost(bool enable) override {
+ mLastEvent = {enable ? EventType::CPUSET_ENABLE : EventType::CPUSET_DISABLE, 0};
+ mEventCount++;
+ return true;
+ }
+
+ size_t eventCount() { return mEventCount; }
+ EventType lastEventType() { return mLastEvent.type; }
+ EventEntry lastEvent() { return mLastEvent; }
+
+protected:
+ virtual ~TestSystemCallback() {}
+
+private:
+ EventEntry mLastEvent;
+ size_t mEventCount;
+
+ DISALLOW_EVIL_CONSTRUCTORS(TestSystemCallback);
+};
+
+
+struct TestClient : public BnResourceManagerClient {
+ TestClient(int pid, const std::shared_ptr<ResourceManagerService> &service)
+ : mReclaimed(false), mPid(pid), mService(service) {}
+
+ Status reclaimResource(bool* _aidl_return) override {
+ mService->removeClient(mPid, getId(ref<TestClient>()));
+ mReclaimed = true;
+ *_aidl_return = true;
+ return Status::ok();
+ }
+
+ Status getName(::std::string* _aidl_return) override {
+ *_aidl_return = "test_client";
+ return Status::ok();
+ }
+
+ bool reclaimed() const {
+ return mReclaimed;
+ }
+
+ void reset() {
+ mReclaimed = false;
+ }
+
+ virtual ~TestClient() {}
+
+private:
+ bool mReclaimed;
+ int mPid;
+ std::shared_ptr<ResourceManagerService> mService;
+ DISALLOW_EVIL_CONSTRUCTORS(TestClient);
+};
+
+static const int kTestPid1 = 30;
+static const int kTestUid1 = 1010;
+
+static const int kTestPid2 = 20;
+static const int kTestUid2 = 1011;
+
+static const int kLowPriorityPid = 40;
+static const int kMidPriorityPid = 25;
+static const int kHighPriorityPid = 10;
+
+using EventType = TestSystemCallback::EventType;
+using EventEntry = TestSystemCallback::EventEntry;
+bool operator== (const EventEntry& lhs, const EventEntry& rhs) {
+ 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 ResourceManagerServiceTestBase : public ::testing::Test {
+public:
+ ResourceManagerServiceTestBase()
+ : mSystemCB(new TestSystemCallback()),
+ 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)) {
+ }
+
+ sp<TestSystemCallback> mSystemCB;
+ std::shared_ptr<ResourceManagerService> mService;
+ std::shared_ptr<IResourceManagerClient> mTestClient1;
+ std::shared_ptr<IResourceManagerClient> mTestClient2;
+ std::shared_ptr<IResourceManagerClient> mTestClient3;
+
+protected:
+ 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.type, res.subType, res.id);
+ r1[resType] = res;
+ }
+ return r1 == resources2;
+ }
+
+ static void expectEqResourceInfo(const ResourceInfo &info,
+ int uid,
+ 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));
+ }
+};
+
+} // namespace android
diff --git a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
index 3a45dcf..a029d45 100644
--- a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
+++ b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
@@ -16,189 +16,17 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "ResourceManagerService_test"
+
#include <utils/Log.h>
-#include <gtest/gtest.h>
-
+#include "ResourceManagerServiceTestUtils.h"
#include "ResourceManagerService.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 android {
-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();
-}
-
-struct TestProcessInfo : public ProcessInfoInterface {
- TestProcessInfo() {}
- virtual ~TestProcessInfo() {}
-
- virtual bool getPriority(int pid, int *priority) {
- // For testing, use pid as priority.
- // Lower the value higher the priority.
- *priority = pid;
- return true;
- }
-
- virtual bool isValidPid(int /* pid */) {
- return true;
- }
-
-private:
- DISALLOW_EVIL_CONSTRUCTORS(TestProcessInfo);
-};
-
-struct TestSystemCallback :
- public ResourceManagerService::SystemCallbackInterface {
- TestSystemCallback() :
- mLastEvent({EventType::INVALID, 0}), mEventCount(0) {}
-
- enum EventType {
- INVALID = -1,
- VIDEO_ON = 0,
- VIDEO_OFF = 1,
- VIDEO_RESET = 2,
- CPUSET_ENABLE = 3,
- CPUSET_DISABLE = 4,
- };
-
- struct EventEntry {
- EventType type;
- int arg;
- };
-
- virtual void noteStartVideo(int uid) override {
- mLastEvent = {EventType::VIDEO_ON, uid};
- mEventCount++;
- }
-
- virtual void noteStopVideo(int uid) override {
- mLastEvent = {EventType::VIDEO_OFF, uid};
- mEventCount++;
- }
-
- virtual void noteResetVideo() override {
- mLastEvent = {EventType::VIDEO_RESET, 0};
- mEventCount++;
- }
-
- virtual bool requestCpusetBoost(bool enable) override {
- mLastEvent = {enable ? EventType::CPUSET_ENABLE : EventType::CPUSET_DISABLE, 0};
- mEventCount++;
- return true;
- }
-
- size_t eventCount() { return mEventCount; }
- EventType lastEventType() { return mLastEvent.type; }
- EventEntry lastEvent() { return mLastEvent; }
-
-protected:
- virtual ~TestSystemCallback() {}
-
-private:
- EventEntry mLastEvent;
- size_t mEventCount;
-
- DISALLOW_EVIL_CONSTRUCTORS(TestSystemCallback);
-};
-
-
-struct TestClient : public BnResourceManagerClient {
- TestClient(int pid, const std::shared_ptr<ResourceManagerService> &service)
- : mReclaimed(false), mPid(pid), mService(service) {}
-
- Status reclaimResource(bool* _aidl_return) override {
- mService->removeClient(mPid, getId(ref<TestClient>()));
- mReclaimed = true;
- *_aidl_return = true;
- return Status::ok();
- }
-
- Status getName(::std::string* _aidl_return) override {
- *_aidl_return = "test_client";
- return Status::ok();
- }
-
- bool reclaimed() const {
- return mReclaimed;
- }
-
- void reset() {
- mReclaimed = false;
- }
-
- virtual ~TestClient() {}
-
-private:
- bool mReclaimed;
- int mPid;
- std::shared_ptr<ResourceManagerService> mService;
- DISALLOW_EVIL_CONSTRUCTORS(TestClient);
-};
-
-static const int kTestPid1 = 30;
-static const int kTestUid1 = 1010;
-
-static const int kTestPid2 = 20;
-static const int kTestUid2 = 1011;
-
-static const int kLowPriorityPid = 40;
-static const int kMidPriorityPid = 25;
-static const int kHighPriorityPid = 10;
-
-using EventType = TestSystemCallback::EventType;
-using EventEntry = TestSystemCallback::EventEntry;
-bool operator== (const EventEntry& lhs, const EventEntry& rhs) {
- 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 {
+class ResourceManagerServiceTest : public ResourceManagerServiceTestBase {
public:
- ResourceManagerServiceTest()
- : mSystemCB(new TestSystemCallback()),
- 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 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.type, res.subType, res.id);
- r1[resType] = res;
- }
- return r1 == resources2;
- }
-
- static void expectEqResourceInfo(const ResourceInfo &info,
- int uid,
- 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));
- }
+ ResourceManagerServiceTest() : ResourceManagerServiceTestBase() {}
void verifyClients(bool c1, bool c2, bool c3) {
TestClient *client1 = static_cast<TestClient*>(mTestClient1.get());
@@ -897,12 +725,6 @@
EXPECT_EQ(4u, mSystemCB->eventCount());
EXPECT_EQ(EventType::CPUSET_DISABLE, mSystemCB->lastEventType());
}
-
- sp<TestSystemCallback> mSystemCB;
- std::shared_ptr<ResourceManagerService> mService;
- std::shared_ptr<IResourceManagerClient> mTestClient1;
- std::shared_ptr<IResourceManagerClient> mTestClient2;
- std::shared_ptr<IResourceManagerClient> mTestClient3;
};
TEST_F(ResourceManagerServiceTest, config) {
diff --git a/services/mediaresourcemanager/test/ResourceObserverService_test.cpp b/services/mediaresourcemanager/test/ResourceObserverService_test.cpp
new file mode 100644
index 0000000..acd9df1
--- /dev/null
+++ b/services/mediaresourcemanager/test/ResourceObserverService_test.cpp
@@ -0,0 +1,460 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ResourceObserverService_test"
+
+#include <iostream>
+#include <list>
+
+#include <aidl/android/media/BnResourceObserver.h>
+#include <utils/Log.h>
+#include "ResourceObserverService.h"
+#include "ResourceManagerServiceTestUtils.h"
+
+namespace android {
+
+using ::aidl::android::media::BnResourceObserver;
+using ::aidl::android::media::MediaObservableParcel;
+using ::aidl::android::media::MediaObservableType;
+
+#define BUSY ::aidl::android::media::MediaObservableEvent::kBusy
+#define IDLE ::aidl::android::media::MediaObservableEvent::kIdle
+#define ALL ::aidl::android::media::MediaObservableEvent::kAll
+
+struct EventTracker {
+ struct Event {
+ enum { NoEvent, Busy, Idle } type = NoEvent;
+ int uid = 0;
+ int pid = 0;
+ std::vector<MediaObservableParcel> observables;
+ };
+
+ static const Event NoEvent;
+
+ static std::string toString(const MediaObservableParcel& observable) {
+ return "{" + ::aidl::android::media::toString(observable.type)
+ + ", " + std::to_string(observable.value) + "}";
+ }
+ static std::string toString(const Event& event) {
+ std::string eventStr;
+ switch (event.type) {
+ case Event::Busy:
+ eventStr = "Busy";
+ break;
+ case Event::Idle:
+ eventStr = "Idle";
+ break;
+ default:
+ return "NoEvent";
+ }
+ std::string observableStr;
+ for (auto &observable : event.observables) {
+ if (!observableStr.empty()) {
+ observableStr += ", ";
+ }
+ observableStr += toString(observable);
+ }
+ return "{" + eventStr + ", " + std::to_string(event.uid) + ", "
+ + std::to_string(event.pid) + ", {" + observableStr + "}}";
+ }
+
+ static Event Busy(int uid, int pid, const std::vector<MediaObservableParcel>& observables) {
+ return { Event::Busy, uid, pid, observables };
+ }
+ static Event Idle(int uid, int pid, const std::vector<MediaObservableParcel>& observables) {
+ return { Event::Idle, uid, pid, observables };
+ }
+
+ // Pop 1 event from front, wait for up to timeoutUs if empty.
+ const Event& pop(int64_t timeoutUs = 0) {
+ std::unique_lock lock(mLock);
+
+ if (mEventQueue.empty() && timeoutUs > 0) {
+ mCondition.wait_for(lock, std::chrono::microseconds(timeoutUs));
+ }
+
+ if (mEventQueue.empty()) {
+ mPoppedEvent = NoEvent;
+ } else {
+ mPoppedEvent = *mEventQueue.begin();
+ mEventQueue.pop_front();
+ }
+
+ return mPoppedEvent;
+ }
+
+ // Push 1 event to back.
+ void append(const Event& event) {
+ ALOGD("%s", toString(event).c_str());
+
+ std::unique_lock lock(mLock);
+
+ mEventQueue.push_back(event);
+ mCondition.notify_one();
+ }
+
+private:
+ std::mutex mLock;
+ std::condition_variable mCondition;
+ Event mPoppedEvent;
+ std::list<Event> mEventQueue;
+};
+
+const EventTracker::Event EventTracker::NoEvent;
+
+// Operators for GTest macros.
+bool operator==(const EventTracker::Event& lhs, const EventTracker::Event& rhs) {
+ return lhs.type == rhs.type && lhs.uid == rhs.uid && lhs.pid == rhs.pid &&
+ lhs.observables == rhs.observables;
+}
+
+std::ostream& operator<<(std::ostream& str, const EventTracker::Event& v) {
+ str << EventTracker::toString(v);
+ return str;
+}
+
+struct TestObserver : public BnResourceObserver, public EventTracker {
+ TestObserver(const char *name) : mName(name) {}
+ ~TestObserver() = default;
+ Status onStatusChanged(MediaObservableEvent event, int32_t uid, int32_t pid,
+ const std::vector<MediaObservableParcel>& observables) override {
+ ALOGD("%s: %s", mName.c_str(), __FUNCTION__);
+ if (event == MediaObservableEvent::kBusy) {
+ append(Busy(uid, pid, observables));
+ } else {
+ append(Idle(uid, pid, observables));
+ }
+
+ return Status::ok();
+ }
+ std::string mName;
+};
+
+class ResourceObserverServiceTest : public ResourceManagerServiceTestBase {
+public:
+ ResourceObserverServiceTest() : ResourceManagerServiceTestBase(),
+ mObserverService(::ndk::SharedRefBase::make<ResourceObserverService>()),
+ mTestObserver1(::ndk::SharedRefBase::make<TestObserver>("observer1")),
+ mTestObserver2(::ndk::SharedRefBase::make<TestObserver>("observer2")),
+ mTestObserver3(::ndk::SharedRefBase::make<TestObserver>("observer3")) {
+ mService->setObserverService(mObserverService);
+ }
+
+ void registerObservers(MediaObservableEvent filter = ALL) {
+ std::vector<MediaObservableFilter> filters1, filters2, filters3;
+ filters1 = {{MediaObservableType::kVideoSecureCodec, filter}};
+ filters2 = {{MediaObservableType::kVideoNonSecureCodec, filter}};
+ filters3 = {{MediaObservableType::kVideoSecureCodec, filter},
+ {MediaObservableType::kVideoNonSecureCodec, filter}};
+
+ // mTestObserver1 monitors secure video codecs.
+ EXPECT_TRUE(mObserverService->registerObserver(mTestObserver1, filters1).isOk());
+
+ // mTestObserver2 monitors non-secure video codecs.
+ EXPECT_TRUE(mObserverService->registerObserver(mTestObserver2, filters2).isOk());
+
+ // mTestObserver3 monitors both secure & non-secure video codecs.
+ EXPECT_TRUE(mObserverService->registerObserver(mTestObserver3, filters3).isOk());
+ }
+
+protected:
+ std::shared_ptr<ResourceObserverService> mObserverService;
+ std::shared_ptr<TestObserver> mTestObserver1;
+ std::shared_ptr<TestObserver> mTestObserver2;
+ std::shared_ptr<TestObserver> mTestObserver3;
+};
+
+TEST_F(ResourceObserverServiceTest, testRegisterObserver) {
+ std::vector<MediaObservableFilter> filters1;
+ Status status;
+
+ // Register with null observer should fail.
+ status = mObserverService->registerObserver(nullptr, filters1);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(status.getServiceSpecificError(), BAD_VALUE);
+
+ // Register with empty observables should fail.
+ status = mObserverService->registerObserver(mTestObserver1, filters1);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(status.getServiceSpecificError(), BAD_VALUE);
+
+ // mTestObserver1 monitors secure video codecs.
+ filters1 = {{MediaObservableType::kVideoSecureCodec, ALL}};
+ EXPECT_TRUE(mObserverService->registerObserver(mTestObserver1, filters1).isOk());
+
+ // Register duplicates should fail.
+ status = mObserverService->registerObserver(mTestObserver1, filters1);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(status.getServiceSpecificError(), ALREADY_EXISTS);
+}
+
+TEST_F(ResourceObserverServiceTest, testUnregisterObserver) {
+ std::vector<MediaObservableFilter> filters1;
+ Status status;
+
+ // Unregister without registering first should fail.
+ status = mObserverService->unregisterObserver(mTestObserver1);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(status.getServiceSpecificError(), NAME_NOT_FOUND);
+
+ // mTestObserver1 monitors secure video codecs.
+ filters1 = {{MediaObservableType::kVideoSecureCodec, ALL}};
+ EXPECT_TRUE(mObserverService->registerObserver(mTestObserver1, filters1).isOk());
+ EXPECT_TRUE(mObserverService->unregisterObserver(mTestObserver1).isOk());
+
+ // Unregister again should fail.
+ status = mObserverService->unregisterObserver(mTestObserver1);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(status.getServiceSpecificError(), NAME_NOT_FOUND);
+}
+
+TEST_F(ResourceObserverServiceTest, testAddResourceBasic) {
+ registerObservers();
+
+ std::vector<MediaObservableParcel> observables1, observables2, observables3;
+ observables1 = {{MediaObservableType::kVideoSecureCodec, 1}};
+ observables2 = {{MediaObservableType::kVideoNonSecureCodec, 1}};
+ observables3 = {{MediaObservableType::kVideoSecureCodec, 1},
+ {MediaObservableType::kVideoNonSecureCodec, 1}};
+
+ std::vector<MediaResourceParcel> resources;
+ // Add secure video codec.
+ resources = {MediaResource::CodecResource(1 /*secure*/, 1 /*video*/)};
+ mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources);
+ EXPECT_EQ(mTestObserver1->pop(), EventTracker::Busy(kTestUid1, kTestPid1, observables1));
+ EXPECT_EQ(mTestObserver2->pop(), EventTracker::NoEvent);
+ EXPECT_EQ(mTestObserver3->pop(), EventTracker::Busy(kTestUid1, kTestPid1, observables1));
+
+ // Add non-secure video codec.
+ resources = {MediaResource::CodecResource(0 /*secure*/, 1 /*video*/)};
+ mService->addResource(kTestPid2, kTestUid2, getId(mTestClient2), mTestClient2, resources);
+ EXPECT_EQ(mTestObserver1->pop(), EventTracker::NoEvent);
+ EXPECT_EQ(mTestObserver2->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables2));
+ EXPECT_EQ(mTestObserver3->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables2));
+
+ // Add secure & non-secure video codecs.
+ resources = {MediaResource::CodecResource(1 /*secure*/, 1 /*video*/),
+ MediaResource::CodecResource(0 /*secure*/, 1 /*video*/)};
+ mService->addResource(kTestPid2, kTestUid2, getId(mTestClient3), mTestClient3, resources);
+ EXPECT_EQ(mTestObserver1->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables1));
+ EXPECT_EQ(mTestObserver2->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables2));
+ EXPECT_EQ(mTestObserver3->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables3));
+
+ // Add additional audio codecs, should be ignored.
+ resources.push_back(MediaResource::CodecResource(1 /*secure*/, 0 /*video*/));
+ resources.push_back(MediaResource::CodecResource(0 /*secure*/, 0 /*video*/));
+ mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources);
+ EXPECT_EQ(mTestObserver1->pop(), EventTracker::Busy(kTestUid1, kTestPid1, observables1));
+ EXPECT_EQ(mTestObserver2->pop(), EventTracker::Busy(kTestUid1, kTestPid1, observables2));
+ EXPECT_EQ(mTestObserver3->pop(), EventTracker::Busy(kTestUid1, kTestPid1, observables3));
+}
+
+TEST_F(ResourceObserverServiceTest, testAddResourceMultiple) {
+ registerObservers();
+
+ std::vector<MediaObservableParcel> observables1, observables2, observables3;
+ observables1 = {{MediaObservableType::kVideoSecureCodec, 1}};
+ observables2 = {{MediaObservableType::kVideoNonSecureCodec, 1}};
+ observables3 = {{MediaObservableType::kVideoSecureCodec, 1},
+ {MediaObservableType::kVideoNonSecureCodec, 1}};
+
+ std::vector<MediaResourceParcel> resources;
+
+ // Add multiple secure & non-secure video codecs.
+ // Multiple entries of the same type should be merged, count should be propagated correctly.
+ resources = {MediaResource::CodecResource(1 /*secure*/, 1 /*video*/),
+ MediaResource::CodecResource(1 /*secure*/, 1 /*video*/),
+ MediaResource::CodecResource(0 /*secure*/, 1 /*video*/, 3 /*count*/)};
+ observables1 = {{MediaObservableType::kVideoSecureCodec, 2}};
+ observables2 = {{MediaObservableType::kVideoNonSecureCodec, 3}};
+ observables3 = {{MediaObservableType::kVideoSecureCodec, 2},
+ {MediaObservableType::kVideoNonSecureCodec, 3}};
+ mService->addResource(kTestPid2, kTestUid2, getId(mTestClient3), mTestClient3, resources);
+ EXPECT_EQ(mTestObserver1->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables1));
+ EXPECT_EQ(mTestObserver2->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables2));
+ EXPECT_EQ(mTestObserver3->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables3));
+}
+
+TEST_F(ResourceObserverServiceTest, testRemoveResourceBasic) {
+ registerObservers();
+
+ std::vector<MediaObservableParcel> observables1, observables2, observables3;
+ observables1 = {{MediaObservableType::kVideoSecureCodec, 1}};
+ observables2 = {{MediaObservableType::kVideoNonSecureCodec, 1}};
+ observables3 = {{MediaObservableType::kVideoSecureCodec, 1},
+ {MediaObservableType::kVideoNonSecureCodec, 1}};
+
+ std::vector<MediaResourceParcel> resources;
+ // Add secure video codec to client1.
+ resources = {MediaResource::CodecResource(1 /*secure*/, 1 /*video*/)};
+ mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources);
+ EXPECT_EQ(mTestObserver1->pop(), EventTracker::Busy(kTestUid1, kTestPid1, observables1));
+ EXPECT_EQ(mTestObserver2->pop(), EventTracker::NoEvent);
+ EXPECT_EQ(mTestObserver3->pop(), EventTracker::Busy(kTestUid1, kTestPid1, observables1));
+ // Remove secure video codec. observer 1&3 should receive updates.
+ mService->removeResource(kTestPid1, getId(mTestClient1), resources);
+ EXPECT_EQ(mTestObserver1->pop(), EventTracker::Idle(kTestUid1, kTestPid1, observables1));
+ EXPECT_EQ(mTestObserver2->pop(), EventTracker::NoEvent);
+ EXPECT_EQ(mTestObserver3->pop(), EventTracker::Idle(kTestUid1, kTestPid1, observables1));
+ // Remove secure video codec again, should have no event.
+ mService->removeResource(kTestPid1, getId(mTestClient1), resources);
+ EXPECT_EQ(mTestObserver1->pop(), EventTracker::NoEvent);
+ EXPECT_EQ(mTestObserver2->pop(), EventTracker::NoEvent);
+ EXPECT_EQ(mTestObserver3->pop(), EventTracker::NoEvent);
+ // Remove client1, should have no event.
+ mService->removeClient(kTestPid1, getId(mTestClient1));
+ EXPECT_EQ(mTestObserver1->pop(), EventTracker::NoEvent);
+ EXPECT_EQ(mTestObserver2->pop(), EventTracker::NoEvent);
+ EXPECT_EQ(mTestObserver3->pop(), EventTracker::NoEvent);
+
+ // Add non-secure video codec to client2.
+ resources = {MediaResource::CodecResource(0 /*secure*/, 1 /*video*/)};
+ mService->addResource(kTestPid2, kTestUid2, getId(mTestClient2), mTestClient2, resources);
+ EXPECT_EQ(mTestObserver1->pop(), EventTracker::NoEvent);
+ EXPECT_EQ(mTestObserver2->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables2));
+ EXPECT_EQ(mTestObserver3->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables2));
+ // Remove client2, observer 2&3 should receive updates.
+ mService->removeClient(kTestPid2, getId(mTestClient2));
+ EXPECT_EQ(mTestObserver1->pop(), EventTracker::NoEvent);
+ EXPECT_EQ(mTestObserver2->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables2));
+ EXPECT_EQ(mTestObserver3->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables2));
+ // Remove non-secure codec after client2 removed, should have no event.
+ mService->removeResource(kTestPid2, getId(mTestClient2), resources);
+ EXPECT_EQ(mTestObserver1->pop(), EventTracker::NoEvent);
+ EXPECT_EQ(mTestObserver2->pop(), EventTracker::NoEvent);
+ EXPECT_EQ(mTestObserver3->pop(), EventTracker::NoEvent);
+ // Remove client2 again, should have no event.
+ mService->removeClient(kTestPid2, getId(mTestClient2));
+ EXPECT_EQ(mTestObserver1->pop(), EventTracker::NoEvent);
+ EXPECT_EQ(mTestObserver2->pop(), EventTracker::NoEvent);
+ EXPECT_EQ(mTestObserver3->pop(), EventTracker::NoEvent);
+
+ // Add secure & non-secure video codecs, plus audio codecs (that's ignored).
+ resources = {MediaResource::CodecResource(1 /*secure*/, 1 /*video*/),
+ MediaResource::CodecResource(0 /*secure*/, 1 /*video*/),
+ MediaResource::CodecResource(1 /*secure*/, 0 /*video*/),
+ MediaResource::CodecResource(0 /*secure*/, 0 /*video*/)};
+ mService->addResource(kTestPid2, kTestUid2, getId(mTestClient3), mTestClient3, resources);
+ EXPECT_EQ(mTestObserver1->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables1));
+ EXPECT_EQ(mTestObserver2->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables2));
+ EXPECT_EQ(mTestObserver3->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables3));
+ // Remove one audio codec, should have no event.
+ resources = {MediaResource::CodecResource(1 /*secure*/, 0 /*video*/)};
+ mService->removeResource(kTestPid2, getId(mTestClient3), resources);
+ EXPECT_EQ(mTestObserver1->pop(), EventTracker::NoEvent);
+ EXPECT_EQ(mTestObserver2->pop(), EventTracker::NoEvent);
+ EXPECT_EQ(mTestObserver3->pop(), EventTracker::NoEvent);
+ // Remove the other audio codec and the secure video codec, only secure video codec
+ // removal should be reported.
+ resources = {MediaResource::CodecResource(0 /*secure*/, 0 /*video*/),
+ MediaResource::CodecResource(1 /*secure*/, 1 /*video*/)};
+ mService->removeResource(kTestPid2, getId(mTestClient3), resources);
+ EXPECT_EQ(mTestObserver1->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables1));
+ EXPECT_EQ(mTestObserver2->pop(), EventTracker::NoEvent);
+ EXPECT_EQ(mTestObserver3->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables1));
+ // Remove client3 entirely. Non-secure video codec removal should be reported.
+ mService->removeClient(kTestPid2, getId(mTestClient3));
+ EXPECT_EQ(mTestObserver1->pop(), EventTracker::NoEvent);
+ EXPECT_EQ(mTestObserver2->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables2));
+ EXPECT_EQ(mTestObserver3->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables2));
+}
+
+TEST_F(ResourceObserverServiceTest, testRemoveResourceMultiple) {
+ registerObservers();
+
+ std::vector<MediaObservableParcel> observables1, observables2, observables3;
+ observables1 = {{MediaObservableType::kVideoSecureCodec, 1}};
+ observables2 = {{MediaObservableType::kVideoNonSecureCodec, 1}};
+ observables3 = {{MediaObservableType::kVideoSecureCodec, 1},
+ {MediaObservableType::kVideoNonSecureCodec, 1}};
+
+ std::vector<MediaResourceParcel> resources;
+
+ // Add multiple secure & non-secure video codecs, plus audio codecs (that's ignored).
+ // (ResourceManager will merge these internally.)
+ resources = {MediaResource::CodecResource(1 /*secure*/, 1 /*video*/),
+ MediaResource::CodecResource(0 /*secure*/, 1 /*video*/, 4 /*count*/),
+ MediaResource::CodecResource(1 /*secure*/, 0 /*video*/),
+ MediaResource::CodecResource(0 /*secure*/, 0 /*video*/)};
+ mService->addResource(kTestPid2, kTestUid2, getId(mTestClient3), mTestClient3, resources);
+ observables1 = {{MediaObservableType::kVideoSecureCodec, 1}};
+ observables2 = {{MediaObservableType::kVideoNonSecureCodec, 4}};
+ observables3 = {{MediaObservableType::kVideoSecureCodec, 1},
+ {MediaObservableType::kVideoNonSecureCodec, 4}};
+ EXPECT_EQ(mTestObserver1->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables1));
+ EXPECT_EQ(mTestObserver2->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables2));
+ EXPECT_EQ(mTestObserver3->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables3));
+ // Remove one audio codec, 2 secure video codecs and 2 non-secure video codecs.
+ // 1 secure video codec removal and 2 non-secure video codec removals should be reported.
+ resources = {MediaResource::CodecResource(0 /*secure*/, 0 /*video*/),
+ MediaResource::CodecResource(1 /*secure*/, 1 /*video*/),
+ MediaResource::CodecResource(1 /*secure*/, 1 /*video*/),
+ MediaResource::CodecResource(0 /*secure*/, 1 /*video*/, 2 /*count*/)};
+ mService->removeResource(kTestPid2, getId(mTestClient3), resources);
+ observables1 = {{MediaObservableType::kVideoSecureCodec, 1}};
+ observables2 = {{MediaObservableType::kVideoNonSecureCodec, 2}};
+ observables3 = {{MediaObservableType::kVideoSecureCodec, 1},
+ {MediaObservableType::kVideoNonSecureCodec, 2}};
+ EXPECT_EQ(mTestObserver1->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables1));
+ EXPECT_EQ(mTestObserver2->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables2));
+ EXPECT_EQ(mTestObserver3->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables3));
+ // Remove client3 entirely. 2 non-secure video codecs removal should be reported.
+ mService->removeClient(kTestPid2, getId(mTestClient3));
+ EXPECT_EQ(mTestObserver1->pop(), EventTracker::NoEvent);
+ EXPECT_EQ(mTestObserver2->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables2));
+ EXPECT_EQ(mTestObserver3->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables2));
+}
+
+TEST_F(ResourceObserverServiceTest, testEventFilters) {
+ // Register observers with different event filters.
+ std::vector<MediaObservableFilter> filters1, filters2, filters3;
+ filters1 = {{MediaObservableType::kVideoSecureCodec, BUSY}};
+ filters2 = {{MediaObservableType::kVideoNonSecureCodec, IDLE}};
+ filters3 = {{MediaObservableType::kVideoSecureCodec, IDLE},
+ {MediaObservableType::kVideoNonSecureCodec, BUSY}};
+
+ // mTestObserver1 monitors secure video codecs.
+ EXPECT_TRUE(mObserverService->registerObserver(mTestObserver1, filters1).isOk());
+
+ // mTestObserver2 monitors non-secure video codecs.
+ EXPECT_TRUE(mObserverService->registerObserver(mTestObserver2, filters2).isOk());
+
+ // mTestObserver3 monitors both secure & non-secure video codecs.
+ EXPECT_TRUE(mObserverService->registerObserver(mTestObserver3, filters3).isOk());
+
+ std::vector<MediaObservableParcel> observables1, observables2;
+ observables1 = {{MediaObservableType::kVideoSecureCodec, 1}};
+ observables2 = {{MediaObservableType::kVideoNonSecureCodec, 1}};
+
+ std::vector<MediaResourceParcel> resources;
+
+ // Add secure & non-secure video codecs.
+ resources = {MediaResource::CodecResource(1 /*secure*/, 1 /*video*/),
+ MediaResource::CodecResource(0 /*secure*/, 1 /*video*/)};
+ mService->addResource(kTestPid2, kTestUid2, getId(mTestClient3), mTestClient3, resources);
+ EXPECT_EQ(mTestObserver1->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables1));
+ EXPECT_EQ(mTestObserver2->pop(), EventTracker::NoEvent);
+ EXPECT_EQ(mTestObserver3->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables2));
+
+ // Remove secure & non-secure video codecs.
+ mService->removeResource(kTestPid2, getId(mTestClient3), resources);
+ EXPECT_EQ(mTestObserver1->pop(), EventTracker::NoEvent);
+ EXPECT_EQ(mTestObserver2->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables2));
+ EXPECT_EQ(mTestObserver3->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables1));
+}
+
+} // namespace android
diff --git a/services/mediaresourcemanager/test/build_and_run_all_unit_tests.sh b/services/mediaresourcemanager/test/build_and_run_all_unit_tests.sh
new file mode 100755
index 0000000..1c4ae98
--- /dev/null
+++ b/services/mediaresourcemanager/test/build_and_run_all_unit_tests.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+#
+# Run tests in this directory.
+#
+
+if [ "$SYNC_FINISHED" != true ]; then
+ if [ -z "$ANDROID_BUILD_TOP" ]; then
+ echo "Android build environment not set"
+ exit -1
+ fi
+
+ # ensure we have mm
+ . $ANDROID_BUILD_TOP/build/envsetup.sh
+
+ mm
+
+ echo "waiting for device"
+
+ adb root && adb wait-for-device remount && adb sync
+fi
+
+echo "========================================"
+
+echo "testing ResourceManagerService"
+#adb shell /data/nativetest64/ResourceManagerService_test/ResourceManagerService_test
+adb shell /data/nativetest/ResourceManagerService_test/ResourceManagerService_test
+
+echo "testing ServiceLog"
+#adb shell /data/nativetest64/ServiceLog_test/ServiceLog_test
+adb shell /data/nativetest/ServiceLog_test/ServiceLog_test
+
+echo "testing ResourceObserverService"
+#adb shell /data/nativetest64/ResourceObserverService_test/ResourceObserverService_test
+adb shell /data/nativetest/ResourceObserverService_test/ResourceObserverService_test
diff --git a/services/mediatranscoding/Android.bp b/services/mediatranscoding/Android.bp
index a895071..a9fd14f 100644
--- a/services/mediatranscoding/Android.bp
+++ b/services/mediatranscoding/Android.bp
@@ -18,19 +18,34 @@
],
}
-cc_library_shared {
+cc_library {
name: "libmediatranscodingservice",
- srcs: ["MediaTranscodingService.cpp"],
+ srcs: [
+ "MediaTranscodingService.cpp",
+ "SimulatedTranscoder.cpp",
+ ],
+
+ min_sdk_version: "29",
+ apex_available: [
+ "com.android.media",
+ "test_com.android.media",
+ ],
shared_libs: [
+ "libandroid#31",
"libbase",
"libbinder_ndk",
+ "libcutils",
"liblog",
"libmediatranscoding",
"libutils",
],
+ export_shared_lib_headers: [
+ "libmediatranscoding",
+ ],
+
static_libs: [
"mediatranscoding_aidl_interface-ndk_platform",
],
@@ -50,8 +65,7 @@
shared_libs: [
"libbase",
- // TODO(hkuang): Use libbinder_ndk
- "libbinder",
+ "libbinder_ndk",
"libutils",
"liblog",
"libbase",
@@ -59,12 +73,16 @@
"libmediatranscodingservice",
],
+ min_sdk_version: "29",
+ apex_available: [
+ "com.android.media",
+ "test_com.android.media",
+ ],
+
static_libs: [
"mediatranscoding_aidl_interface-ndk_platform",
],
- init_rc: ["mediatranscoding.rc"],
-
cflags: [
"-Werror",
"-Wall",
diff --git a/services/mediatranscoding/MediaTranscodingService.cpp b/services/mediatranscoding/MediaTranscodingService.cpp
index 82d4161..b80fe57 100644
--- a/services/mediatranscoding/MediaTranscodingService.cpp
+++ b/services/mediatranscoding/MediaTranscodingService.cpp
@@ -16,13 +16,24 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaTranscodingService"
-#include <MediaTranscodingService.h>
+#include "MediaTranscodingService.h"
+
#include <android/binder_manager.h>
#include <android/binder_process.h>
-#include <private/android_filesystem_config.h>
+#include <android/permission_manager.h>
+#include <cutils/properties.h>
+#include <media/TranscoderWrapper.h>
+#include <media/TranscodingClientManager.h>
+#include <media/TranscodingLogger.h>
+#include <media/TranscodingResourcePolicy.h>
+#include <media/TranscodingSessionController.h>
+#include <media/TranscodingThermalPolicy.h>
+#include <media/TranscodingUidPolicy.h>
#include <utils/Log.h>
#include <utils/Vector.h>
+#include "SimulatedTranscoder.h"
+
namespace android {
// Convenience methods for constructing binder::Status objects for error returns
@@ -31,23 +42,53 @@
errorCode, \
String8::format("%s:%d: " errorString, __FUNCTION__, __LINE__, ##__VA_ARGS__))
-// Can MediaTranscoding service trust the caller based on the calling UID?
-// TODO(hkuang): Add MediaProvider's UID.
-static bool isTrustedCallingUid(uid_t uid) {
- switch (uid) {
- case AID_ROOT: // root user
- case AID_SYSTEM:
- case AID_SHELL:
- case AID_MEDIA: // mediaserver
- return true;
- default:
- return false;
- }
-}
+static constexpr int64_t kTranscoderHeartBeatIntervalUs = 1000000LL;
MediaTranscodingService::MediaTranscodingService()
- : mTranscodingClientManager(TranscodingClientManager::getInstance()) {
+ : mUidPolicy(new TranscodingUidPolicy()),
+ mResourcePolicy(new TranscodingResourcePolicy()),
+ mThermalPolicy(new TranscodingThermalPolicy()),
+ mLogger(new TranscodingLogger()) {
ALOGV("MediaTranscodingService is created");
+ bool simulated = property_get_bool("debug.transcoding.simulated_transcoder", false);
+ if (simulated) {
+ // Overrid default config params with shorter values for testing.
+ TranscodingSessionController::ControllerConfig config = {
+ .pacerBurstThresholdMs = 500,
+ .pacerBurstCountQuota = 10,
+ .pacerBurstTimeQuotaSeconds = 3,
+ };
+ mSessionController.reset(new TranscodingSessionController(
+ [](const std::shared_ptr<TranscoderCallbackInterface>& cb)
+ -> std::shared_ptr<TranscoderInterface> {
+ return std::make_shared<SimulatedTranscoder>(cb);
+ },
+ mUidPolicy, mResourcePolicy, mThermalPolicy, &config));
+ } else {
+ int32_t overrideBurstCountQuota =
+ property_get_int32("persist.transcoding.burst_count_quota", -1);
+ int32_t pacerBurstTimeQuotaSeconds =
+ property_get_int32("persist.transcoding.burst_time_quota_seconds", -1);
+ // Override default config params with properties if present.
+ TranscodingSessionController::ControllerConfig config;
+ if (overrideBurstCountQuota > 0) {
+ config.pacerBurstCountQuota = overrideBurstCountQuota;
+ }
+ if (pacerBurstTimeQuotaSeconds > 0) {
+ config.pacerBurstTimeQuotaSeconds = pacerBurstTimeQuotaSeconds;
+ }
+ mSessionController.reset(new TranscodingSessionController(
+ [logger = mLogger](const std::shared_ptr<TranscoderCallbackInterface>& cb)
+ -> std::shared_ptr<TranscoderInterface> {
+ return std::make_shared<TranscoderWrapper>(cb, logger,
+ kTranscoderHeartBeatIntervalUs);
+ },
+ mUidPolicy, mResourcePolicy, mThermalPolicy, &config));
+ }
+ mClientManager.reset(new TranscodingClientManager(mSessionController));
+ mUidPolicy->setCallback(mSessionController);
+ mResourcePolicy->setCallback(mSessionController);
+ mThermalPolicy->setCallback(mSessionController);
}
MediaTranscodingService::~MediaTranscodingService() {
@@ -56,6 +97,23 @@
binder_status_t MediaTranscodingService::dump(int fd, const char** /*args*/, uint32_t /*numArgs*/) {
String8 result;
+
+ uid_t callingUid = AIBinder_getCallingUid();
+ pid_t callingPid = AIBinder_getCallingPid();
+ if (__builtin_available(android __TRANSCODING_MIN_API__, *)) {
+ int32_t permissionResult;
+ if (APermissionManager_checkPermission("android.permission.DUMP", callingPid, callingUid,
+ &permissionResult) != PERMISSION_MANAGER_STATUS_OK ||
+ permissionResult != PERMISSION_MANAGER_PERMISSION_GRANTED) {
+ result.format(
+ "Permission Denial: "
+ "can't dump MediaTranscodingService from pid=%d, uid=%d\n",
+ AIBinder_getCallingPid(), AIBinder_getCallingUid());
+ write(fd, result.string(), result.size());
+ return PERMISSION_DENIED;
+ }
+ }
+
const size_t SIZE = 256;
char buffer[SIZE];
@@ -64,7 +122,8 @@
write(fd, result.string(), result.size());
Vector<String16> args;
- mTranscodingClientManager.dumpAllClients(fd, args);
+ mClientManager->dumpAllClients(fd, args);
+ mSessionController->dumpAllSessions(fd, args);
return OK;
}
@@ -72,126 +131,39 @@
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;
+ if (__builtin_available(android __TRANSCODING_MIN_API__, *)) {
+ // Once service is started, we want it to stay even is client side perished.
+ AServiceManager_forceLazyServicesPersist(true /*persist*/);
+ (void)AServiceManager_registerLazyService(service->asBinder().get(), getServiceName());
}
}
Status MediaTranscodingService::registerClient(
- const std::shared_ptr<ITranscodingServiceClient>& in_client,
- const std::string& in_opPackageName, int32_t in_clientUid, int32_t in_clientPid,
- int32_t* _aidl_return) {
- if (in_client == nullptr) {
- ALOGE("Client can not be null");
- *_aidl_return = kInvalidJobId;
- return Status::fromServiceSpecificError(ERROR_ILLEGAL_ARGUMENT);
- }
-
- int32_t callingPid = AIBinder_getCallingPid();
- int32_t callingUid = AIBinder_getCallingUid();
-
- // Check if we can trust clientUid. Only privilege caller could forward the uid on app client's behalf.
- if (in_clientUid == USE_CALLING_UID) {
- in_clientUid = callingUid;
- } else if (!isTrustedCallingUid(callingUid)) {
- ALOGE("MediaTranscodingService::registerClient failed (calling PID %d, calling UID %d) "
- "rejected "
- "(don't trust clientUid %d)",
- in_clientPid, in_clientUid, in_clientUid);
- return STATUS_ERROR_FMT(ERROR_PERMISSION_DENIED,
- "Untrusted caller (calling PID %d, UID %d) trying to "
- "register client",
- in_clientPid, in_clientUid);
- }
-
- // Check if we can trust clientPid. Only privilege caller could forward the pid on app client's behalf.
- if (in_clientPid == USE_CALLING_PID) {
- in_clientPid = callingPid;
- } else if (!isTrustedCallingUid(callingUid)) {
- ALOGE("MediaTranscodingService::registerClient client failed (calling PID %d, calling UID "
- "%d) rejected "
- "(don't trust clientPid %d)",
- in_clientPid, in_clientUid, in_clientPid);
- return STATUS_ERROR_FMT(ERROR_PERMISSION_DENIED,
- "Untrusted caller (calling PID %d, UID %d) trying to "
- "register client",
- in_clientPid, in_clientUid);
- }
-
- // We know the clientId must be equal to its pid as we assigned client's pid as its clientId.
- int32_t clientId = in_clientPid;
-
- // Checks if the client already registers.
- if (mTranscodingClientManager.isClientIdRegistered(clientId)) {
- return Status::fromServiceSpecificError(ERROR_ALREADY_EXISTS);
+ const std::shared_ptr<ITranscodingClientCallback>& in_callback,
+ const std::string& in_clientName, const std::string& in_opPackageName,
+ std::shared_ptr<ITranscodingClient>* _aidl_return) {
+ if (in_callback == nullptr) {
+ *_aidl_return = nullptr;
+ return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT, "Client callback cannot be null!");
}
// Creates the client and uses its process id as client id.
- std::unique_ptr<TranscodingClientManager::ClientInfo> newClient =
- std::make_unique<TranscodingClientManager::ClientInfo>(
- in_client, clientId, in_clientPid, in_clientUid, in_opPackageName);
- status_t err = mTranscodingClientManager.addClient(std::move(newClient));
+ std::shared_ptr<ITranscodingClient> newClient;
+
+ status_t err =
+ mClientManager->addClient(in_callback, in_clientName, in_opPackageName, &newClient);
if (err != OK) {
- *_aidl_return = kInvalidClientId;
+ *_aidl_return = nullptr;
return STATUS_ERROR_FMT(err, "Failed to add client to TranscodingClientManager");
}
- ALOGD("Assign client: %s pid: %d, uid: %d with id: %d", in_opPackageName.c_str(), in_clientPid,
- in_clientUid, clientId);
-
- *_aidl_return = clientId;
- return Status::ok();
-}
-
-Status MediaTranscodingService::unregisterClient(int32_t clientId, bool* _aidl_return) {
- ALOGD("unregisterClient id: %d", clientId);
- int32_t callingUid = AIBinder_getCallingUid();
- int32_t callingPid = AIBinder_getCallingPid();
-
- // Only the client with clientId or the trusted caller could unregister the client.
- if (callingPid != clientId) {
- if (!isTrustedCallingUid(callingUid)) {
- ALOGE("Untrusted caller (calling PID %d, UID %d) trying to "
- "unregister client with id: %d",
- callingUid, callingPid, clientId);
- *_aidl_return = true;
- return STATUS_ERROR_FMT(ERROR_PERMISSION_DENIED,
- "Untrusted caller (calling PID %d, UID %d) trying to "
- "unregister client with id: %d",
- callingUid, callingPid, clientId);
- }
- }
-
- *_aidl_return = (mTranscodingClientManager.removeClient(clientId) == OK);
+ *_aidl_return = newClient;
return Status::ok();
}
Status MediaTranscodingService::getNumOfClients(int32_t* _aidl_return) {
ALOGD("MediaTranscodingService::getNumOfClients");
- *_aidl_return = mTranscodingClientManager.getNumOfClients();
- return Status::ok();
-}
-
-Status MediaTranscodingService::submitRequest(int32_t /*clientId*/,
- const TranscodingRequestParcel& /*request*/,
- TranscodingJobParcel* /*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*/,
- TranscodingJobParcel* /*out_job*/,
- bool* /*_aidl_return*/) {
- // TODO(hkuang): Add implementation.
+ *_aidl_return = mClientManager->getNumOfClients();
return Status::ok();
}
diff --git a/services/mediatranscoding/MediaTranscodingService.h b/services/mediatranscoding/MediaTranscodingService.h
index cc69727..12be131 100644
--- a/services/mediatranscoding/MediaTranscodingService.h
+++ b/services/mediatranscoding/MediaTranscodingService.h
@@ -19,19 +19,25 @@
#include <aidl/android/media/BnMediaTranscodingService.h>
#include <binder/IServiceManager.h>
-#include <media/TranscodingClientManager.h>
namespace android {
using Status = ::ndk::ScopedAStatus;
using ::aidl::android::media::BnMediaTranscodingService;
-using ::aidl::android::media::ITranscodingServiceClient;
-using ::aidl::android::media::TranscodingJobParcel;
+using ::aidl::android::media::ITranscodingClient;
+using ::aidl::android::media::ITranscodingClientCallback;
using ::aidl::android::media::TranscodingRequestParcel;
+using ::aidl::android::media::TranscodingSessionParcel;
+class TranscodingClientManager;
+class TranscodingLogger;
+class TranscodingSessionController;
+class UidPolicyInterface;
+class ResourcePolicyInterface;
+class ThermalPolicyInterface;
class MediaTranscodingService : public BnMediaTranscodingService {
public:
- static constexpr int32_t kInvalidJobId = -1;
+ static constexpr int32_t kInvalidSessionId = -1;
static constexpr int32_t kInvalidClientId = -1;
MediaTranscodingService();
@@ -41,22 +47,12 @@
static const char* getServiceName() { return "media.transcoding"; }
- Status registerClient(const std::shared_ptr<ITranscodingServiceClient>& in_client,
- const std::string& in_opPackageName, int32_t in_clientUid,
- int32_t in_clientPid, int32_t* _aidl_return) override;
-
- Status unregisterClient(int32_t clientId, bool* _aidl_return) override;
+ Status registerClient(const std::shared_ptr<ITranscodingClientCallback>& in_callback,
+ const std::string& in_clientName, const std::string& in_opPackageName,
+ std::shared_ptr<ITranscodingClient>* _aidl_return) override;
Status getNumOfClients(int32_t* _aidl_return) override;
- Status submitRequest(int32_t in_clientId, const TranscodingRequestParcel& in_request,
- TranscodingJobParcel* out_job, int32_t* _aidl_return) override;
-
- Status cancelJob(int32_t in_clientId, int32_t in_jobId, bool* _aidl_return) override;
-
- Status getJobWithId(int32_t in_jobId, TranscodingJobParcel* out_job,
- bool* _aidl_return) override;
-
virtual inline binder_status_t dump(int /*fd*/, const char** /*args*/, uint32_t /*numArgs*/);
private:
@@ -64,7 +60,12 @@
mutable std::mutex mServiceLock;
- TranscodingClientManager& mTranscodingClientManager;
+ std::shared_ptr<UidPolicyInterface> mUidPolicy;
+ std::shared_ptr<ResourcePolicyInterface> mResourcePolicy;
+ std::shared_ptr<ThermalPolicyInterface> mThermalPolicy;
+ std::shared_ptr<TranscodingLogger> mLogger;
+ std::shared_ptr<TranscodingSessionController> mSessionController;
+ std::shared_ptr<TranscodingClientManager> mClientManager;
};
} // namespace android
diff --git a/services/mediatranscoding/SimulatedTranscoder.cpp b/services/mediatranscoding/SimulatedTranscoder.cpp
new file mode 100644
index 0000000..e80dbc5
--- /dev/null
+++ b/services/mediatranscoding/SimulatedTranscoder.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SimulatedTranscoder"
+#include "SimulatedTranscoder.h"
+
+#include <utils/Log.h>
+
+#include <thread>
+
+namespace android {
+
+//static
+const char* SimulatedTranscoder::toString(Event::Type type) {
+ switch (type) {
+ case Event::Start:
+ return "Start";
+ case Event::Pause:
+ return "Pause";
+ case Event::Resume:
+ return "Resume";
+ case Event::Stop:
+ return "Stop";
+ case Event::Finished:
+ return "Finished";
+ case Event::Failed:
+ return "Failed";
+ case Event::Abandon:
+ return "Abandon";
+ default:
+ break;
+ }
+ return "(unknown)";
+}
+
+SimulatedTranscoder::SimulatedTranscoder(const std::shared_ptr<TranscoderCallbackInterface>& cb)
+ : mCallback(cb), mLooperReady(false) {
+ ALOGV("SimulatedTranscoder CTOR: %p", this);
+}
+
+SimulatedTranscoder::~SimulatedTranscoder() {
+ ALOGV("SimulatedTranscoder DTOR: %p", this);
+}
+
+void SimulatedTranscoder::start(
+ ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& request,
+ uid_t /*callingUid*/,
+ const std::shared_ptr<ITranscodingClientCallback>& /*clientCallback*/) {
+ {
+ auto lock = std::scoped_lock(mLock);
+ int64_t processingTimeUs = kSessionDurationUs;
+ if (request.testConfig.has_value() && request.testConfig->processingTotalTimeMs > 0) {
+ processingTimeUs = request.testConfig->processingTotalTimeMs * 1000;
+ }
+ ALOGI("%s: session {%lld, %d}: processingTimeUs: %lld", __FUNCTION__, (long long)clientId,
+ sessionId, (long long)processingTimeUs);
+ SessionKeyType key = std::make_pair(clientId, sessionId);
+ mRemainingTimeMap.emplace(key, processingTimeUs);
+ }
+
+ queueEvent(Event::Start, clientId, sessionId, [=] {
+ auto callback = mCallback.lock();
+ if (callback != nullptr) {
+ callback->onStarted(clientId, sessionId);
+ }
+ });
+}
+
+void SimulatedTranscoder::pause(ClientIdType clientId, SessionIdType sessionId) {
+ queueEvent(Event::Pause, clientId, sessionId, [=] {
+ auto callback = mCallback.lock();
+ if (callback != nullptr) {
+ callback->onPaused(clientId, sessionId);
+ }
+ });
+}
+
+void SimulatedTranscoder::resume(
+ ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& /*request*/,
+ uid_t /*callingUid*/,
+ const std::shared_ptr<ITranscodingClientCallback>& /*clientCallback*/) {
+ queueEvent(Event::Resume, clientId, sessionId, [=] {
+ auto callback = mCallback.lock();
+ if (callback != nullptr) {
+ callback->onResumed(clientId, sessionId);
+ }
+ });
+}
+
+void SimulatedTranscoder::stop(ClientIdType clientId, SessionIdType sessionId, bool abandon) {
+ queueEvent(Event::Stop, clientId, sessionId, nullptr);
+
+ if (abandon) {
+ queueEvent(Event::Abandon, 0, 0, nullptr);
+ }
+}
+
+void SimulatedTranscoder::queueEvent(Event::Type type, ClientIdType clientId,
+ SessionIdType sessionId, std::function<void()> runnable) {
+ ALOGV("%s: session {%lld, %d}: %s", __FUNCTION__, (long long)clientId, sessionId,
+ toString(type));
+
+ auto lock = std::scoped_lock(mLock);
+
+ if (!mLooperReady) {
+ // A shared_ptr to ourselves is given to the thread's stack, so that SimulatedTranscoder
+ // object doesn't go away until the thread exits. When a watchdog timeout happens, this
+ // allows the session controller to release its reference to the TranscoderWrapper object
+ // without blocking on the thread exits.
+ std::thread([owner = shared_from_this()]() { owner->threadLoop(); }).detach();
+ mLooperReady = true;
+ }
+
+ mQueue.push_back({type, clientId, sessionId, runnable});
+ mCondition.notify_one();
+}
+
+void SimulatedTranscoder::threadLoop() {
+ bool running = false;
+ std::chrono::steady_clock::time_point lastRunningTime;
+ Event lastRunningEvent;
+
+ std::unique_lock<std::mutex> lock(mLock);
+ // SimulatedTranscoder currently lives in the transcoding service, as long as
+ // MediaTranscodingService itself.
+ while (true) {
+ // Wait for the next event.
+ while (mQueue.empty()) {
+ if (!running) {
+ mCondition.wait(lock);
+ continue;
+ }
+ // If running, wait for the remaining life of this session. Report finish if timed out.
+ SessionKeyType key =
+ std::make_pair(lastRunningEvent.clientId, lastRunningEvent.sessionId);
+ std::cv_status status = mCondition.wait_for(lock, mRemainingTimeMap[key]);
+ if (status == std::cv_status::timeout) {
+ running = false;
+
+ auto callback = mCallback.lock();
+ if (callback != nullptr) {
+ mRemainingTimeMap.erase(key);
+
+ lock.unlock();
+ callback->onFinish(lastRunningEvent.clientId, lastRunningEvent.sessionId);
+ lock.lock();
+ }
+ } else {
+ // Advance last running time and remaining time. This is needed to guard
+ // against bad events (which will be ignored) or spurious wakeups, in that
+ // case we don't want to wait for the same time again.
+ auto now = std::chrono::steady_clock::now();
+ mRemainingTimeMap[key] -= std::chrono::duration_cast<std::chrono::microseconds>(
+ now - lastRunningTime);
+ lastRunningTime = now;
+ }
+ }
+
+ // Handle the events, adjust state and send updates to client accordingly.
+ Event event = *mQueue.begin();
+ mQueue.pop_front();
+
+ ALOGD("%s: session {%lld, %d}: %s", __FUNCTION__, (long long)event.clientId,
+ event.sessionId, toString(event.type));
+
+ if (event.type == Event::Abandon) {
+ break;
+ }
+
+ SessionKeyType key = std::make_pair(event.clientId, event.sessionId);
+ if (!running && (event.type == Event::Start || event.type == Event::Resume)) {
+ running = true;
+ lastRunningTime = std::chrono::steady_clock::now();
+ lastRunningEvent = event;
+ ALOGV("%s: session {%lld, %d}: remaining time: %lld", __FUNCTION__,
+ (long long)event.clientId, event.sessionId,
+ (long long)mRemainingTimeMap[key].count());
+
+ } else if (running && (event.type == Event::Pause || event.type == Event::Stop)) {
+ running = false;
+ if (event.type == Event::Stop) {
+ mRemainingTimeMap.erase(key);
+ } else {
+ mRemainingTimeMap[key] -= std::chrono::duration_cast<std::chrono::microseconds>(
+ std::chrono::steady_clock::now() - lastRunningTime);
+ }
+ } else {
+ ALOGW("%s: discarding bad event: session {%lld, %d}: %s", __FUNCTION__,
+ (long long)event.clientId, event.sessionId, toString(event.type));
+ continue;
+ }
+
+ if (event.runnable != nullptr) {
+ lock.unlock();
+ event.runnable();
+ lock.lock();
+ }
+ }
+}
+
+} // namespace android
diff --git a/services/mediatranscoding/SimulatedTranscoder.h b/services/mediatranscoding/SimulatedTranscoder.h
new file mode 100644
index 0000000..58e2e30
--- /dev/null
+++ b/services/mediatranscoding/SimulatedTranscoder.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_SIMULATED_TRANSCODER_H
+#define ANDROID_MEDIA_SIMULATED_TRANSCODER_H
+
+#include <android-base/thread_annotations.h>
+#include <media/TranscoderInterface.h>
+
+#include <list>
+#include <map>
+#include <mutex>
+
+namespace android {
+
+/**
+ * SimulatedTranscoder is currently used to instantiate MediaTranscodingService
+ * on service side for testing, so that we could actually test the IPC calls of
+ * MediaTranscodingService to expose issues that's observable only over IPC.
+ * SimulatedTranscoder is used when useSimulatedTranscoder in TranscodingTestConfig
+ * is set to true.
+ *
+ * SimulatedTranscoder simulates session execution by reporting finish after kSessionDurationUs.
+ * Session lifecycle events are reported via progress updates with special progress
+ * numbers (equal to the Event's type).
+ */
+class SimulatedTranscoder : public TranscoderInterface,
+ public std::enable_shared_from_this<SimulatedTranscoder> {
+public:
+ struct Event {
+ enum Type { NoEvent, Start, Pause, Resume, Stop, Finished, Failed, Abandon } type;
+ ClientIdType clientId;
+ SessionIdType sessionId;
+ std::function<void()> runnable;
+ };
+
+ static constexpr int64_t kSessionDurationUs = 1000000;
+
+ SimulatedTranscoder(const std::shared_ptr<TranscoderCallbackInterface>& cb);
+ ~SimulatedTranscoder();
+
+ // TranscoderInterface
+ void start(ClientIdType clientId, SessionIdType sessionId,
+ const TranscodingRequestParcel& request, uid_t callingUid,
+ const std::shared_ptr<ITranscodingClientCallback>& clientCallback) override;
+ void pause(ClientIdType clientId, SessionIdType sessionId) override;
+ void resume(ClientIdType clientId, SessionIdType sessionId,
+ const TranscodingRequestParcel& request, uid_t callingUid,
+ const std::shared_ptr<ITranscodingClientCallback>& clientCallback) override;
+ void stop(ClientIdType clientId, SessionIdType sessionId, bool abandon = false) override;
+ // ~TranscoderInterface
+
+private:
+ std::weak_ptr<TranscoderCallbackInterface> mCallback;
+ std::mutex mLock;
+ std::condition_variable mCondition;
+ std::list<Event> mQueue GUARDED_BY(mLock);
+ bool mLooperReady;
+
+ using SessionKeyType = std::pair<ClientIdType, SessionIdType>;
+ // map of session's remaining time in microsec.
+ std::map<SessionKeyType, std::chrono::microseconds> mRemainingTimeMap;
+
+ static const char* toString(Event::Type type);
+ void queueEvent(Event::Type type, ClientIdType clientId, SessionIdType sessionId,
+ std::function<void()> runnable);
+ void threadLoop();
+};
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_SIMULATED_TRANSCODER_H
diff --git a/services/mediatranscoding/main_mediatranscodingservice.cpp b/services/mediatranscoding/main_mediatranscodingservice.cpp
index 7d862e6..14c568e 100644
--- a/services/mediatranscoding/main_mediatranscodingservice.cpp
+++ b/services/mediatranscoding/main_mediatranscodingservice.cpp
@@ -15,8 +15,7 @@
*/
#include <android-base/logging.h>
-#include <binder/IPCThreadState.h>
-#include <binder/ProcessState.h>
+#include <android/binder_process.h>
#include "MediaTranscodingService.h"
@@ -25,12 +24,9 @@
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::MediaTranscodingService::instantiate();
- ProcessState::self()->startThreadPool();
- IPCThreadState::self()->joinThreadPool();
+ ABinderProcess_startThreadPool();
+ ABinderProcess_joinThreadPool();
}
diff --git a/services/mediatranscoding/mediatranscoding.rc b/services/mediatranscoding/mediatranscoding.rc
deleted file mode 100644
index 5bfef59..0000000
--- a/services/mediatranscoding/mediatranscoding.rc
+++ /dev/null
@@ -1,6 +0,0 @@
-service media.transcoding /system/bin/mediatranscoding
- class main
- user media
- group media
- ioprio rt 4
- task_profiles ProcessCapacityHigh HighPerformance
diff --git a/services/mediatranscoding/tests/Android.bp b/services/mediatranscoding/tests/Android.bp
index a856c05..cb180ec 100644
--- a/services/mediatranscoding/tests/Android.bp
+++ b/services/mediatranscoding/tests/Android.bp
@@ -30,18 +30,42 @@
"libbinder_ndk",
"liblog",
"libutils",
- "libmediatranscodingservice",
+ "libcutils",
],
static_libs: [
"mediatranscoding_aidl_interface-ndk_platform",
+ "resourcemanager_aidl_interface-ndk_platform",
+ "libmediatranscodingservice",
+ ],
+
+ required: [
+ "TranscodingUidPolicy_TestAppA",
+ "TranscodingUidPolicy_TestAppB",
+ "TranscodingUidPolicy_TestAppC",
],
}
-// MediaTranscodingService unit test
+// MediaTranscodingService unit test using simulated transcoder
cc_test {
- name: "mediatranscodingservice_tests",
+ name: "mediatranscodingservice_simulated_tests",
defaults: ["mediatranscodingservice_test_defaults"],
- srcs: ["mediatranscodingservice_tests.cpp"],
+ srcs: ["mediatranscodingservice_simulated_tests.cpp"],
+}
+
+// MediaTranscodingService unit test using real transcoder
+cc_test {
+ name: "mediatranscodingservice_real_tests",
+ defaults: ["mediatranscodingservice_test_defaults"],
+
+ srcs: ["mediatranscodingservice_real_tests.cpp"],
+}
+
+// MediaTranscodingService unit test related to resource management
+cc_test {
+ name: "mediatranscodingservice_resource_tests",
+ defaults: ["mediatranscodingservice_test_defaults"],
+
+ srcs: ["mediatranscodingservice_resource_tests.cpp"],
}
diff --git a/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h b/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
new file mode 100644
index 0000000..0cb2fad
--- /dev/null
+++ b/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
@@ -0,0 +1,556 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Unit Test for MediaTranscodingService.
+
+#include <aidl/android/media/BnTranscodingClientCallback.h>
+#include <aidl/android/media/IMediaTranscodingService.h>
+#include <aidl/android/media/ITranscodingClient.h>
+#include <aidl/android/media/ITranscodingClientCallback.h>
+#include <aidl/android/media/TranscodingRequestParcel.h>
+#include <aidl/android/media/TranscodingSessionParcel.h>
+#include <aidl/android/media/TranscodingSessionPriority.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <binder/PermissionController.h>
+#include <cutils/multiuser.h>
+#include <fcntl.h>
+#include <gtest/gtest.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <utils/Log.h>
+
+#include <iostream>
+#include <list>
+
+#include "SimulatedTranscoder.h"
+
+namespace android {
+
+namespace media {
+
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::media::BnTranscodingClientCallback;
+using aidl::android::media::IMediaTranscodingService;
+using aidl::android::media::ITranscodingClient;
+using aidl::android::media::ITranscodingClientCallback;
+using aidl::android::media::TranscodingRequestParcel;
+using aidl::android::media::TranscodingSessionParcel;
+using aidl::android::media::TranscodingSessionPriority;
+using aidl::android::media::TranscodingTestConfig;
+using aidl::android::media::TranscodingVideoTrackFormat;
+
+constexpr int32_t kClientUseCallingPid = IMediaTranscodingService::USE_CALLING_PID;
+
+constexpr uid_t kClientUid = 5000;
+#define UID(n) (kClientUid + (n))
+
+constexpr pid_t kClientPid = 10000;
+#define PID(n) (kClientPid + (n))
+
+constexpr int32_t kClientId = 0;
+#define CLIENT(n) (kClientId + (n))
+
+constexpr const char* kClientName = "TestClient";
+constexpr const char* kClientPackageA = "com.android.tests.transcoding.testapp.A";
+constexpr const char* kClientPackageB = "com.android.tests.transcoding.testapp.B";
+constexpr const char* kClientPackageC = "com.android.tests.transcoding.testapp.C";
+
+constexpr const char* kTestActivityName = "/com.android.tests.transcoding.MainActivity";
+
+static status_t getUidForPackage(String16 packageName, userid_t userId, /*inout*/ uid_t& uid) {
+ PermissionController pc;
+ uid = pc.getPackageUid(packageName, 0);
+ if (uid <= 0) {
+ ALOGE("Unknown package: '%s'", String8(packageName).string());
+ return BAD_VALUE;
+ }
+
+ if (userId < 0) {
+ ALOGE("Invalid user: %d", userId);
+ return BAD_VALUE;
+ }
+
+ uid = multiuser_get_uid(userId, uid);
+ return NO_ERROR;
+}
+
+struct ShellHelper {
+ static bool RunCmd(const std::string& cmdStr) {
+ int ret = system(cmdStr.c_str());
+ if (ret != 0) {
+ ALOGE("Failed to run cmd: %s, exitcode %d", cmdStr.c_str(), ret);
+ return false;
+ }
+ return true;
+ }
+
+ static bool Start(const char* packageName, const char* activityName) {
+ return RunCmd("am start -W " + std::string(packageName) + std::string(activityName) +
+ " &> /dev/null");
+ }
+
+ static bool Stop(const char* packageName) {
+ return RunCmd("am force-stop " + std::string(packageName));
+ }
+};
+
+struct EventTracker {
+ struct Event {
+ enum { NoEvent, Start, Pause, Resume, Finished, Failed } type;
+ int64_t clientId;
+ int32_t sessionId;
+ };
+
+#define DECLARE_EVENT(action) \
+ static Event action(int32_t clientId, int32_t sessionId) { \
+ return {Event::action, clientId, sessionId}; \
+ }
+
+ DECLARE_EVENT(Start);
+ DECLARE_EVENT(Pause);
+ DECLARE_EVENT(Resume);
+ DECLARE_EVENT(Finished);
+ DECLARE_EVENT(Failed);
+
+ static constexpr Event NoEvent = {Event::NoEvent, 0, 0};
+
+ static std::string toString(const Event& event) {
+ std::string eventStr;
+ switch (event.type) {
+ case Event::Start:
+ eventStr = "Start";
+ break;
+ case Event::Pause:
+ eventStr = "Pause";
+ break;
+ case Event::Resume:
+ eventStr = "Resume";
+ break;
+ case Event::Finished:
+ eventStr = "Finished";
+ break;
+ case Event::Failed:
+ eventStr = "Failed";
+ break;
+ default:
+ return "NoEvent";
+ }
+ return "session {" + std::to_string(event.clientId) + ", " +
+ std::to_string(event.sessionId) + "}: " + eventStr;
+ }
+
+ // Pop 1 event from front, wait for up to timeoutUs if empty.
+ const Event& pop(int64_t timeoutUs = 0) {
+ std::unique_lock lock(mLock);
+
+ if (mEventQueue.empty() && timeoutUs > 0) {
+ mCondition.wait_for(lock, std::chrono::microseconds(timeoutUs));
+ }
+
+ if (mEventQueue.empty()) {
+ mPoppedEvent = NoEvent;
+ } else {
+ mPoppedEvent = *mEventQueue.begin();
+ mEventQueue.pop_front();
+ }
+
+ return mPoppedEvent;
+ }
+
+ bool waitForSpecificEventAndPop(const Event& target, std::list<Event>* outEvents,
+ int64_t timeoutUs = 0) {
+ std::unique_lock lock(mLock);
+
+ auto startTime = std::chrono::system_clock::now();
+ int64_t remainingUs = timeoutUs;
+
+ std::list<Event>::iterator it;
+ while (((it = std::find(mEventQueue.begin(), mEventQueue.end(), target)) ==
+ mEventQueue.end()) &&
+ remainingUs > 0) {
+ std::cv_status status =
+ mCondition.wait_for(lock, std::chrono::microseconds(remainingUs));
+ if (status == std::cv_status::timeout) {
+ break;
+ }
+ std::chrono::microseconds elapsedTime = std::chrono::system_clock::now() - startTime;
+ remainingUs = timeoutUs - elapsedTime.count();
+ }
+
+ if (it == mEventQueue.end()) {
+ return false;
+ }
+ *outEvents = std::list<Event>(mEventQueue.begin(), std::next(it));
+ mEventQueue.erase(mEventQueue.begin(), std::next(it));
+ return true;
+ }
+
+ // Push 1 event to back.
+ void append(const Event& event,
+ const TranscodingErrorCode err = TranscodingErrorCode::kNoError) {
+ ALOGD("%s", toString(event).c_str());
+
+ std::unique_lock lock(mLock);
+
+ mEventQueue.push_back(event);
+ if (err != TranscodingErrorCode::kNoError) {
+ mLastErrQueue.push_back(err);
+ }
+ mCondition.notify_one();
+ }
+
+ void updateProgress(int progress) {
+ std::unique_lock lock(mLock);
+ mLastProgress = progress;
+ mUpdateCount++;
+ }
+
+ int getUpdateCount(int* lastProgress) {
+ std::unique_lock lock(mLock);
+ *lastProgress = mLastProgress;
+ return mUpdateCount;
+ }
+
+ TranscodingErrorCode getLastError() {
+ std::unique_lock lock(mLock);
+ if (mLastErrQueue.empty()) {
+ return TranscodingErrorCode::kNoError;
+ }
+ TranscodingErrorCode err = mLastErrQueue.front();
+ mLastErrQueue.pop_front();
+ return err;
+ }
+
+private:
+ std::mutex mLock;
+ std::condition_variable mCondition;
+ Event mPoppedEvent;
+ std::list<Event> mEventQueue;
+ std::list<TranscodingErrorCode> mLastErrQueue;
+ int mUpdateCount = 0;
+ int mLastProgress = -1;
+};
+
+// Operators for GTest macros.
+bool operator==(const EventTracker::Event& lhs, const EventTracker::Event& rhs) {
+ return lhs.type == rhs.type && lhs.clientId == rhs.clientId && lhs.sessionId == rhs.sessionId;
+}
+
+std::ostream& operator<<(std::ostream& str, const EventTracker::Event& v) {
+ str << EventTracker::toString(v);
+ return str;
+}
+
+static constexpr bool success = true;
+static constexpr bool fail = false;
+
+struct TestClientCallback : public BnTranscodingClientCallback,
+ public EventTracker,
+ public std::enable_shared_from_this<TestClientCallback> {
+ TestClientCallback(const char* packageName, int32_t id)
+ : mClientId(id), mClientPid(PID(id)), mClientUid(UID(id)), mPackageName(packageName) {
+ ALOGI("TestClientCallback %d created: pid %d, uid %d", id, PID(id), UID(id));
+
+ // Use package uid if that's available.
+ uid_t packageUid;
+ if (getUidForPackage(String16(packageName), 0 /*userId*/, packageUid) == NO_ERROR) {
+ mClientUid = packageUid;
+ }
+ }
+
+ virtual ~TestClientCallback() { ALOGI("TestClientCallback %d destroyed", mClientId); }
+
+ Status openFileDescriptor(const std::string& in_fileUri, const std::string& in_mode,
+ ::ndk::ScopedFileDescriptor* _aidl_return) override {
+ ALOGD("@@@ openFileDescriptor: %s", in_fileUri.c_str());
+ int fd;
+ if (in_mode == "w" || in_mode == "rw") {
+ int kOpenFlags;
+ if (in_mode == "w") {
+ // Write-only, create file if non-existent, truncate existing file.
+ kOpenFlags = O_WRONLY | O_CREAT | O_TRUNC;
+ } else {
+ // Read-Write, create if non-existent, no truncate (service will truncate if needed)
+ kOpenFlags = O_RDWR | O_CREAT;
+ }
+ // User R+W permission.
+ constexpr int kFileMode = S_IRUSR | S_IWUSR;
+ fd = open(in_fileUri.c_str(), kOpenFlags, kFileMode);
+ } else {
+ fd = open(in_fileUri.c_str(), O_RDONLY);
+ }
+ _aidl_return->set(fd);
+ return Status::ok();
+ }
+
+ Status onTranscodingStarted(int32_t in_sessionId) override {
+ append(EventTracker::Start(mClientId, in_sessionId));
+ return Status::ok();
+ }
+
+ Status onTranscodingPaused(int32_t in_sessionId) override {
+ append(EventTracker::Pause(mClientId, in_sessionId));
+ return Status::ok();
+ }
+
+ Status onTranscodingResumed(int32_t in_sessionId) override {
+ append(EventTracker::Resume(mClientId, in_sessionId));
+ return Status::ok();
+ }
+
+ Status onTranscodingFinished(
+ int32_t in_sessionId,
+ const ::aidl::android::media::TranscodingResultParcel& /* in_result */) override {
+ append(Finished(mClientId, in_sessionId));
+ return Status::ok();
+ }
+
+ Status onTranscodingFailed(int32_t in_sessionId,
+ ::aidl::android::media::TranscodingErrorCode in_errorCode) override {
+ append(Failed(mClientId, in_sessionId), in_errorCode);
+ return Status::ok();
+ }
+
+ Status onAwaitNumberOfSessionsChanged(int32_t /* in_sessionId */,
+ int32_t /* in_oldAwaitNumber */,
+ int32_t /* in_newAwaitNumber */) override {
+ return Status::ok();
+ }
+
+ Status onProgressUpdate(int32_t /* in_sessionId */, int32_t in_progress) override {
+ updateProgress(in_progress);
+ return Status::ok();
+ }
+
+ Status registerClient(const char* packageName,
+ const std::shared_ptr<IMediaTranscodingService>& service) {
+ // Override the default uid if the package uid is found.
+ uid_t uid;
+ if (getUidForPackage(String16(packageName), 0 /*userId*/, uid) == NO_ERROR) {
+ mClientUid = uid;
+ }
+
+ ALOGD("registering %s with uid %d", packageName, mClientUid);
+
+ std::shared_ptr<ITranscodingClient> client;
+ Status status =
+ service->registerClient(shared_from_this(), kClientName, packageName, &client);
+
+ mClient = status.isOk() ? client : nullptr;
+ return status;
+ }
+
+ Status unregisterClient() {
+ Status status;
+ if (mClient != nullptr) {
+ status = mClient->unregister();
+ mClient = nullptr;
+ }
+ return status;
+ }
+
+ template <bool expectation = success>
+ bool submit(int32_t sessionId, const char* sourceFilePath, const char* destinationFilePath,
+ TranscodingSessionPriority priority = TranscodingSessionPriority::kNormal,
+ int bitrateBps = -1, int overridePid = -1, int overrideUid = -1,
+ int sessionDurationMs = -1) {
+ constexpr bool shouldSucceed = (expectation == success);
+ bool result;
+ TranscodingRequestParcel request;
+ TranscodingSessionParcel session;
+
+ request.sourceFilePath = sourceFilePath;
+ request.destinationFilePath = destinationFilePath;
+ request.priority = priority;
+ request.clientPid = (overridePid == -1) ? mClientPid : overridePid;
+ request.clientUid = (overrideUid == -1) ? mClientUid : overrideUid;
+ request.clientPackageName = (overrideUid == -1) ? mPackageName : "";
+ if (bitrateBps > 0) {
+ request.requestedVideoTrackFormat.emplace(TranscodingVideoTrackFormat());
+ request.requestedVideoTrackFormat->bitrateBps = bitrateBps;
+ }
+ if (sessionDurationMs > 0) {
+ request.isForTesting = true;
+ request.testConfig.emplace(TranscodingTestConfig());
+ request.testConfig->processingTotalTimeMs = sessionDurationMs;
+ }
+ Status status = mClient->submitRequest(request, &session, &result);
+
+ EXPECT_TRUE(status.isOk());
+ EXPECT_EQ(result, shouldSucceed);
+ if (shouldSucceed) {
+ EXPECT_EQ(session.sessionId, sessionId);
+ }
+
+ return status.isOk() && (result == shouldSucceed) &&
+ (!shouldSucceed || session.sessionId == sessionId);
+ }
+
+ template <bool expectation = success>
+ bool cancel(int32_t sessionId) {
+ constexpr bool shouldSucceed = (expectation == success);
+ bool result;
+ Status status = mClient->cancelSession(sessionId, &result);
+
+ EXPECT_TRUE(status.isOk());
+ EXPECT_EQ(result, shouldSucceed);
+
+ return status.isOk() && (result == shouldSucceed);
+ }
+
+ template <bool expectation = success>
+ bool getSession(int32_t sessionId, const char* sourceFilePath,
+ const char* destinationFilePath) {
+ constexpr bool shouldSucceed = (expectation == success);
+ bool result;
+ TranscodingSessionParcel session;
+ Status status = mClient->getSessionWithId(sessionId, &session, &result);
+
+ EXPECT_TRUE(status.isOk());
+ EXPECT_EQ(result, shouldSucceed);
+ if (shouldSucceed) {
+ EXPECT_EQ(session.sessionId, sessionId);
+ EXPECT_EQ(session.request.sourceFilePath, sourceFilePath);
+ }
+
+ return status.isOk() && (result == shouldSucceed) &&
+ (!shouldSucceed || (session.sessionId == sessionId &&
+ session.request.sourceFilePath == sourceFilePath &&
+ session.request.destinationFilePath == destinationFilePath));
+ }
+
+ template <bool expectation = success>
+ bool addClientUid(int32_t sessionId, uid_t clientUid) {
+ constexpr bool shouldSucceed = (expectation == success);
+ bool result;
+ Status status = mClient->addClientUid(sessionId, clientUid, &result);
+
+ EXPECT_TRUE(status.isOk());
+ EXPECT_EQ(result, shouldSucceed);
+
+ return status.isOk() && (result == shouldSucceed);
+ }
+
+ template <bool expectation = success>
+ bool getClientUids(int32_t sessionId, std::vector<int32_t>* clientUids) {
+ constexpr bool shouldSucceed = (expectation == success);
+ std::optional<std::vector<int32_t>> aidl_return;
+ Status status = mClient->getClientUids(sessionId, &aidl_return);
+
+ EXPECT_TRUE(status.isOk());
+ bool success = (aidl_return != std::nullopt);
+ if (success) {
+ *clientUids = *aidl_return;
+ }
+ EXPECT_EQ(success, shouldSucceed);
+
+ return status.isOk() && (success == shouldSucceed);
+ }
+
+ int32_t mClientId;
+ pid_t mClientPid;
+ uid_t mClientUid;
+ std::string mPackageName;
+ std::shared_ptr<ITranscodingClient> mClient;
+};
+
+class MediaTranscodingServiceTestBase : public ::testing::Test {
+public:
+ MediaTranscodingServiceTestBase() { ALOGI("MediaTranscodingServiceTestBase created"); }
+
+ virtual ~MediaTranscodingServiceTestBase() {
+ ALOGI("MediaTranscodingServiceTestBase destroyed");
+ }
+
+ void SetUp() override {
+ // Need thread pool to receive callbacks, otherwise oneway callbacks are
+ // silently ignored.
+ ABinderProcess_startThreadPool();
+ ::ndk::SpAIBinder binder(AServiceManager_waitForService("media.transcoding"));
+ mService = IMediaTranscodingService::fromBinder(binder);
+ if (mService == nullptr) {
+ ALOGE("Failed to connect to the media.trascoding service.");
+ return;
+ }
+
+ mClient1 = ::ndk::SharedRefBase::make<TestClientCallback>(kClientPackageA, 1);
+ mClient2 = ::ndk::SharedRefBase::make<TestClientCallback>(kClientPackageB, 2);
+ mClient3 = ::ndk::SharedRefBase::make<TestClientCallback>(kClientPackageC, 3);
+ }
+
+ Status registerOneClient(const std::shared_ptr<TestClientCallback>& callback) {
+ ALOGD("registering %s with uid %d", callback->mPackageName.c_str(), callback->mClientUid);
+
+ std::shared_ptr<ITranscodingClient> client;
+ Status status =
+ mService->registerClient(callback, kClientName, callback->mPackageName, &client);
+
+ if (status.isOk()) {
+ callback->mClient = client;
+ } else {
+ callback->mClient = nullptr;
+ }
+ return status;
+ }
+
+ void registerMultipleClients() {
+ // Register 3 clients.
+ EXPECT_TRUE(registerOneClient(mClient1).isOk());
+ EXPECT_TRUE(registerOneClient(mClient2).isOk());
+ EXPECT_TRUE(registerOneClient(mClient3).isOk());
+
+ // Check the number of clients.
+ int32_t numOfClients;
+ Status status = mService->getNumOfClients(&numOfClients);
+ EXPECT_TRUE(status.isOk());
+ EXPECT_GE(numOfClients, 3);
+ }
+
+ void unregisterMultipleClients() {
+ // Unregister the clients.
+ EXPECT_TRUE(mClient1->unregisterClient().isOk());
+ EXPECT_TRUE(mClient2->unregisterClient().isOk());
+ EXPECT_TRUE(mClient3->unregisterClient().isOk());
+ }
+
+ const char* prepareOutputFile(const char* path) {
+ deleteFile(path);
+ return path;
+ }
+
+ void deleteFile(const char* path) { unlink(path); }
+
+ void dismissKeyguard() {
+ EXPECT_TRUE(ShellHelper::RunCmd("input keyevent KEYCODE_WAKEUP"));
+ EXPECT_TRUE(ShellHelper::RunCmd("wm dismiss-keyguard"));
+ }
+
+ void stopAppPackages() {
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageB));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageC));
+ }
+
+ std::shared_ptr<IMediaTranscodingService> mService;
+ std::shared_ptr<TestClientCallback> mClient1;
+ std::shared_ptr<TestClientCallback> mClient2;
+ std::shared_ptr<TestClientCallback> mClient3;
+};
+
+} // namespace media
+} // namespace android
diff --git a/services/mediatranscoding/tests/README.txt b/services/mediatranscoding/tests/README.txt
new file mode 100644
index 0000000..cde465e
--- /dev/null
+++ b/services/mediatranscoding/tests/README.txt
@@ -0,0 +1,8 @@
+mediatranscodingservice_simulated_tests:
+ Tests media transcoding service with simulated transcoder.
+
+mediatranscodingservice_real_tests:
+ Tests media transcoding service with real transcoder. Uses the same test assets
+ as the MediaTranscoder unit tests. Before running the test, please make sure
+ to push the test assets to /sdcard:
+ adb push $TOP/frameworks/av/media/libmediatranscoding/tests/assets /data/local/tmp/TranscodingTestAssets
diff --git a/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/Android.bp b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/Android.bp
new file mode 100644
index 0000000..df00aa1
--- /dev/null
+++ b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/Android.bp
@@ -0,0 +1,34 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_services_mediatranscoding_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: [
+ "frameworks_av_services_mediatranscoding_license",
+ ],
+}
+
+android_test_helper_app {
+ name: "TranscodingUidPolicy_TestAppA",
+ manifest: "TestAppA.xml",
+ static_libs: ["androidx.test.rules"],
+ sdk_version: "test_current",
+ srcs: ["src/**/*.java"],
+}
+
+android_test_helper_app {
+ name: "TranscodingUidPolicy_TestAppB",
+ manifest: "TestAppB.xml",
+ static_libs: ["androidx.test.rules"],
+ sdk_version: "test_current",
+ srcs: ["src/**/*.java"],
+}
+
+android_test_helper_app {
+ name: "TranscodingUidPolicy_TestAppC",
+ manifest: "TestAppC.xml",
+ static_libs: ["androidx.test.rules"],
+ sdk_version: "test_current",
+ srcs: ["src/**/*.java"],
+}
diff --git a/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppA.xml b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppA.xml
new file mode 100644
index 0000000..0dff171
--- /dev/null
+++ b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppA.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.transcoding.testapp.A"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <application android:label="TestAppA">
+ <activity android:name="com.android.tests.transcoding.MainActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name="com.android.tests.transcoding.ResourcePolicyTestActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
+
diff --git a/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppB.xml b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppB.xml
new file mode 100644
index 0000000..4baa35a
--- /dev/null
+++ b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppB.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.transcoding.testapp.B"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <application android:label="TestAppB">
+ <activity android:name="com.android.tests.transcoding.MainActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
+
diff --git a/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppC.xml b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppC.xml
new file mode 100644
index 0000000..3dde3af
--- /dev/null
+++ b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppC.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.transcoding.testapp.C"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <application android:label="TestAppC">
+ <activity android:name="com.android.tests.transcoding.MainActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/src/com/android/tests/transcoding/MainActivity.java b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/src/com/android/tests/transcoding/MainActivity.java
new file mode 100644
index 0000000..b79164d
--- /dev/null
+++ b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/src/com/android/tests/transcoding/MainActivity.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tests.transcoding;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * This is an empty activity for testing the UID policy of media transcoding service.
+ */
+public class MainActivity extends Activity {
+ private static final String TAG = "MainActivity";
+
+ // Called at the start of the full lifetime.
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // Initialize Activity and inflate the UI.
+ }
+
+ // Called after onCreate has finished, use to restore UI state
+ @Override
+ public void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ // Restore UI state from the savedInstanceState.
+ // This bundle has also been passed to onCreate.
+ // Will only be called if the Activity has been
+ // killed by the system since it was last visible.
+ }
+
+ // Called before subsequent visible lifetimes
+ // for an activity process.
+ @Override
+ public void onRestart() {
+ super.onRestart();
+ // Load changes knowing that the Activity has already
+ // been visible within this process.
+ }
+
+ // Called at the start of the visible lifetime.
+ @Override
+ public void onStart() {
+ super.onStart();
+ // Apply any required UI change now that the Activity is visible.
+ }
+
+ // Called at the start of the active lifetime.
+ @Override
+ public void onResume() {
+ super.onResume();
+ // Resume any paused UI updates, threads, or processes required
+ // by the Activity but suspended when it was inactive.
+ }
+
+ // Called to save UI state changes at the
+ // end of the active lifecycle.
+ @Override
+ public void onSaveInstanceState(Bundle savedInstanceState) {
+ // Save UI state changes to the savedInstanceState.
+ // This bundle will be passed to onCreate and
+ // onRestoreInstanceState if the process is
+ // killed and restarted by the run time.
+ super.onSaveInstanceState(savedInstanceState);
+ }
+
+ // Called at the end of the active lifetime.
+ @Override
+ public void onPause() {
+ // Suspend UI updates, threads, or CPU intensive processes
+ // that don't need to be updated when the Activity isn't
+ // the active foreground Activity.
+ super.onPause();
+ }
+
+ // Called at the end of the visible lifetime.
+ @Override
+ public void onStop() {
+ // Suspend remaining UI updates, threads, or processing
+ // that aren't required when the Activity isn't visible.
+ // Persist all edits or state changes
+ // as after this call the process is likely to be killed.
+ super.onStop();
+ }
+
+ // Sometimes called at the end of the full lifetime.
+ @Override
+ public void onDestroy() {
+ // Clean up any resources including ending threads,
+ // closing database connections etc.
+ super.onDestroy();
+ }
+}
diff --git a/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/src/com/android/tests/transcoding/ResourcePolicyTestActivity.java b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/src/com/android/tests/transcoding/ResourcePolicyTestActivity.java
new file mode 100644
index 0000000..c9e2ddb
--- /dev/null
+++ b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/src/com/android/tests/transcoding/ResourcePolicyTestActivity.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.transcoding;
+
+import android.app.Activity;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.VideoCapabilities;
+import android.media.MediaCodecList;
+import android.media.MediaFormat;
+import android.os.Bundle;
+import android.util.Log;
+import java.io.IOException;
+import java.util.Vector;
+
+public class ResourcePolicyTestActivity extends Activity {
+ public static final int TYPE_NONSECURE = 0;
+ public static final int TYPE_SECURE = 1;
+ public static final int TYPE_MIX = 2;
+
+ protected String TAG;
+ private static final int FRAME_RATE = 10;
+ private static final int IFRAME_INTERVAL = 10; // 10 seconds between I-frames
+ private static final String MIME = MediaFormat.MIMETYPE_VIDEO_AVC;
+ private static final int TIMEOUT_MS = 5000;
+
+ private Vector<MediaCodec> mCodecs = new Vector<MediaCodec>();
+
+ private class TestCodecCallback extends MediaCodec.Callback {
+ @Override
+ public void onInputBufferAvailable(MediaCodec codec, int index) {
+ Log.d(TAG, "onInputBufferAvailable " + codec.toString());
+ }
+
+ @Override
+ public void onOutputBufferAvailable(
+ MediaCodec codec, int index, MediaCodec.BufferInfo info) {
+ Log.d(TAG, "onOutputBufferAvailable " + codec.toString());
+ }
+
+ @Override
+ public void onError(MediaCodec codec, MediaCodec.CodecException e) {
+ Log.d(TAG, "onError " + codec.toString() + " errorCode " + e.getErrorCode());
+ }
+
+ @Override
+ public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
+ Log.d(TAG, "onOutputFormatChanged " + codec.toString());
+ }
+ }
+
+ private MediaCodec.Callback mCallback = new TestCodecCallback();
+
+ private MediaFormat getTestFormat(CodecCapabilities caps, boolean securePlayback) {
+ VideoCapabilities vcaps = caps.getVideoCapabilities();
+ int width = vcaps.getSupportedWidths().getLower();
+ int height = vcaps.getSupportedHeightsFor(width).getLower();
+ int bitrate = vcaps.getBitrateRange().getLower();
+
+ MediaFormat format = MediaFormat.createVideoFormat(MIME, width, height);
+ format.setInteger(MediaFormat.KEY_COLOR_FORMAT, caps.colorFormats[0]);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
+ format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
+ format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
+ format.setFeatureEnabled(CodecCapabilities.FEATURE_SecurePlayback, securePlayback);
+ return format;
+ }
+
+ private MediaCodecInfo getTestCodecInfo(boolean securePlayback) {
+ // Use avc decoder for testing.
+ boolean isEncoder = false;
+
+ MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS);
+ for (MediaCodecInfo info : mcl.getCodecInfos()) {
+ if (info.isEncoder() != isEncoder) {
+ continue;
+ }
+ CodecCapabilities caps;
+ try {
+ caps = info.getCapabilitiesForType(MIME);
+ boolean securePlaybackSupported =
+ caps.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback);
+ boolean securePlaybackRequired =
+ caps.isFeatureRequired(CodecCapabilities.FEATURE_SecurePlayback);
+ if ((securePlayback && securePlaybackSupported)
+ || (!securePlayback && !securePlaybackRequired)) {
+ Log.d(TAG, "securePlayback " + securePlayback + " will use " + info.getName());
+ } else {
+ Log.d(TAG, "securePlayback " + securePlayback + " skip " + info.getName());
+ continue;
+ }
+ } catch (IllegalArgumentException e) {
+ // mime is not supported
+ continue;
+ }
+ return info;
+ }
+
+ return null;
+ }
+
+ protected int allocateCodecs(int max) {
+ Bundle extras = getIntent().getExtras();
+ int type = TYPE_NONSECURE;
+ if (extras != null) {
+ type = extras.getInt("test-type", type);
+ Log.d(TAG, "type is: " + type);
+ }
+
+ boolean shouldSkip = false;
+ boolean securePlayback;
+ if (type == TYPE_NONSECURE || type == TYPE_MIX) {
+ securePlayback = false;
+ MediaCodecInfo info = getTestCodecInfo(securePlayback);
+ if (info != null) {
+ allocateCodecs(max, info, securePlayback);
+ } else {
+ shouldSkip = true;
+ }
+ }
+
+ if (!shouldSkip) {
+ if (type == TYPE_SECURE || type == TYPE_MIX) {
+ securePlayback = true;
+ MediaCodecInfo info = getTestCodecInfo(securePlayback);
+ if (info != null) {
+ allocateCodecs(max, info, securePlayback);
+ } else {
+ shouldSkip = true;
+ }
+ }
+ }
+
+ if (shouldSkip) {
+ Log.d(TAG, "test skipped as there's no supported codec.");
+ finishWithResult(RESULT_OK);
+ }
+
+ Log.d(TAG, "allocateCodecs returned " + mCodecs.size());
+ return mCodecs.size();
+ }
+
+ protected void allocateCodecs(int max, MediaCodecInfo info, boolean securePlayback) {
+ String name = info.getName();
+ CodecCapabilities caps = info.getCapabilitiesForType(MIME);
+ MediaFormat format = getTestFormat(caps, securePlayback);
+ MediaCodec codec = null;
+ for (int i = mCodecs.size(); i < max; ++i) {
+ try {
+ Log.d(TAG, "Create codec " + name + " #" + i);
+ codec = MediaCodec.createByCodecName(name);
+ codec.setCallback(mCallback);
+ Log.d(TAG, "Configure codec " + format);
+ codec.configure(format, null, null, 0);
+ Log.d(TAG, "Start codec " + format);
+ codec.start();
+ mCodecs.add(codec);
+ codec = null;
+ } catch (IllegalArgumentException e) {
+ Log.d(TAG, "IllegalArgumentException " + e.getMessage());
+ break;
+ } catch (IOException e) {
+ Log.d(TAG, "IOException " + e.getMessage());
+ break;
+ } catch (MediaCodec.CodecException e) {
+ Log.d(TAG, "CodecException 0x" + Integer.toHexString(e.getErrorCode()));
+ break;
+ } finally {
+ if (codec != null) {
+ Log.d(TAG, "release codec");
+ codec.release();
+ codec = null;
+ }
+ }
+ }
+ }
+
+ protected void finishWithResult(int result) {
+ for (int i = 0; i < mCodecs.size(); ++i) {
+ Log.d(TAG, "release codec #" + i);
+ mCodecs.get(i).release();
+ }
+ mCodecs.clear();
+ setResult(result);
+ finish();
+ Log.d(TAG, "activity finished");
+ }
+
+ private void doUseCodecs() {
+ int current = 0;
+ try {
+ for (current = 0; current < mCodecs.size(); ++current) {
+ mCodecs.get(current).getName();
+ }
+ } catch (MediaCodec.CodecException e) {
+ Log.d(TAG, "useCodecs got CodecException 0x" + Integer.toHexString(e.getErrorCode()));
+ if (e.getErrorCode() == MediaCodec.CodecException.ERROR_RECLAIMED) {
+ Log.d(TAG, "Remove codec " + current + " from the list");
+ mCodecs.get(current).release();
+ mCodecs.remove(current);
+ mGotReclaimedException = true;
+ mUseCodecs = false;
+ }
+ return;
+ }
+ }
+
+ private Thread mWorkerThread;
+ private volatile boolean mUseCodecs = true;
+ private volatile boolean mGotReclaimedException = false;
+ protected void useCodecs() {
+ mWorkerThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ long start = System.currentTimeMillis();
+ long timeSinceStartedMs = 0;
+ while (mUseCodecs && (timeSinceStartedMs < TIMEOUT_MS)) {
+ doUseCodecs();
+ try {
+ Thread.sleep(50 /* millis */);
+ } catch (InterruptedException e) {
+ }
+ timeSinceStartedMs = System.currentTimeMillis() - start;
+ }
+ if (mGotReclaimedException) {
+ Log.d(TAG, "Got expected reclaim exception.");
+ }
+ finishWithResult(RESULT_OK);
+ }
+ });
+ mWorkerThread.start();
+ }
+
+ private static final int MAX_INSTANCES = 32;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ TAG = "ResourcePolicyTestActivity";
+
+ Log.d(TAG, "onCreate called.");
+ super.onCreate(savedInstanceState);
+
+ if (allocateCodecs(MAX_INSTANCES) == MAX_INSTANCES) {
+ // haven't reached the limit with MAX_INSTANCES, no need to wait for reclaim exception.
+ //mWaitForReclaim = false;
+ Log.d(TAG, "Didn't hit resource limitation");
+ }
+
+ useCodecs();
+ }
+
+ @Override
+ protected void onDestroy() {
+ Log.d(TAG, "onDestroy called.");
+ super.onDestroy();
+ }
+}
diff --git a/services/mediatranscoding/tests/build_and_run_all_unit_tests.sh b/services/mediatranscoding/tests/build_and_run_all_unit_tests.sh
old mode 100644
new mode 100755
index bcdc7f7..edf6778
--- a/services/mediatranscoding/tests/build_and_run_all_unit_tests.sh
+++ b/services/mediatranscoding/tests/build_and_run_all_unit_tests.sh
@@ -13,11 +13,35 @@
mm
-echo "waiting for device"
+# Push the files onto the device.
+. $ANDROID_BUILD_TOP/frameworks/av/media/libmediatranscoding/tests/push_assets.sh
-adb root && adb wait-for-device remount && adb sync
+echo "[==========] installing test apps"
+adb root
+adb install -t -r -g -d $ANDROID_TARGET_OUT_TESTCASES/TranscodingUidPolicy_TestAppA/arm64/TranscodingUidPolicy_TestAppA.apk
+adb install -t -r -g -d $ANDROID_TARGET_OUT_TESTCASES/TranscodingUidPolicy_TestAppB/arm64/TranscodingUidPolicy_TestAppB.apk
+adb install -t -r -g -d $ANDROID_TARGET_OUT_TESTCASES/TranscodingUidPolicy_TestAppC/arm64/TranscodingUidPolicy_TestAppC.apk
-echo "========================================"
+echo "[==========] waiting for device and sync"
+adb wait-for-device remount && adb sync
-echo "testing mediatranscodingservice"
-adb shell /data/nativetest64/mediatranscodingservice_tests/mediatranscodingservice_tests
+echo "[==========] running simulated tests"
+adb shell setprop debug.transcoding.simulated_transcoder true
+adb shell kill -9 `pid media.transcoding`
+#adb shell /data/nativetest64/mediatranscodingservice_simulated_tests/mediatranscodingservice_simulated_tests
+adb shell /data/nativetest/mediatranscodingservice_simulated_tests/mediatranscodingservice_simulated_tests
+
+echo "[==========] running real tests"
+adb shell setprop debug.transcoding.simulated_transcoder false
+adb shell kill -9 `pid media.transcoding`
+#adb shell /data/nativetest64/mediatranscodingservice_real_tests/mediatranscodingservice_real_tests
+adb shell /data/nativetest/mediatranscodingservice_real_tests/mediatranscodingservice_real_tests
+
+echo "[==========] running resource tests"
+adb shell kill -9 `pid media.transcoding`
+#adb shell /data/nativetest64/mediatranscodingservice_resource_tests/mediatranscodingservice_resource_tests
+adb shell /data/nativetest/mediatranscodingservice_resource_tests/mediatranscodingservice_resource_tests
+
+echo "[==========] removing debug properties"
+adb shell setprop debug.transcoding.simulated_transcoder \"\"
+adb shell kill -9 `pid media.transcoding`
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp
new file mode 100644
index 0000000..e9eebe2
--- /dev/null
+++ b/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp
@@ -0,0 +1,455 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Unit Test for MediaTranscodingService.
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaTranscodingServiceRealTest"
+
+#include "MediaTranscodingServiceTestHelper.h"
+
+/*
+ * Tests media transcoding service with real transcoder.
+ *
+ * Uses the same test assets as the MediaTranscoder unit tests. Before running the test,
+ * please make sure to push the test assets to /sdcard:
+ *
+ * adb push $TOP/frameworks/av/media/libmediatranscoding/transcoder/tests/assets /data/local/tmp/TranscodingTestAssets
+ */
+namespace android {
+
+namespace media {
+
+constexpr int64_t kPaddingUs = 1000000;
+constexpr int64_t kSessionWithPaddingUs = 10000000 + kPaddingUs;
+constexpr int32_t kBitRate = 8 * 1000 * 1000; // 8Mbs
+
+constexpr const char* kShortSrcPath =
+ "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
+constexpr const char* kLongSrcPath = "/data/local/tmp/TranscodingTestAssets/longtest_15s.mp4";
+
+#define OUTPATH(name) "/data/local/tmp/MediaTranscodingService_" #name ".MP4"
+
+class MediaTranscodingServiceRealTest : public MediaTranscodingServiceTestBase {
+public:
+ MediaTranscodingServiceRealTest() { ALOGI("MediaTranscodingServiceResourceTest created"); }
+
+ virtual ~MediaTranscodingServiceRealTest() {
+ ALOGI("MediaTranscodingServiceResourceTest destroyed");
+ }
+};
+
+TEST_F(MediaTranscodingServiceRealTest, TestInvalidSource) {
+ registerMultipleClients();
+
+ const char* srcPath = "bad_file_uri";
+ const char* dstPath = prepareOutputFile(OUTPATH(TestInvalidSource));
+
+ // Submit one session.
+ EXPECT_TRUE(
+ mClient1->submit(0, srcPath, dstPath, TranscodingSessionPriority::kNormal, kBitRate));
+
+ // Check expected error.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Failed(CLIENT(1), 0));
+ EXPECT_EQ(mClient1->getLastError(), TranscodingErrorCode::kErrorIO);
+
+ unregisterMultipleClients();
+}
+
+TEST_F(MediaTranscodingServiceRealTest, TestPassthru) {
+ registerMultipleClients();
+
+ const char* dstPath = prepareOutputFile(OUTPATH(TestPassthru));
+
+ // Submit one session.
+ EXPECT_TRUE(mClient1->submit(0, kShortSrcPath, dstPath));
+
+ // Wait for session to finish.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+ EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+
+ unregisterMultipleClients();
+}
+
+TEST_F(MediaTranscodingServiceRealTest, TestTranscodeVideo) {
+ registerMultipleClients();
+
+ const char* dstPath = prepareOutputFile(OUTPATH(TestTranscodeVideo));
+
+ // Submit one session.
+ EXPECT_TRUE(mClient1->submit(0, kShortSrcPath, dstPath, TranscodingSessionPriority::kNormal,
+ kBitRate));
+
+ // Wait for session to finish.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+ EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+
+ unregisterMultipleClients();
+}
+
+TEST_F(MediaTranscodingServiceRealTest, TestTranscodeVideoProgress) {
+ registerMultipleClients();
+
+ const char* dstPath = prepareOutputFile(OUTPATH(TestTranscodeVideoProgress));
+
+ // Submit one session.
+ EXPECT_TRUE(mClient1->submit(0, kLongSrcPath, dstPath, TranscodingSessionPriority::kNormal,
+ kBitRate));
+
+ // Wait for session to finish.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+ EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+
+ // Check the progress update messages are received. For this clip (around ~15 second long),
+ // expect at least 10 updates, and the last update should be 100.
+ int lastProgress;
+ EXPECT_GE(mClient1->getUpdateCount(&lastProgress), 10);
+ EXPECT_EQ(lastProgress, 100);
+
+ unregisterMultipleClients();
+}
+
+/*
+ * Test cancel immediately after start.
+ */
+TEST_F(MediaTranscodingServiceRealTest, TestCancelImmediately) {
+ registerMultipleClients();
+
+ const char* srcPath0 = kLongSrcPath;
+ const char* srcPath1 = kShortSrcPath;
+ const char* dstPath0 = prepareOutputFile(OUTPATH(TestCancelImmediately_Session0));
+ const char* dstPath1 = prepareOutputFile(OUTPATH(TestCancelImmediately_Session1));
+
+ // Submit one session, should start immediately.
+ EXPECT_TRUE(
+ mClient1->submit(0, srcPath0, dstPath0, TranscodingSessionPriority::kNormal, kBitRate));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+ EXPECT_TRUE(mClient1->getSession(0, srcPath0, dstPath0));
+
+ // Test cancel session immediately, getSession should fail after cancel.
+ EXPECT_TRUE(mClient1->cancel(0));
+ EXPECT_TRUE(mClient1->getSession<fail>(0, "", ""));
+
+ // Submit new session, new session should start immediately and finish.
+ EXPECT_TRUE(
+ mClient1->submit(1, srcPath1, dstPath1, TranscodingSessionPriority::kNormal, kBitRate));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
+ EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 1));
+
+ unregisterMultipleClients();
+}
+
+/*
+ * Test cancel in the middle of transcoding.
+ */
+TEST_F(MediaTranscodingServiceRealTest, TestCancelWhileRunning) {
+ registerMultipleClients();
+
+ const char* srcPath0 = kLongSrcPath;
+ const char* srcPath1 = kShortSrcPath;
+ const char* dstPath0 = prepareOutputFile(OUTPATH(TestCancelWhileRunning_Session0));
+ const char* dstPath1 = prepareOutputFile(OUTPATH(TestCancelWhileRunning_Session1));
+
+ // Submit two sessions, session 0 should start immediately, session 1 should be queued.
+ EXPECT_TRUE(
+ mClient1->submit(0, srcPath0, dstPath0, TranscodingSessionPriority::kNormal, kBitRate));
+ EXPECT_TRUE(
+ mClient1->submit(1, srcPath1, dstPath1, TranscodingSessionPriority::kNormal, kBitRate));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+ EXPECT_TRUE(mClient1->getSession(0, srcPath0, dstPath0));
+ EXPECT_TRUE(mClient1->getSession(1, srcPath1, dstPath1));
+
+ // Session 0 (longtest) shouldn't finish in 1 seconds.
+ EXPECT_EQ(mClient1->pop(1000000), EventTracker::NoEvent);
+
+ // Now cancel session 0. Session 1 should start immediately and finish.
+ EXPECT_TRUE(mClient1->cancel(0));
+ EXPECT_TRUE(mClient1->getSession<fail>(0, "", ""));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
+ EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 1));
+
+ unregisterMultipleClients();
+}
+
+TEST_F(MediaTranscodingServiceRealTest, TestPauseResumeSingleClient) {
+ registerMultipleClients();
+
+ const char* srcPath0 = kLongSrcPath;
+ const char* srcPath1 = kShortSrcPath;
+ const char* dstPath0 = prepareOutputFile(OUTPATH(TestPauseResumeSingleClient_Session0));
+ const char* dstPath1 = prepareOutputFile(OUTPATH(TestPauseResumeSingleClient_Session1));
+
+ // Submit one offline session, should start immediately.
+ EXPECT_TRUE(mClient1->submit(0, srcPath0, dstPath0, TranscodingSessionPriority::kUnspecified,
+ kBitRate));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+ // Test get session after starts.
+ EXPECT_TRUE(mClient1->getSession(0, srcPath0, dstPath0));
+
+ // Submit one realtime session.
+ EXPECT_TRUE(
+ mClient1->submit(1, srcPath1, dstPath1, TranscodingSessionPriority::kNormal, kBitRate));
+
+ // Offline session should pause.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Pause(CLIENT(1), 0));
+ EXPECT_TRUE(mClient1->getSession(0, srcPath0, dstPath0));
+
+ // Realtime session should start immediately, and run to finish.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
+ EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 1));
+
+ // Test get session after finish fails.
+ EXPECT_TRUE(mClient1->getSession<fail>(1, "", ""));
+
+ // Then offline session should resume.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Resume(CLIENT(1), 0));
+ // Test get session after resume.
+ EXPECT_TRUE(mClient1->getSession(0, srcPath0, dstPath0));
+
+ // Offline session should finish.
+ EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+ // Test get session after finish fails.
+ EXPECT_TRUE(mClient1->getSession<fail>(0, "", ""));
+
+ unregisterMultipleClients();
+}
+
+/*
+ * Basic test for pause/resume with two clients, with one session each.
+ * Top app's session should preempt the other app's session.
+ */
+TEST_F(MediaTranscodingServiceRealTest, TestPauseResumeMultiClients) {
+ ALOGD("TestPauseResumeMultiClients starting...");
+
+ dismissKeyguard();
+ stopAppPackages();
+
+ registerMultipleClients();
+
+ const char* srcPath0 = kLongSrcPath;
+ const char* srcPath1 = kShortSrcPath;
+ const char* dstPath0 = prepareOutputFile(OUTPATH(TestPauseResumeMultiClients_Client0));
+ const char* dstPath1 = prepareOutputFile(OUTPATH(TestPauseResumeMultiClients_Client1));
+
+ ALOGD("Moving app A to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
+
+ // Submit session to Client1.
+ ALOGD("Submitting session to client1 (app A) ...");
+ EXPECT_TRUE(
+ mClient1->submit(0, srcPath0, dstPath0, TranscodingSessionPriority::kNormal, kBitRate));
+
+ // Client1's session should start immediately.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+
+ ALOGD("Moving app B to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageB, kTestActivityName));
+
+ // Client1's session should continue to run, since Client2 (app B) doesn't have any session.
+ EXPECT_EQ(mClient1->pop(1000000), EventTracker::NoEvent);
+
+ // Submit session to Client2.
+ ALOGD("Submitting session to client2 (app B) ...");
+ EXPECT_TRUE(
+ mClient2->submit(0, srcPath1, dstPath1, TranscodingSessionPriority::kNormal, kBitRate));
+
+ // Client1's session should pause, client2's session should start.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Pause(CLIENT(1), 0));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Start(CLIENT(2), 0));
+
+ // Client2's session should finish, then Client1's session should resume.
+ EXPECT_EQ(mClient2->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(2), 0));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Resume(CLIENT(1), 0));
+
+ // Client1's session should finish.
+ EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+
+ unregisterMultipleClients();
+
+ stopAppPackages();
+
+ ALOGD("TestPauseResumeMultiClients finished.");
+}
+
+TEST_F(MediaTranscodingServiceRealTest, TestUidGoneForeground) {
+ ALOGD("TestUidGoneForeground starting...");
+
+ dismissKeyguard();
+ stopAppPackages();
+
+ registerMultipleClients();
+
+ const char* dstPath0 = prepareOutputFile(OUTPATH(TestUidGoneForegroundSession0));
+ const char* dstPath1 = prepareOutputFile(OUTPATH(TestUidGoneForegroundSession1));
+
+ // Test kill foreground app, using only 1 uid.
+ ALOGD("Moving app A to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
+
+ // Submit sessions to Client1 (app A).
+ ALOGD("Submitting sessions to client1 (app A) ...");
+ EXPECT_TRUE(mClient1->submit(0, kLongSrcPath, dstPath0, TranscodingSessionPriority::kNormal,
+ kBitRate));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+ EXPECT_TRUE(mClient1->submit(1, kLongSrcPath, dstPath1, TranscodingSessionPriority::kNormal,
+ kBitRate));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::NoEvent);
+
+ // Kill app A, expect to see A's session pause followed by B's session start,
+ // then A's session cancelled with error code kUidGoneCancelled.
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Failed(CLIENT(1), 0));
+ EXPECT_EQ(mClient1->getLastError(), TranscodingErrorCode::kUidGoneCancelled);
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Failed(CLIENT(1), 1));
+ EXPECT_EQ(mClient1->getLastError(), TranscodingErrorCode::kUidGoneCancelled);
+
+ unregisterMultipleClients();
+
+ stopAppPackages();
+
+ ALOGD("TestUidGoneForeground finished.");
+}
+
+TEST_F(MediaTranscodingServiceRealTest, TestUidGoneForegroundMultiUids) {
+ ALOGD("TestUidGoneForegroundMultiUids starting...");
+
+ dismissKeyguard();
+ stopAppPackages();
+
+ registerMultipleClients();
+
+ const char* dstPath0 = prepareOutputFile(OUTPATH(TestUidGoneForegroundSession0));
+ const char* dstPath1 = prepareOutputFile(OUTPATH(TestUidGoneForegroundSession1));
+
+ // Test kill foreground app, using two uids.
+ ALOGD("Moving app B to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageB, kTestActivityName));
+ EXPECT_TRUE(mClient2->submit(0, kLongSrcPath, dstPath0, TranscodingSessionPriority::kNormal,
+ kBitRate));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Start(CLIENT(2), 0));
+ EXPECT_TRUE(mClient2->submit(1, kLongSrcPath, dstPath1, TranscodingSessionPriority::kNormal,
+ kBitRate));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::NoEvent);
+ // Make app A also requesting session 1.
+ EXPECT_TRUE(mClient2->addClientUid(1, mClient1->mClientUid));
+
+ ALOGD("Moving app A to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Pause(CLIENT(2), 0));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Start(CLIENT(2), 1));
+
+ // Kill app A, CLIENT(2)'s session 1 should continue because it's also requested by app B.
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::NoEvent);
+
+ // Kill app B, sessions should be cancelled.
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageB));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Failed(CLIENT(2), 0));
+ EXPECT_EQ(mClient2->getLastError(), TranscodingErrorCode::kUidGoneCancelled);
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Failed(CLIENT(2), 1));
+ EXPECT_EQ(mClient2->getLastError(), TranscodingErrorCode::kUidGoneCancelled);
+
+ unregisterMultipleClients();
+
+ stopAppPackages();
+
+ ALOGD("TestUidGoneForegroundMultiUids finished.");
+}
+TEST_F(MediaTranscodingServiceRealTest, TestUidGoneBackground) {
+ ALOGD("TestUidGoneBackground starting...");
+
+ dismissKeyguard();
+ stopAppPackages();
+
+ registerMultipleClients();
+
+ const char* dstPath0 = prepareOutputFile(OUTPATH(TestUidGoneForegroundSession0));
+ const char* dstPath1 = prepareOutputFile(OUTPATH(TestUidGoneForegroundSession1));
+
+ // Test kill background app, using two uids.
+ ALOGD("Moving app B to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageB, kTestActivityName));
+ EXPECT_TRUE(mClient2->submit(0, kLongSrcPath, dstPath0, TranscodingSessionPriority::kNormal,
+ kBitRate));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Start(CLIENT(2), 0));
+ EXPECT_TRUE(mClient2->submit(1, kLongSrcPath, dstPath1, TranscodingSessionPriority::kNormal,
+ kBitRate));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::NoEvent);
+
+ ALOGD("Moving app A to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
+ EXPECT_TRUE(mClient1->submit(0, kLongSrcPath, dstPath0, TranscodingSessionPriority::kNormal,
+ kBitRate));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Pause(CLIENT(2), 0));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+
+ // Kill app B, all its sessions should be cancelled.
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageB));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Failed(CLIENT(2), 0));
+ EXPECT_EQ(mClient2->getLastError(), TranscodingErrorCode::kUidGoneCancelled);
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Failed(CLIENT(2), 1));
+ EXPECT_EQ(mClient2->getLastError(), TranscodingErrorCode::kUidGoneCancelled);
+
+ unregisterMultipleClients();
+
+ stopAppPackages();
+
+ ALOGD("TestUidGoneBackground finished.");
+}
+
+TEST_F(MediaTranscodingServiceRealTest, TestUidGoneBackgroundMultiUids) {
+ ALOGD("TestUidGoneBackgroundMultiUids starting...");
+
+ dismissKeyguard();
+ stopAppPackages();
+
+ registerMultipleClients();
+
+ const char* dstPath0 = prepareOutputFile(OUTPATH(TestUidGoneForegroundSession0));
+ const char* dstPath1 = prepareOutputFile(OUTPATH(TestUidGoneForegroundSession1));
+
+ // Test kill background app, using two uids.
+ ALOGD("Moving app B to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageB, kTestActivityName));
+ EXPECT_TRUE(mClient2->submit(0, kLongSrcPath, dstPath0, TranscodingSessionPriority::kNormal,
+ kBitRate));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Start(CLIENT(2), 0));
+ EXPECT_TRUE(mClient2->submit(1, kLongSrcPath, dstPath1, TranscodingSessionPriority::kNormal,
+ kBitRate));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::NoEvent);
+ // Make app A also requesting session 1.
+ EXPECT_TRUE(mClient2->addClientUid(1, mClient1->mClientUid));
+
+ ALOGD("Moving app A to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Pause(CLIENT(2), 0));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Start(CLIENT(2), 1));
+
+ // Kill app B, CLIENT(2)'s session 1 should continue to run, session 0 on
+ // the other hand should be cancelled.
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageB));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Failed(CLIENT(2), 0));
+ EXPECT_EQ(mClient2->getLastError(), TranscodingErrorCode::kUidGoneCancelled);
+
+ unregisterMultipleClients();
+
+ stopAppPackages();
+
+ ALOGD("TestUidGoneBackgroundMultiUids finished.");
+}
+
+} // namespace media
+} // namespace android
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_resource_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_resource_tests.cpp
new file mode 100644
index 0000000..790e80b
--- /dev/null
+++ b/services/mediatranscoding/tests/mediatranscodingservice_resource_tests.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Unit Test for MediaTranscodingService.
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaTranscodingServiceResourceTest"
+
+#include <aidl/android/media/BnResourceManagerClient.h>
+#include <aidl/android/media/IResourceManagerService.h>
+#include <binder/ActivityManager.h>
+
+#include "MediaTranscodingServiceTestHelper.h"
+
+/*
+ * Tests media transcoding service with real transcoder.
+ *
+ * Uses the same test assets as the MediaTranscoder unit tests. Before running the test,
+ * please make sure to push the test assets to /sdcard:
+ *
+ * adb push $TOP/frameworks/av/media/libmediatranscoding/transcoder/tests/assets /data/local/tmp/TranscodingTestAssets
+ */
+namespace android {
+
+namespace media {
+
+constexpr int64_t kPaddingUs = 400000;
+constexpr int32_t kBitRate = 8 * 1000 * 1000; // 8Mbs
+
+constexpr const char* kLongSrcPath = "/data/local/tmp/TranscodingTestAssets/longtest_15s.mp4";
+
+constexpr const char* kResourcePolicyTestActivity =
+ "/com.android.tests.transcoding.ResourcePolicyTestActivity";
+
+#define OUTPATH(name) "/data/local/tmp/MediaTranscodingService_" #name ".MP4"
+
+/*
+ * The OOM score we're going to ask ResourceManager to use for our native transcoding
+ * service. ResourceManager issues reclaims based on these scores. It gets the scores
+ * from ActivityManagerService, which doesn't track native services. The values of the
+ * OOM scores are defined in:
+ * frameworks/base/services/core/java/com/android/server/am/ProcessList.java
+ * We use SERVICE_ADJ which is lower priority than an app possibly visible to the
+ * user, but higher priority than a cached app (which could be killed without disruption
+ * to the user).
+ */
+constexpr static int32_t SERVICE_ADJ = 500;
+
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::media::BnResourceManagerClient;
+using aidl::android::media::IResourceManagerService;
+
+/*
+ * Placeholder ResourceManagerClient for registering process info override
+ * with the IResourceManagerService. This is only used as a token by the service
+ * to get notifications about binder death, not used for reclaiming resources.
+ */
+struct ResourceManagerClient : public BnResourceManagerClient {
+ explicit ResourceManagerClient() = default;
+
+ Status reclaimResource(bool* _aidl_return) override {
+ *_aidl_return = false;
+ return Status::ok();
+ }
+
+ Status getName(::std::string* _aidl_return) override {
+ _aidl_return->clear();
+ return Status::ok();
+ }
+
+ virtual ~ResourceManagerClient() = default;
+};
+
+static std::shared_ptr<ResourceManagerClient> gResourceManagerClient =
+ ::ndk::SharedRefBase::make<ResourceManagerClient>();
+
+void TranscodingHelper_setProcessInfoOverride(int32_t procState, int32_t oomScore) {
+ ::ndk::SpAIBinder binder(AServiceManager_getService("media.resource_manager"));
+ std::shared_ptr<IResourceManagerService> service = IResourceManagerService::fromBinder(binder);
+ if (service == nullptr) {
+ ALOGE("Failed to get IResourceManagerService");
+ return;
+ }
+ Status status =
+ service->overrideProcessInfo(gResourceManagerClient, getpid(), procState, oomScore);
+ if (!status.isOk()) {
+ ALOGW("Failed to setProcessInfoOverride.");
+ }
+}
+
+class MediaTranscodingServiceResourceTest : public MediaTranscodingServiceTestBase {
+public:
+ MediaTranscodingServiceResourceTest() { ALOGI("MediaTranscodingServiceResourceTest created"); }
+
+ virtual ~MediaTranscodingServiceResourceTest() {
+ ALOGI("MediaTranscodingServiceResourceTest destroyed");
+ }
+};
+
+/**
+ * Basic testing for handling resource lost.
+ *
+ * This test starts a transcoding session (that's somewhat long and takes several seconds),
+ * then launches an activity that allocates video codec instances until it hits insufficient
+ * resource error. Because the activity is running in foreground,
+ * ResourceManager would reclaim codecs from transcoding service which should
+ * cause the session to be paused. The activity will hold the codecs for a few seconds
+ * before releasing them, and the transcoding service should be able to resume
+ * and complete the session.
+ *
+ * Note that this test must run as root. We need to simulate submitting a request for a
+ * client {uid,pid} running at lower priority. As a cmd line test, it's not easy to get the
+ * pid of a living app, so we use our own {uid,pid} to submit. However, since we're a native
+ * process, RM doesn't have our proc info and the reclaim will fail. So we need to use
+ * RM's setProcessInfoOverride to override our proc info, which requires permission (unless root).
+ */
+TEST_F(MediaTranscodingServiceResourceTest, TestResourceLost) {
+ ALOGD("TestResourceLost starting..., pid %d", ::getpid());
+
+ // We're going to submit the request using our own {uid,pid}. Since we're a native
+ // process, RM doesn't have our proc info and the reclaim will fail. So we need to use
+ // RM's setProcessInfoOverride to override our proc info.
+ TranscodingHelper_setProcessInfoOverride(ActivityManager::PROCESS_STATE_SERVICE, SERVICE_ADJ);
+
+ EXPECT_TRUE(ShellHelper::RunCmd("input keyevent KEYCODE_WAKEUP"));
+ EXPECT_TRUE(ShellHelper::RunCmd("wm dismiss-keyguard"));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
+
+ registerMultipleClients();
+
+ const char* srcPath0 = kLongSrcPath;
+ const char* dstPath0 = OUTPATH(TestPauseResumeMultiClients_Client0);
+ deleteFile(dstPath0);
+
+ ALOGD("Moving app A to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
+
+ // Submit session to Client1.
+ ALOGD("Submitting session to client1 (app A) ...");
+ EXPECT_TRUE(mClient1->submit(0, srcPath0, dstPath0, TranscodingSessionPriority::kNormal,
+ kBitRate, ::getpid(), ::getuid()));
+
+ // Client1's session should start immediately.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+
+ // Launch ResourcePolicyTestActivity, which will try to allocate up to 32
+ // instances, which should trigger insufficient resources on most devices.
+ // (Note that it's possible that the device supports a very high number of
+ // resource instances, in which case we'll simply require that the session completes.)
+ ALOGD("Launch ResourcePolicyTestActivity...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kResourcePolicyTestActivity));
+
+ // The basic requirement is that the session should complete. Wait for finish
+ // event to come and pop up all events received.
+ std::list<EventTracker::Event> events;
+ EXPECT_TRUE(mClient1->waitForSpecificEventAndPop(EventTracker::Finished(CLIENT(1), 0), &events,
+ 15000000));
+
+ // If there is only 1 event, it must be finish (otherwise waitForSpecificEventAndPop
+ // woudldn't pop up anything), and we're ok.
+ //
+ // TODO: If there is only 1 event (finish), and no pause/resume happened, we need
+ // to verify that the ResourcePolicyTestActivity actually was able to allocate
+ // all 32 instances without hitting insufficient resources. Otherwise, it could
+ // be that ResourceManager was not able to reclaim codecs from the transcoding
+ // service at all, which means the resource management is broken.
+ if (events.size() > 1) {
+ EXPECT_TRUE(events.size() >= 3);
+ size_t i = 0;
+ for (auto& event : events) {
+ if (i == 0) {
+ EXPECT_EQ(event, EventTracker::Pause(CLIENT(1), 0));
+ } else if (i == events.size() - 2) {
+ EXPECT_EQ(event, EventTracker::Resume(CLIENT(1), 0));
+ } else if (i == events.size() - 1) {
+ EXPECT_EQ(event, EventTracker::Finished(CLIENT(1), 0));
+ } else {
+ EXPECT_TRUE(event == EventTracker::Pause(CLIENT(1), 0) ||
+ event == EventTracker::Resume(CLIENT(1), 0));
+ }
+ i++;
+ }
+ }
+
+ unregisterMultipleClients();
+
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
+}
+
+} // namespace media
+} // namespace android
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
new file mode 100644
index 0000000..cb354f4
--- /dev/null
+++ b/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Unit Test for MediaTranscodingService.
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaTranscodingServiceSimulatedTest"
+
+#include <aidl/android/media/BnTranscodingClientCallback.h>
+#include <aidl/android/media/IMediaTranscodingService.h>
+#include <aidl/android/media/ITranscodingClient.h>
+#include <aidl/android/media/ITranscodingClientCallback.h>
+#include <aidl/android/media/TranscodingRequestParcel.h>
+#include <aidl/android/media/TranscodingSessionParcel.h>
+#include <aidl/android/media/TranscodingSessionPriority.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <binder/PermissionController.h>
+#include <cutils/multiuser.h>
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+#include <iostream>
+#include <list>
+#include <unordered_set>
+
+#include "MediaTranscodingServiceTestHelper.h"
+#include "SimulatedTranscoder.h"
+
+namespace android {
+
+namespace media {
+
+// Note that -1 is valid and means using calling pid/uid for the service. But only privilege caller
+// could use them. This test is not a privilege caller.
+constexpr int32_t kInvalidClientPid = -5;
+constexpr int32_t kInvalidClientUid = -10;
+constexpr const char* kInvalidClientName = "";
+constexpr const char* kInvalidClientOpPackageName = "";
+
+constexpr int64_t kPaddingUs = 1000000;
+constexpr int64_t kSessionWithPaddingUs = SimulatedTranscoder::kSessionDurationUs + kPaddingUs;
+constexpr int64_t kWatchdogTimeoutUs = 3000000;
+// Pacer settings used for simulated tests. Listed here for reference.
+constexpr int32_t kSimulatedPacerBurstThresholdMs = 500;
+//constexpr int32_t kSimulatedPacerBurstCountQuota = 10;
+//constexpr int32_t kSimulatedPacerBurstTimeQuotaSec = 3;
+
+constexpr const char* kClientOpPackageName = "TestClientPackage";
+
+class MediaTranscodingServiceSimulatedTest : public MediaTranscodingServiceTestBase {
+public:
+ MediaTranscodingServiceSimulatedTest() { ALOGI("MediaTranscodingServiceResourceTest created"); }
+
+ virtual ~MediaTranscodingServiceSimulatedTest() {
+ ALOGI("MediaTranscodingServiceResourceTest destroyed");
+ }
+
+ void testPacerHelper(int numSubmits, int sessionDurationMs, int expectedSuccess) {
+ // Idle to clear out burst history.
+ usleep(kSimulatedPacerBurstThresholdMs * 2 * 1000);
+ for (int i = 0; i < numSubmits; i++) {
+ EXPECT_TRUE(mClient3->submit(i, "test_source_file_0", "test_destination_file_0",
+ TranscodingSessionPriority::kNormal, -1 /*bitrateBps*/,
+ -1 /*overridePid*/, -1 /*overrideUid*/,
+ sessionDurationMs));
+ }
+ for (int i = 0; i < expectedSuccess; i++) {
+ EXPECT_EQ(mClient3->pop(kPaddingUs), EventTracker::Start(CLIENT(3), i));
+ EXPECT_EQ(mClient3->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(3), i));
+ }
+ for (int i = expectedSuccess; i < numSubmits; i++) {
+ EXPECT_EQ(mClient3->pop(kPaddingUs), EventTracker::Failed(CLIENT(3), i));
+ EXPECT_EQ(mClient3->getLastError(), TranscodingErrorCode::kDroppedByService);
+ }
+ }
+};
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestRegisterNullClient) {
+ std::shared_ptr<ITranscodingClient> client;
+
+ // Register the client with null callback.
+ Status status = mService->registerClient(nullptr, kClientName, kClientOpPackageName, &client);
+ EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestRegisterClientWithInvalidClientName) {
+ std::shared_ptr<ITranscodingClient> client;
+
+ // Register the client with the service.
+ Status status = mService->registerClient(mClient1, kInvalidClientName,
+ kInvalidClientOpPackageName, &client);
+ EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestRegisterClientWithInvalidClientPackageName) {
+ std::shared_ptr<ITranscodingClient> client;
+
+ // Register the client with the service.
+ Status status =
+ mService->registerClient(mClient1, kClientName, kInvalidClientOpPackageName, &client);
+ EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestRegisterOneClient) {
+ std::shared_ptr<ITranscodingClient> client;
+
+ Status status = mService->registerClient(mClient1, kClientName, kClientOpPackageName, &client);
+ EXPECT_TRUE(status.isOk());
+
+ // Validate the client.
+ EXPECT_TRUE(client != nullptr);
+
+ // Check the number of Clients.
+ int32_t numOfClients;
+ status = mService->getNumOfClients(&numOfClients);
+ EXPECT_TRUE(status.isOk());
+ EXPECT_GE(numOfClients, 1);
+
+ // Unregister the client.
+ status = client->unregister();
+ EXPECT_TRUE(status.isOk());
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestRegisterClientTwice) {
+ std::shared_ptr<ITranscodingClient> client;
+
+ Status status = mService->registerClient(mClient1, kClientName, kClientOpPackageName, &client);
+ EXPECT_TRUE(status.isOk());
+
+ // Validate the client.
+ EXPECT_TRUE(client != nullptr);
+
+ // Register the client again and expects failure.
+ std::shared_ptr<ITranscodingClient> client1;
+ status = mService->registerClient(mClient1, kClientName, kClientOpPackageName, &client1);
+ EXPECT_FALSE(status.isOk());
+
+ // Unregister the client.
+ status = client->unregister();
+ EXPECT_TRUE(status.isOk());
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestRegisterMultipleClients) {
+ registerMultipleClients();
+ unregisterMultipleClients();
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestSessionIdIndependence) {
+ registerMultipleClients();
+
+ // Submit 2 requests on client1 first.
+ EXPECT_TRUE(mClient1->submit(0, "test_source_file", "test_destination_file"));
+ EXPECT_TRUE(mClient1->submit(1, "test_source_file", "test_destination_file"));
+
+ // Submit 2 requests on client2, sessionId should be independent for each client.
+ EXPECT_TRUE(mClient2->submit(0, "test_source_file", "test_destination_file"));
+ EXPECT_TRUE(mClient2->submit(1, "test_source_file", "test_destination_file"));
+
+ // Cancel all sessions.
+ EXPECT_TRUE(mClient1->cancel(0));
+ EXPECT_TRUE(mClient1->cancel(1));
+ EXPECT_TRUE(mClient2->cancel(0));
+ EXPECT_TRUE(mClient2->cancel(1));
+
+ unregisterMultipleClients();
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestSubmitCancelSessions) {
+ registerMultipleClients();
+
+ // Test sessionId assignment.
+ EXPECT_TRUE(mClient1->submit(0, "test_source_file_0", "test_destination_file"));
+ EXPECT_TRUE(mClient1->submit(1, "test_source_file_1", "test_destination_file"));
+ EXPECT_TRUE(mClient1->submit(2, "test_source_file_2", "test_destination_file"));
+
+ // Test submit bad request (no valid sourceFilePath) fails.
+ EXPECT_TRUE(mClient1->submit<fail>(0, "", ""));
+
+ // Test submit bad request (no valid sourceFilePath) fails.
+ EXPECT_TRUE(mClient1->submit<fail>(0, "src", "dst", TranscodingSessionPriority::kNormal,
+ 1000000, kInvalidClientPid, kInvalidClientUid));
+
+ // Test cancel non-existent session fails.
+ EXPECT_TRUE(mClient1->cancel<fail>(100));
+
+ // Session 0 should start immediately and finish in 2 seconds, followed by Session 1 start.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+ EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
+
+ // Test cancel valid sessionId in random order.
+ // Test cancel finished session fails.
+ EXPECT_TRUE(mClient1->cancel(2));
+ EXPECT_TRUE(mClient1->cancel<fail>(0));
+ EXPECT_TRUE(mClient1->cancel(1));
+
+ // Test cancel session again fails.
+ EXPECT_TRUE(mClient1->cancel<fail>(1));
+
+ // Test no more events arriving after cancel.
+ EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::NoEvent);
+
+ unregisterMultipleClients();
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestGetSessions) {
+ registerMultipleClients();
+
+ // Submit 3 requests.
+ EXPECT_TRUE(mClient1->submit(0, "test_source_file_0", "test_destination_file_0"));
+ EXPECT_TRUE(mClient1->submit(1, "test_source_file_1", "test_destination_file_1"));
+ EXPECT_TRUE(mClient1->submit(2, "test_source_file_2", "test_destination_file_2"));
+
+ // Test get sessions by id.
+ EXPECT_TRUE(mClient1->getSession(2, "test_source_file_2", "test_destination_file_2"));
+ EXPECT_TRUE(mClient1->getSession(1, "test_source_file_1", "test_destination_file_1"));
+ EXPECT_TRUE(mClient1->getSession(0, "test_source_file_0", "test_destination_file_0"));
+
+ // Test get session by invalid id fails.
+ EXPECT_TRUE(mClient1->getSession<fail>(100, "", ""));
+ EXPECT_TRUE(mClient1->getSession<fail>(-1, "", ""));
+
+ // Test get session after cancel fails.
+ EXPECT_TRUE(mClient1->cancel(2));
+ EXPECT_TRUE(mClient1->getSession<fail>(2, "", ""));
+
+ // Session 0 should start immediately and finish in 2 seconds, followed by Session 1 start.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+ EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
+
+ // Test get session after finish fails.
+ EXPECT_TRUE(mClient1->getSession<fail>(0, "", ""));
+
+ // Test get the remaining session 1.
+ EXPECT_TRUE(mClient1->getSession(1, "test_source_file_1", "test_destination_file_1"));
+
+ // Cancel remaining session 1.
+ EXPECT_TRUE(mClient1->cancel(1));
+
+ unregisterMultipleClients();
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestAddGetClientUids) {
+ registerMultipleClients();
+
+ std::vector<int32_t> clientUids;
+ TranscodingRequestParcel request;
+ TranscodingSessionParcel session;
+ uid_t ownUid = ::getuid();
+
+ // Submit one real-time session.
+ EXPECT_TRUE(mClient1->submit(0, "test_source_file_0", "test_destination_file"));
+
+ // Should have mClientUid in client uid list.
+ EXPECT_TRUE(mClient1->getClientUids(0, &clientUids));
+ EXPECT_EQ(clientUids.size(), 1u);
+ EXPECT_EQ(clientUids[0], (int32_t)mClient1->mClientUid);
+
+ // Adding invalid client uid should fail.
+ EXPECT_TRUE(mClient1->addClientUid<fail>(0, kInvalidClientUid));
+
+ // Adding mClientUid again should fail.
+ EXPECT_TRUE(mClient1->addClientUid<fail>(0, mClient1->mClientUid));
+
+ // Submit one offline session.
+ EXPECT_TRUE(mClient1->submit(1, "test_source_file_1", "test_destination_file_1",
+ TranscodingSessionPriority::kUnspecified));
+
+ // Should not have any uids in client uid list.
+ EXPECT_TRUE(mClient1->getClientUids(1, &clientUids));
+ EXPECT_EQ(clientUids.size(), 0u);
+
+ // Add own uid (with IMediaTranscodingService::USE_CALLING_UID), should succeed.
+ EXPECT_TRUE(mClient1->addClientUid(1, IMediaTranscodingService::USE_CALLING_UID));
+ EXPECT_TRUE(mClient1->getClientUids(1, &clientUids));
+ EXPECT_EQ(clientUids.size(), 1u);
+ EXPECT_EQ(clientUids[0], (int32_t)ownUid);
+
+ // Adding mClientUid should succeed.
+ EXPECT_TRUE(mClient1->addClientUid(1, mClient1->mClientUid));
+ EXPECT_TRUE(mClient1->getClientUids(1, &clientUids));
+ std::unordered_set<uid_t> uidSet;
+ uidSet.insert(clientUids.begin(), clientUids.end());
+ EXPECT_EQ(uidSet.size(), 2u);
+ EXPECT_EQ(uidSet.count(ownUid), 1u);
+ EXPECT_EQ(uidSet.count(mClient1->mClientUid), 1u);
+
+ unregisterMultipleClients();
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestSubmitCancelWithOfflineSessions) {
+ registerMultipleClients();
+
+ // Submit some offline sessions first.
+ EXPECT_TRUE(mClient1->submit(0, "test_source_file_0", "test_destination_file_0",
+ TranscodingSessionPriority::kUnspecified));
+ EXPECT_TRUE(mClient1->submit(1, "test_source_file_1", "test_destination_file_1",
+ TranscodingSessionPriority::kUnspecified));
+
+ // Session 0 should start immediately.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+
+ // Submit more real-time sessions.
+ EXPECT_TRUE(mClient1->submit(2, "test_source_file_2", "test_destination_file_2"));
+ EXPECT_TRUE(mClient1->submit(3, "test_source_file_3", "test_destination_file_3"));
+
+ // Session 0 should pause immediately and session 2 should start.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Pause(CLIENT(1), 0));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 2));
+
+ // Session 2 should finish in 2 seconds and session 3 should start.
+ EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 2));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 3));
+
+ // Cancel session 3 now
+ EXPECT_TRUE(mClient1->cancel(3));
+
+ // Session 0 should resume and finish in 2 seconds, followed by session 1 start.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Resume(CLIENT(1), 0));
+ EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
+
+ // Cancel remaining session 1.
+ EXPECT_TRUE(mClient1->cancel(1));
+
+ unregisterMultipleClients();
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestClientUseAfterUnregister) {
+ std::shared_ptr<ITranscodingClient> client;
+
+ // Register a client, then unregister.
+ Status status = mService->registerClient(mClient1, kClientName, kClientOpPackageName, &client);
+ EXPECT_TRUE(status.isOk());
+
+ status = client->unregister();
+ EXPECT_TRUE(status.isOk());
+
+ // Test various operations on the client, should fail with ERROR_DISCONNECTED.
+ TranscodingSessionParcel session;
+ bool result;
+ status = client->getSessionWithId(0, &session, &result);
+ EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
+
+ status = client->cancelSession(0, &result);
+ EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
+
+ TranscodingRequestParcel request;
+ status = client->submitRequest(request, &session, &result);
+ EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestTranscodingUidPolicy) {
+ ALOGD("TestTranscodingUidPolicy starting...");
+
+ EXPECT_TRUE(ShellHelper::RunCmd("input keyevent KEYCODE_WAKEUP"));
+ EXPECT_TRUE(ShellHelper::RunCmd("wm dismiss-keyguard"));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageB));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageC));
+
+ registerMultipleClients();
+
+ ALOGD("Moving app A to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
+
+ // Submit 3 requests.
+ ALOGD("Submitting session to client1 (app A) ...");
+ EXPECT_TRUE(mClient1->submit(0, "test_source_file_0", "test_destination_file_0"));
+ EXPECT_TRUE(mClient1->submit(1, "test_source_file_1", "test_destination_file_1"));
+ EXPECT_TRUE(mClient1->submit(2, "test_source_file_2", "test_destination_file_2"));
+
+ // Session 0 should start immediately.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+
+ ALOGD("Moving app B to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageB, kTestActivityName));
+
+ // Session 0 should continue and finish in 2 seconds, then session 1 should start.
+ EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
+
+ ALOGD("Submitting session to client2 (app B) ...");
+ EXPECT_TRUE(mClient2->submit(0, "test_source_file_0", "test_destination_file_0"));
+
+ // Client1's session should pause, client2's session should start.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Pause(CLIENT(1), 1));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Start(CLIENT(2), 0));
+
+ ALOGD("Moving app A back to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
+
+ // Client2's session should pause, client1's session 1 should resume.
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Pause(CLIENT(2), 0));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Resume(CLIENT(1), 1));
+
+ // Client2's session 1 should finish in 2 seconds, then its session 2 should start.
+ EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 1));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 2));
+
+ // After client2's sessions finish, client1's session should resume.
+ EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 2));
+ EXPECT_EQ(mClient2->pop(kPaddingUs), EventTracker::Resume(CLIENT(2), 0));
+
+ unregisterMultipleClients();
+
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageB));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageC));
+
+ ALOGD("TestTranscodingUidPolicy finished.");
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestTranscodingUidPolicyWithMultipleClientUids) {
+ ALOGD("TestTranscodingUidPolicyWithMultipleClientUids starting...");
+
+ EXPECT_TRUE(ShellHelper::RunCmd("input keyevent KEYCODE_WAKEUP"));
+ EXPECT_TRUE(ShellHelper::RunCmd("wm dismiss-keyguard"));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageB));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageC));
+
+ registerMultipleClients();
+
+ ALOGD("Moving app A to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
+
+ // Submit 3 requests.
+ ALOGD("Submitting session to client1 (app A)...");
+ EXPECT_TRUE(mClient1->submit(0, "test_source_file_0", "test_destination_file_0"));
+ EXPECT_TRUE(mClient1->submit(1, "test_source_file_1", "test_destination_file_1"));
+ EXPECT_TRUE(mClient1->submit(2, "test_source_file_2", "test_destination_file_2"));
+
+ // mClient1's Session 0 should start immediately.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+
+ // Add client2 (app B)'s uid to mClient1's session 1.
+ EXPECT_TRUE(mClient1->addClientUid(1, mClient2->mClientUid));
+
+ ALOGD("Moving app B to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageB, kTestActivityName));
+
+ // mClient1's session 0 should pause, session 1 should start.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Pause(CLIENT(1), 0));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
+
+ ALOGD("Moving app A back to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
+ EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 1));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Resume(CLIENT(1), 0));
+
+ unregisterMultipleClients();
+
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageB));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageC));
+
+ ALOGD("TestTranscodingUidPolicyWithMultipleClientUids finished.");
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestTranscodingThermalPolicy) {
+ ALOGD("TestTranscodingThermalPolicy starting...");
+
+ registerMultipleClients();
+
+ // Submit request, should start immediately.
+ EXPECT_TRUE(mClient1->submit(0, "test_source_file_0", "test_destination_file_0"));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+
+ // Now, simulate thermal status change by adb cmd. The status code is as defined in
+ // frameworks/native/include/android/thermal.h.
+ // ATHERMAL_STATUS_SEVERE(3): should start throttling.
+ EXPECT_TRUE(ShellHelper::RunCmd("cmd thermalservice override-status 3"));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Pause(CLIENT(1), 0));
+
+ // ATHERMAL_STATUS_CRITICAL(4): shouldn't start throttling again (already started).
+ EXPECT_TRUE(ShellHelper::RunCmd("cmd thermalservice override-status 4"));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::NoEvent);
+
+ // ATHERMAL_STATUS_MODERATE(2): should stop throttling.
+ EXPECT_TRUE(ShellHelper::RunCmd("cmd thermalservice override-status 2"));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Resume(CLIENT(1), 0));
+
+ // ATHERMAL_STATUS_LIGHT(1): shouldn't stop throttling again (already stopped).
+ EXPECT_TRUE(ShellHelper::RunCmd("cmd thermalservice override-status 1"));
+ EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 0));
+
+ unregisterMultipleClients();
+
+ ALOGD("TestTranscodingThermalPolicy finished.");
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestTranscodingWatchdog) {
+ ALOGD("TestTranscodingWatchdog starting...");
+
+ registerMultipleClients();
+
+ // SimulatedTranscoder itself does not send heartbeat. Its sessions last 1sec
+ // by default, so timeout will not happen normally.
+ // Here we run a session of 4000ms with TranscodingTestConfig. This will trigger
+ // a watchdog timeout on server side. We use it to check that error code is correct.
+ EXPECT_TRUE(mClient1->submit(
+ 0, "test_source_file_0", "test_destination_file_0", TranscodingSessionPriority::kNormal,
+ -1 /*bitrateBps*/, -1 /*overridePid*/, -1 /*overrideUid*/, 4000 /*sessionDurationMs*/));
+ EXPECT_EQ(mClient1->pop(100000), EventTracker::Start(CLIENT(1), 0));
+ EXPECT_EQ(mClient1->pop(kWatchdogTimeoutUs - 100000), EventTracker::NoEvent);
+ EXPECT_EQ(mClient1->pop(200000), EventTracker::Failed(CLIENT(1), 0));
+ EXPECT_EQ(mClient1->getLastError(), TranscodingErrorCode::kWatchdogTimeout);
+
+ // After the timeout, submit another request and check it's finished.
+ EXPECT_TRUE(mClient1->submit(1, "test_source_file_1", "test_destination_file_1"));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
+ EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 1));
+
+ unregisterMultipleClients();
+
+ ALOGD("TestTranscodingWatchdog finished.");
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestTranscodingPacerOverCountQuotaOnly) {
+ ALOGD("TestTranscodingPacerOverCountQuotaOnly starting...");
+
+ registerMultipleClients();
+ testPacerHelper(12 /*numSubmits*/, 100 /*sessionDurationMs*/, 12 /*expectedSuccess*/);
+ unregisterMultipleClients();
+
+ ALOGD("TestTranscodingPacerOverCountQuotaOnly finished.");
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestTranscodingPacerOverTimeQuotaOnly) {
+ ALOGD("TestTranscodingPacerOverTimeQuotaOnly starting...");
+
+ registerMultipleClients();
+ testPacerHelper(5 /*numSubmits*/, 1000 /*sessionDurationMs*/, 5 /*expectedSuccess*/);
+ unregisterMultipleClients();
+
+ ALOGD("TestTranscodingPacerOverTimeQuotaOnly finished.");
+}
+
+TEST_F(MediaTranscodingServiceSimulatedTest, TestTranscodingPacerOverQuota) {
+ ALOGD("TestTranscodingPacerOverQuota starting...");
+
+ registerMultipleClients();
+ testPacerHelper(12 /*numSubmits*/, 400 /*sessionDurationMs*/, 10 /*expectedSuccess*/);
+ unregisterMultipleClients();
+
+ // Idle to clear out burst history. Since we expect it to actually fail, wait for cooldown.
+ ALOGD("TestTranscodingPacerOverQuota finished.");
+}
+
+} // namespace media
+} // namespace android
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_tests.cpp
deleted file mode 100644
index 5a791fe..0000000
--- a/services/mediatranscoding/tests/mediatranscodingservice_tests.cpp
+++ /dev/null
@@ -1,239 +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.
- */
-
-// Unit Test for MediaTranscoding Service.
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "MediaTranscodingServiceTest"
-
-#include <aidl/android/media/BnTranscodingServiceClient.h>
-#include <aidl/android/media/IMediaTranscodingService.h>
-#include <aidl/android/media/ITranscodingServiceClient.h>
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <android/binder_ibinder_jni.h>
-#include <android/binder_manager.h>
-#include <android/binder_process.h>
-#include <cutils/ashmem.h>
-#include <gtest/gtest.h>
-#include <stdlib.h>
-#include <sys/mman.h>
-#include <utils/Log.h>
-
-namespace android {
-
-namespace media {
-
-using Status = ::ndk::ScopedAStatus;
-using aidl::android::media::BnTranscodingServiceClient;
-using aidl::android::media::IMediaTranscodingService;
-using aidl::android::media::ITranscodingServiceClient;
-
-constexpr int32_t kInvalidClientId = -5;
-
-// Note that -1 is valid and means using calling pid/uid for the service. But only privilege caller could
-// use them. This test is not a privilege caller.
-constexpr int32_t kInvalidClientPid = -5;
-constexpr int32_t kInvalidClientUid = -5;
-constexpr const char* kInvalidClientOpPackageName = "";
-
-constexpr int32_t kClientUseCallingPid = -1;
-constexpr int32_t kClientUseCallingUid = -1;
-constexpr const char* kClientOpPackageName = "TestClient";
-
-class MediaTranscodingServiceTest : public ::testing::Test {
-public:
- MediaTranscodingServiceTest() { ALOGD("MediaTranscodingServiceTest created"); }
-
- void SetUp() override {
- ::ndk::SpAIBinder binder(AServiceManager_getService("media.transcoding"));
- mService = IMediaTranscodingService::fromBinder(binder);
- if (mService == nullptr) {
- ALOGE("Failed to connect to the media.trascoding service.");
- return;
- }
- }
-
- ~MediaTranscodingServiceTest() { ALOGD("MediaTranscodingingServiceTest destroyed"); }
-
- std::shared_ptr<IMediaTranscodingService> mService = nullptr;
-};
-
-struct TestClient : public BnTranscodingServiceClient {
- TestClient(const std::shared_ptr<IMediaTranscodingService>& service) : mService(service) {
- ALOGD("TestClient Created");
- }
-
- Status getName(std::string* _aidl_return) override {
- *_aidl_return = "test_client";
- return Status::ok();
- }
-
- Status onTranscodingFinished(
- int32_t /* in_jobId */,
- const ::aidl::android::media::TranscodingResultParcel& /* in_result */) override {
- return Status::ok();
- }
-
- Status onTranscodingFailed(
- int32_t /* in_jobId */,
- ::aidl::android::media::TranscodingErrorCode /*in_errorCode */) override {
- return Status::ok();
- }
-
- Status onAwaitNumberOfJobsChanged(int32_t /* in_jobId */, int32_t /* in_oldAwaitNumber */,
- int32_t /* in_newAwaitNumber */) override {
- return Status::ok();
- }
-
- Status onProgressUpdate(int32_t /* in_jobId */, int32_t /* in_progress */) override {
- return Status::ok();
- }
-
- virtual ~TestClient() { ALOGI("TestClient destroyed"); };
-
-private:
- std::shared_ptr<IMediaTranscodingService> mService;
-};
-
-TEST_F(MediaTranscodingServiceTest, TestRegisterNullClient) {
- std::shared_ptr<ITranscodingServiceClient> client = nullptr;
- int32_t clientId = 0;
- Status status = mService->registerClient(client, kClientOpPackageName, kClientUseCallingUid,
- kClientUseCallingPid, &clientId);
- EXPECT_FALSE(status.isOk());
-}
-
-TEST_F(MediaTranscodingServiceTest, TestRegisterClientWithInvalidClientPid) {
- std::shared_ptr<ITranscodingServiceClient> client =
- ::ndk::SharedRefBase::make<TestClient>(mService);
- EXPECT_TRUE(client != nullptr);
-
- // Register the client with the service.
- int32_t clientId = 0;
- Status status = mService->registerClient(client, kClientOpPackageName, kClientUseCallingUid,
- kInvalidClientPid, &clientId);
- EXPECT_FALSE(status.isOk());
-}
-
-TEST_F(MediaTranscodingServiceTest, TestRegisterClientWithInvalidClientUid) {
- std::shared_ptr<ITranscodingServiceClient> client =
- ::ndk::SharedRefBase::make<TestClient>(mService);
- EXPECT_TRUE(client != nullptr);
-
- // Register the client with the service.
- int32_t clientId = 0;
- Status status = mService->registerClient(client, kClientOpPackageName, kInvalidClientUid,
- kClientUseCallingPid, &clientId);
- EXPECT_FALSE(status.isOk());
-}
-
-TEST_F(MediaTranscodingServiceTest, TestRegisterClientWithInvalidClientPackageName) {
- std::shared_ptr<ITranscodingServiceClient> client =
- ::ndk::SharedRefBase::make<TestClient>(mService);
- EXPECT_TRUE(client != nullptr);
-
- // Register the client with the service.
- int32_t clientId = 0;
- Status status = mService->registerClient(client, kInvalidClientOpPackageName,
- kClientUseCallingUid, kClientUseCallingPid, &clientId);
- EXPECT_FALSE(status.isOk());
-}
-
-TEST_F(MediaTranscodingServiceTest, TestRegisterOneClient) {
- std::shared_ptr<ITranscodingServiceClient> client =
- ::ndk::SharedRefBase::make<TestClient>(mService);
- EXPECT_TRUE(client != nullptr);
-
- // Register the client with the service.
- int32_t clientId = 0;
- Status status = mService->registerClient(client, kClientOpPackageName, kClientUseCallingPid,
- kClientUseCallingUid, &clientId);
- ALOGD("client id is %d", clientId);
- EXPECT_TRUE(status.isOk());
-
- // Validate the clientId.
- EXPECT_TRUE(clientId > 0);
-
- // Check the number of Clients.
- int32_t numOfClients;
- status = mService->getNumOfClients(&numOfClients);
- EXPECT_TRUE(status.isOk());
- EXPECT_EQ(1, numOfClients);
-
- // Unregister the client.
- bool res;
- status = mService->unregisterClient(clientId, &res);
- EXPECT_TRUE(status.isOk());
- EXPECT_TRUE(res);
-}
-
-TEST_F(MediaTranscodingServiceTest, TestUnRegisterClientWithInvalidClientId) {
- std::shared_ptr<ITranscodingServiceClient> client =
- ::ndk::SharedRefBase::make<TestClient>(mService);
- EXPECT_TRUE(client != nullptr);
-
- // Register the client with the service.
- int32_t clientId = 0;
- Status status = mService->registerClient(client, kClientOpPackageName, kClientUseCallingUid,
- kClientUseCallingPid, &clientId);
- ALOGD("client id is %d", clientId);
- EXPECT_TRUE(status.isOk());
-
- // Validate the clientId.
- EXPECT_TRUE(clientId > 0);
-
- // Check the number of Clients.
- int32_t numOfClients;
- status = mService->getNumOfClients(&numOfClients);
- EXPECT_TRUE(status.isOk());
- EXPECT_EQ(1, numOfClients);
-
- // Unregister the client with invalid ID
- bool res;
- mService->unregisterClient(kInvalidClientId, &res);
- EXPECT_FALSE(res);
-
- // Unregister the valid client.
- mService->unregisterClient(clientId, &res);
-}
-
-TEST_F(MediaTranscodingServiceTest, TestRegisterClientTwice) {
- std::shared_ptr<ITranscodingServiceClient> client =
- ::ndk::SharedRefBase::make<TestClient>(mService);
- EXPECT_TRUE(client != nullptr);
-
- // Register the client with the service.
- int32_t clientId = 0;
- Status status = mService->registerClient(client, kClientOpPackageName, kClientUseCallingUid,
- kClientUseCallingPid, &clientId);
- EXPECT_TRUE(status.isOk());
-
- // Validate the clientId.
- EXPECT_TRUE(clientId > 0);
-
- // Register the client again and expects failure.
- status = mService->registerClient(client, kClientOpPackageName, kClientUseCallingUid,
- kClientUseCallingPid, &clientId);
- EXPECT_FALSE(status.isOk());
-
- // Unregister the valid client.
- bool res;
- mService->unregisterClient(clientId, &res);
-}
-
-} // namespace media
-} // namespace android
diff --git a/services/oboeservice/AAudioClientTracker.cpp b/services/oboeservice/AAudioClientTracker.cpp
index 9d9ca63..054a896 100644
--- a/services/oboeservice/AAudioClientTracker.cpp
+++ b/services/oboeservice/AAudioClientTracker.cpp
@@ -41,7 +41,7 @@
: Singleton<AAudioClientTracker>() {
}
-std::string AAudioClientTracker::dump() const {
+std::string AAudioClientTracker::dump() const NO_THREAD_SAFETY_ANALYSIS {
std::stringstream result;
const bool isLocked = AAudio_tryUntilTrue(
[this]()->bool { return mLock.try_lock(); } /* f */,
@@ -198,7 +198,7 @@
for (const auto& serviceStream : streamsToClose) {
aaudio_handle_t handle = serviceStream->getHandle();
ALOGW("binderDied() close abandoned stream 0x%08X\n", handle);
- aaudioService->closeStream(handle);
+ aaudioService->asAAudioServiceInterface().closeStream(handle);
}
// mStreams should be empty now
}
@@ -207,7 +207,7 @@
}
-std::string AAudioClientTracker::NotificationClient::dump() const {
+std::string AAudioClientTracker::NotificationClient::dump() const NO_THREAD_SAFETY_ANALYSIS {
std::stringstream result;
const bool isLocked = AAudio_tryUntilTrue(
[this]()->bool { return mLock.try_lock(); } /* f */,
diff --git a/services/oboeservice/AAudioClientTracker.h b/services/oboeservice/AAudioClientTracker.h
index 943b809..2b38621 100644
--- a/services/oboeservice/AAudioClientTracker.h
+++ b/services/oboeservice/AAudioClientTracker.h
@@ -21,10 +21,11 @@
#include <mutex>
#include <set>
+#include <android-base/thread_annotations.h>
#include <utils/Singleton.h>
#include <aaudio/AAudio.h>
-#include "binding/IAAudioClient.h"
+#include <aaudio/IAAudioClient.h>
#include "AAudioService.h"
namespace aaudio {
@@ -46,7 +47,7 @@
*/
std::string dump() const;
- aaudio_result_t registerClient(pid_t pid, const android::sp<android::IAAudioClient>& client);
+ aaudio_result_t registerClient(pid_t pid, const android::sp<IAAudioClient>& client);
void unregisterClient(pid_t pid);
@@ -114,10 +115,12 @@
};
// This must be called under mLock
- android::sp<NotificationClient> getNotificationClient_l(pid_t pid);
+ android::sp<NotificationClient> getNotificationClient_l(pid_t pid)
+ REQUIRES(mLock);
mutable std::mutex mLock;
- std::map<pid_t, android::sp<NotificationClient>> mNotificationClients;
+ std::map<pid_t, android::sp<NotificationClient>> mNotificationClients
+ GUARDED_BY(mLock);
android::AAudioService *mAAudioService = nullptr;
};
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index 9f34153..3224cfc 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -24,6 +24,7 @@
#include <mutex>
#include <sstream>
#include <utility/AAudioUtilities.h>
+#include <media/AidlConversion.h>
#include "AAudioClientTracker.h"
#include "AAudioEndpointManager.h"
@@ -43,7 +44,7 @@
, mExclusiveStreams() {
}
-std::string AAudioEndpointManager::dump() const {
+std::string AAudioEndpointManager::dump() const NO_THREAD_SAFETY_ANALYSIS {
std::stringstream result;
int index = 0;
@@ -182,7 +183,9 @@
// and START calls. This will help preserve app compatibility.
// An app can avoid having this happen by closing their streams when
// the app is paused.
- AAudioClientTracker::getInstance().setExclusiveEnabled(request.getProcessId(), false);
+ pid_t pid = VALUE_OR_FATAL(
+ aidl2legacy_int32_t_pid_t(request.getIdentity().pid));
+ AAudioClientTracker::getInstance().setExclusiveEnabled(pid, false);
endpointToSteal = endpoint; // return it to caller
}
return nullptr;
@@ -306,6 +309,7 @@
mSharedStreams.end());
serviceEndpoint->close();
+
mSharedCloseCount++;
ALOGV("%s(%p) closed for device %d",
__func__, serviceEndpoint.get(), serviceEndpoint->getDeviceId());
diff --git a/services/oboeservice/AAudioEndpointManager.h b/services/oboeservice/AAudioEndpointManager.h
index ae776b1..b07bcef 100644
--- a/services/oboeservice/AAudioEndpointManager.h
+++ b/services/oboeservice/AAudioEndpointManager.h
@@ -20,6 +20,8 @@
#include <map>
#include <mutex>
#include <sys/types.h>
+
+#include <android-base/thread_annotations.h>
#include <utils/Singleton.h>
#include "binding/AAudioServiceMessage.h"
@@ -70,10 +72,12 @@
const aaudio::AAudioStreamRequest &request);
android::sp<AAudioServiceEndpoint> findExclusiveEndpoint_l(
- const AAudioStreamConfiguration& configuration);
+ const AAudioStreamConfiguration& configuration)
+ REQUIRES(mExclusiveLock);
android::sp<AAudioServiceEndpointShared> findSharedEndpoint_l(
- const AAudioStreamConfiguration& configuration);
+ const AAudioStreamConfiguration& configuration)
+ REQUIRES(mSharedLock);
void closeExclusiveEndpoint(android::sp<AAudioServiceEndpoint> serviceEndpoint);
void closeSharedEndpoint(android::sp<AAudioServiceEndpoint> serviceEndpoint);
@@ -83,23 +87,25 @@
// Lock mSharedLock before mExclusiveLock.
// it is OK to only lock mExclusiveLock.
mutable std::mutex mSharedLock;
- std::vector<android::sp<AAudioServiceEndpointShared>> mSharedStreams;
+ std::vector<android::sp<AAudioServiceEndpointShared>> mSharedStreams
+ GUARDED_BY(mSharedLock);
mutable std::mutex mExclusiveLock;
- std::vector<android::sp<AAudioServiceEndpointMMAP>> mExclusiveStreams;
+ std::vector<android::sp<AAudioServiceEndpointMMAP>> mExclusiveStreams
+ GUARDED_BY(mExclusiveLock);
- // Modified under a lock.
- int32_t mExclusiveSearchCount = 0; // number of times we SEARCHED for an exclusive endpoint
- int32_t mExclusiveFoundCount = 0; // number of times we FOUND an exclusive endpoint
- int32_t mExclusiveOpenCount = 0; // number of times we OPENED an exclusive endpoint
- int32_t mExclusiveCloseCount = 0; // number of times we CLOSED an exclusive endpoint
- int32_t mExclusiveStolenCount = 0; // number of times we STOLE an exclusive endpoint
+ // Counts related to an exclusive endpoint.
+ int32_t mExclusiveSearchCount GUARDED_BY(mExclusiveLock) = 0; // # SEARCHED
+ int32_t mExclusiveFoundCount GUARDED_BY(mExclusiveLock) = 0; // # FOUND
+ int32_t mExclusiveOpenCount GUARDED_BY(mExclusiveLock) = 0; // # OPENED
+ int32_t mExclusiveCloseCount GUARDED_BY(mExclusiveLock) = 0; // # CLOSED
+ int32_t mExclusiveStolenCount GUARDED_BY(mExclusiveLock) = 0; // # STOLEN
// Same as above but for SHARED endpoints.
- int32_t mSharedSearchCount = 0;
- int32_t mSharedFoundCount = 0;
- int32_t mSharedOpenCount = 0;
- int32_t mSharedCloseCount = 0;
+ int32_t mSharedSearchCount GUARDED_BY(mSharedLock) = 0;
+ int32_t mSharedFoundCount GUARDED_BY(mSharedLock) = 0;
+ int32_t mSharedOpenCount GUARDED_BY(mSharedLock) = 0;
+ int32_t mSharedCloseCount GUARDED_BY(mSharedLock) = 0;
// For easily disabling the stealing of exclusive streams.
static constexpr bool kStealingEnabled = true;
diff --git a/services/oboeservice/AAudioMixer.cpp b/services/oboeservice/AAudioMixer.cpp
index 1c03b7f..ad4b830 100644
--- a/services/oboeservice/AAudioMixer.cpp
+++ b/services/oboeservice/AAudioMixer.cpp
@@ -33,25 +33,21 @@
using android::FifoBuffer;
using android::fifo_frames_t;
-AAudioMixer::~AAudioMixer() {
- delete[] mOutputBuffer;
-}
-
void AAudioMixer::allocate(int32_t samplesPerFrame, int32_t framesPerBurst) {
mSamplesPerFrame = samplesPerFrame;
mFramesPerBurst = framesPerBurst;
int32_t samplesPerBuffer = samplesPerFrame * framesPerBurst;
- mOutputBuffer = new float[samplesPerBuffer];
+ mOutputBuffer = std::make_unique<float[]>(samplesPerBuffer);
mBufferSizeInBytes = samplesPerBuffer * sizeof(float);
}
void AAudioMixer::clear() {
- memset(mOutputBuffer, 0, mBufferSizeInBytes);
+ memset(mOutputBuffer.get(), 0, mBufferSizeInBytes);
}
-int32_t AAudioMixer::mix(int streamIndex, FifoBuffer *fifo, bool allowUnderflow) {
+int32_t AAudioMixer::mix(int streamIndex, std::shared_ptr<FifoBuffer> fifo, bool allowUnderflow) {
WrappingBuffer wrappingBuffer;
- float *destination = mOutputBuffer;
+ float *destination = mOutputBuffer.get();
#if AAUDIO_MIXER_ATRACE_ENABLED
ATRACE_BEGIN("aaMix");
@@ -117,5 +113,5 @@
}
float *AAudioMixer::getOutputBuffer() {
- return mOutputBuffer;
+ return mOutputBuffer.get();
}
diff --git a/services/oboeservice/AAudioMixer.h b/services/oboeservice/AAudioMixer.h
index d5abc5b..1a120f2 100644
--- a/services/oboeservice/AAudioMixer.h
+++ b/services/oboeservice/AAudioMixer.h
@@ -25,7 +25,6 @@
class AAudioMixer {
public:
AAudioMixer() {}
- ~AAudioMixer();
void allocate(int32_t samplesPerFrame, int32_t framesPerBurst);
@@ -38,7 +37,7 @@
* @param allowUnderflow if true then allow mixer to advance read index past the write index
* @return frames read from this stream
*/
- int32_t mix(int streamIndex, android::FifoBuffer *fifo, bool allowUnderflow);
+ int32_t mix(int streamIndex, std::shared_ptr<android::FifoBuffer> fifo, bool allowUnderflow);
float *getOutputBuffer();
@@ -47,7 +46,7 @@
private:
void mixPart(float *destination, float *source, int32_t numFrames);
- float *mOutputBuffer = nullptr;
+ std::unique_ptr<float[]> mOutputBuffer;
int32_t mSamplesPerFrame = 0;
int32_t mFramesPerBurst = 0;
int32_t mBufferSizeInBytes = 0;
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index 22cdb35..0b69bf6 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -22,7 +22,9 @@
#include <iostream>
#include <sstream>
+#include <android/media/permission/Identity.h>
#include <aaudio/AAudio.h>
+#include <media/AidlConversion.h>
#include <mediautils/ServiceUtilities.h>
#include <utils/String16.h>
@@ -32,26 +34,34 @@
#include "AAudioService.h"
#include "AAudioServiceStreamMMAP.h"
#include "AAudioServiceStreamShared.h"
-#include "binding/IAAudioService.h"
using namespace android;
using namespace aaudio;
#define MAX_STREAMS_PER_PROCESS 8
+#define AIDL_RETURN(x) { *_aidl_return = (x); return Status::ok(); }
+
+#define VALUE_OR_RETURN_ILLEGAL_ARG_STATUS(x) \
+ ({ auto _tmp = (x); \
+ if (!_tmp.ok()) AIDL_RETURN(AAUDIO_ERROR_ILLEGAL_ARGUMENT); \
+ std::move(_tmp.value()); })
using android::AAudioService;
+using android::media::permission::Identity;
+using binder::Status;
android::AAudioService::AAudioService()
- : BnAAudioService() {
- mAudioClient.clientUid = getuid(); // TODO consider using geteuid()
- mAudioClient.clientPid = getpid();
- mAudioClient.packageName = String16("");
+ : BnAAudioService(),
+ mAdapter(this) {
+ // TODO consider using geteuid()
+ // TODO b/182392769: use identity util
+ mAudioClient.identity.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(getuid()));
+ mAudioClient.identity.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(getpid()));
+ mAudioClient.identity.packageName = std::nullopt;
+ mAudioClient.identity.attributionTag = std::nullopt;
AAudioClientTracker::getInstance().setAAudioService(this);
}
-AAudioService::~AAudioService() {
-}
-
status_t AAudioService::dump(int fd, const Vector<String16>& args) {
std::string result;
@@ -72,18 +82,21 @@
return NO_ERROR;
}
-void AAudioService::registerClient(const sp<IAAudioClient>& client) {
+Status AAudioService::registerClient(const sp<IAAudioClient> &client) {
pid_t pid = IPCThreadState::self()->getCallingPid();
AAudioClientTracker::getInstance().registerClient(pid, client);
+ return Status::ok();
}
-bool AAudioService::isCallerInService() {
- return mAudioClient.clientPid == IPCThreadState::self()->getCallingPid() &&
- mAudioClient.clientUid == IPCThreadState::self()->getCallingUid();
-}
+Status
+AAudioService::openStream(const StreamRequest &_request, StreamParameters* _paramsOut,
+ int32_t *_aidl_return) {
+ static_assert(std::is_same_v<aaudio_result_t, std::decay_t<typeof(*_aidl_return)>>);
-aaudio_handle_t AAudioService::openStream(const aaudio::AAudioStreamRequest &request,
- aaudio::AAudioStreamConfiguration &configurationOutput) {
+ // Create wrapper objects for simple usage of the parcelables.
+ const AAudioStreamRequest request(_request);
+ AAudioStreamConfiguration paramsOut;
+
// A lock in is used to order the opening of endpoints when an
// EXCLUSIVE endpoint is stolen. We want the order to be:
// 1) Thread A opens exclusive MMAP endpoint
@@ -102,23 +115,28 @@
aaudio_sharing_mode_t sharingMode = configurationInput.getSharingMode();
// Enforce limit on client processes.
- pid_t pid = request.getProcessId();
- if (pid != mAudioClient.clientPid) {
+ Identity callingIdentity = request.getIdentity();
+ pid_t pid = IPCThreadState::self()->getCallingPid();
+ callingIdentity.pid = VALUE_OR_RETURN_ILLEGAL_ARG_STATUS(
+ legacy2aidl_pid_t_int32_t(pid));
+ callingIdentity.uid = VALUE_OR_RETURN_ILLEGAL_ARG_STATUS(
+ legacy2aidl_uid_t_int32_t(IPCThreadState::self()->getCallingUid()));
+ if (callingIdentity.pid != mAudioClient.identity.pid) {
int32_t count = AAudioClientTracker::getInstance().getStreamCount(pid);
if (count >= MAX_STREAMS_PER_PROCESS) {
ALOGE("openStream(): exceeded max streams per process %d >= %d",
count, MAX_STREAMS_PER_PROCESS);
- return AAUDIO_ERROR_UNAVAILABLE;
+ AIDL_RETURN(AAUDIO_ERROR_UNAVAILABLE);
}
}
if (sharingMode != AAUDIO_SHARING_MODE_EXCLUSIVE && sharingMode != AAUDIO_SHARING_MODE_SHARED) {
ALOGE("openStream(): unrecognized sharing mode = %d", sharingMode);
- return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
+ AIDL_RETURN(AAUDIO_ERROR_ILLEGAL_ARGUMENT);
}
if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE
- && AAudioClientTracker::getInstance().isExclusiveEnabled(request.getProcessId())) {
+ && AAudioClientTracker::getInstance().isExclusiveEnabled(pid)) {
// only trust audioserver for in service indication
bool inService = false;
if (isCallerInService()) {
@@ -147,29 +165,125 @@
if (result != AAUDIO_OK) {
serviceStream.clear();
- return result;
+ AIDL_RETURN(result);
} else {
aaudio_handle_t handle = mStreamTracker.addStreamForHandle(serviceStream.get());
serviceStream->setHandle(handle);
- pid_t pid = request.getProcessId();
AAudioClientTracker::getInstance().registerClientStream(pid, serviceStream);
- configurationOutput.copyFrom(*serviceStream);
+ paramsOut.copyFrom(*serviceStream);
+ *_paramsOut = std::move(paramsOut).parcelable();
// Log open in MediaMetrics after we have the handle because we need the handle to
// create the metrics ID.
serviceStream->logOpen(handle);
ALOGV("%s(): return handle = 0x%08X", __func__, handle);
- return handle;
+ AIDL_RETURN(handle);
}
}
-aaudio_result_t AAudioService::closeStream(aaudio_handle_t streamHandle) {
+Status AAudioService::closeStream(int32_t streamHandle, int32_t *_aidl_return) {
+ static_assert(std::is_same_v<aaudio_result_t, std::decay_t<typeof(*_aidl_return)>>);
+
// Check permission and ownership first.
sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
if (serviceStream.get() == nullptr) {
ALOGE("closeStream(0x%0x), illegal stream handle", streamHandle);
- return AAUDIO_ERROR_INVALID_HANDLE;
+ AIDL_RETURN(AAUDIO_ERROR_INVALID_HANDLE);
}
- return closeStream(serviceStream);
+ AIDL_RETURN(closeStream(serviceStream));
+}
+
+Status AAudioService::getStreamDescription(int32_t streamHandle, Endpoint* endpoint,
+ int32_t *_aidl_return) {
+ static_assert(std::is_same_v<aaudio_result_t, std::decay_t<typeof(*_aidl_return)>>);
+
+ sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
+ if (serviceStream.get() == nullptr) {
+ ALOGE("getStreamDescription(), illegal stream handle = 0x%0x", streamHandle);
+ AIDL_RETURN(AAUDIO_ERROR_INVALID_HANDLE);
+ }
+ AudioEndpointParcelable endpointParcelable;
+ aaudio_result_t result = serviceStream->getDescription(endpointParcelable);
+ if (result == AAUDIO_OK) {
+ *endpoint = std::move(endpointParcelable).parcelable();
+ }
+ AIDL_RETURN(result);
+}
+
+Status AAudioService::startStream(int32_t streamHandle, int32_t *_aidl_return) {
+ static_assert(std::is_same_v<aaudio_result_t, std::decay_t<typeof(*_aidl_return)>>);
+
+ sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
+ if (serviceStream.get() == nullptr) {
+ ALOGW("%s(), invalid streamHandle = 0x%0x", __func__, streamHandle);
+ AIDL_RETURN(AAUDIO_ERROR_INVALID_HANDLE);
+ }
+ AIDL_RETURN(serviceStream->start());
+}
+
+Status AAudioService::pauseStream(int32_t streamHandle, int32_t *_aidl_return) {
+ static_assert(std::is_same_v<aaudio_result_t, std::decay_t<typeof(*_aidl_return)>>);
+
+ sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
+ if (serviceStream.get() == nullptr) {
+ ALOGW("%s(), invalid streamHandle = 0x%0x", __func__, streamHandle);
+ AIDL_RETURN(AAUDIO_ERROR_INVALID_HANDLE);
+ }
+ AIDL_RETURN(serviceStream->pause());
+}
+
+Status AAudioService::stopStream(int32_t streamHandle, int32_t *_aidl_return) {
+ static_assert(std::is_same_v<aaudio_result_t, std::decay_t<typeof(*_aidl_return)>>);
+
+ sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
+ if (serviceStream.get() == nullptr) {
+ ALOGW("%s(), invalid streamHandle = 0x%0x", __func__, streamHandle);
+ AIDL_RETURN(AAUDIO_ERROR_INVALID_HANDLE);
+ }
+ AIDL_RETURN(serviceStream->stop());
+}
+
+Status AAudioService::flushStream(int32_t streamHandle, int32_t *_aidl_return) {
+ static_assert(std::is_same_v<aaudio_result_t, std::decay_t<typeof(*_aidl_return)>>);
+
+ sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
+ if (serviceStream.get() == nullptr) {
+ ALOGW("%s(), invalid streamHandle = 0x%0x", __func__, streamHandle);
+ AIDL_RETURN(AAUDIO_ERROR_INVALID_HANDLE);
+ }
+ AIDL_RETURN(serviceStream->flush());
+}
+
+Status AAudioService::registerAudioThread(int32_t streamHandle, int32_t clientThreadId, int64_t periodNanoseconds,
+ int32_t *_aidl_return) {
+ static_assert(std::is_same_v<aaudio_result_t, std::decay_t<typeof(*_aidl_return)>>);
+
+ sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
+ if (serviceStream.get() == nullptr) {
+ ALOGW("%s(), invalid streamHandle = 0x%0x", __func__, streamHandle);
+ AIDL_RETURN(AAUDIO_ERROR_INVALID_HANDLE);
+ }
+ int32_t priority = isCallerInService()
+ ? kRealTimeAudioPriorityService : kRealTimeAudioPriorityClient;
+ AIDL_RETURN(serviceStream->registerAudioThread(clientThreadId, priority));
+}
+
+Status AAudioService::unregisterAudioThread(int32_t streamHandle, int32_t clientThreadId,
+ int32_t *_aidl_return) {
+ static_assert(std::is_same_v<aaudio_result_t, std::decay_t<typeof(*_aidl_return)>>);
+
+ sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
+ if (serviceStream.get() == nullptr) {
+ ALOGW("%s(), invalid streamHandle = 0x%0x", __func__, streamHandle);
+ AIDL_RETURN(AAUDIO_ERROR_INVALID_HANDLE);
+ }
+ AIDL_RETURN(serviceStream->unregisterAudioThread(clientThreadId));
+}
+
+bool AAudioService::isCallerInService() {
+ pid_t clientPid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mAudioClient.identity.pid));
+ uid_t clientUid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(mAudioClient.identity.uid));
+ return clientPid == IPCThreadState::self()->getCallingPid() &&
+ clientUid == IPCThreadState::self()->getCallingUid();
}
aaudio_result_t AAudioService::closeStream(sp<AAudioServiceStreamBase> serviceStream) {
@@ -192,9 +306,11 @@
// Only allow owner or the aaudio service to access the stream.
const uid_t callingUserId = IPCThreadState::self()->getCallingUid();
const uid_t ownerUserId = serviceStream->getOwnerUserId();
+ const uid_t clientUid = VALUE_OR_FATAL(
+ aidl2legacy_int32_t_uid_t(mAudioClient.identity.uid));
bool callerOwnsIt = callingUserId == ownerUserId;
- bool serverCalling = callingUserId == mAudioClient.clientUid;
- bool serverOwnsIt = ownerUserId == mAudioClient.clientUid;
+ bool serverCalling = callingUserId == clientUid;
+ bool serverOwnsIt = ownerUserId == clientUid;
bool allowed = callerOwnsIt || serverCalling || serverOwnsIt;
if (!allowed) {
ALOGE("AAudioService: calling uid %d cannot access stream 0x%08X owned by %d",
@@ -205,76 +321,6 @@
return serviceStream;
}
-aaudio_result_t AAudioService::getStreamDescription(
- aaudio_handle_t streamHandle,
- aaudio::AudioEndpointParcelable &parcelable) {
- sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
- if (serviceStream.get() == nullptr) {
- ALOGE("getStreamDescription(), illegal stream handle = 0x%0x", streamHandle);
- return AAUDIO_ERROR_INVALID_HANDLE;
- }
- return serviceStream->getDescription(parcelable);
-}
-
-aaudio_result_t AAudioService::startStream(aaudio_handle_t streamHandle) {
- sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
- if (serviceStream.get() == nullptr) {
- ALOGW("%s(), invalid streamHandle = 0x%0x", __func__, streamHandle);
- return AAUDIO_ERROR_INVALID_HANDLE;
- }
- return serviceStream->start();
-}
-
-aaudio_result_t AAudioService::pauseStream(aaudio_handle_t streamHandle) {
- sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
- if (serviceStream.get() == nullptr) {
- ALOGW("%s(), invalid streamHandle = 0x%0x", __func__, streamHandle);
- return AAUDIO_ERROR_INVALID_HANDLE;
- }
- return serviceStream->pause();
-}
-
-aaudio_result_t AAudioService::stopStream(aaudio_handle_t streamHandle) {
- sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
- if (serviceStream.get() == nullptr) {
- ALOGW("%s(), invalid streamHandle = 0x%0x", __func__, streamHandle);
- return AAUDIO_ERROR_INVALID_HANDLE;
- }
- return serviceStream->stop();
-}
-
-aaudio_result_t AAudioService::flushStream(aaudio_handle_t streamHandle) {
- sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
- if (serviceStream.get() == nullptr) {
- ALOGW("%s(), invalid streamHandle = 0x%0x", __func__, streamHandle);
- return AAUDIO_ERROR_INVALID_HANDLE;
- }
- return serviceStream->flush();
-}
-
-aaudio_result_t AAudioService::registerAudioThread(aaudio_handle_t streamHandle,
- pid_t clientThreadId,
- int64_t /* periodNanoseconds */) {
- sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
- if (serviceStream.get() == nullptr) {
- ALOGW("%s(), invalid streamHandle = 0x%0x", __func__, streamHandle);
- return AAUDIO_ERROR_INVALID_HANDLE;
- }
- int32_t priority = isCallerInService()
- ? kRealTimeAudioPriorityService : kRealTimeAudioPriorityClient;
- return serviceStream->registerAudioThread(clientThreadId, priority);
-}
-
-aaudio_result_t AAudioService::unregisterAudioThread(aaudio_handle_t streamHandle,
- pid_t clientThreadId) {
- sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
- if (serviceStream.get() == nullptr) {
- ALOGW("%s(), invalid streamHandle = 0x%0x", __func__, streamHandle);
- return AAUDIO_ERROR_INVALID_HANDLE;
- }
- return serviceStream->unregisterAudioThread(clientThreadId);
-}
-
aaudio_result_t AAudioService::startClient(aaudio_handle_t streamHandle,
const android::AudioClient& client,
const audio_attributes_t *attr,
diff --git a/services/oboeservice/AAudioService.h b/services/oboeservice/AAudioService.h
index caf48a5..7c1b796 100644
--- a/services/oboeservice/AAudioService.h
+++ b/services/oboeservice/AAudioService.h
@@ -24,69 +24,71 @@
#include <media/AudioClient.h>
#include <aaudio/AAudio.h>
+#include <aaudio/BnAAudioService.h>
#include "binding/AAudioCommon.h"
+#include "binding/AAudioBinderAdapter.h"
#include "binding/AAudioServiceInterface.h"
-#include "binding/IAAudioService.h"
#include "AAudioServiceStreamBase.h"
#include "AAudioStreamTracker.h"
namespace android {
+#define AAUDIO_SERVICE_NAME "media.aaudio"
+
class AAudioService :
public BinderService<AAudioService>,
- public BnAAudioService,
- public aaudio::AAudioServiceInterface
+ public aaudio::BnAAudioService
{
friend class BinderService<AAudioService>;
public:
AAudioService();
- virtual ~AAudioService();
+ virtual ~AAudioService() = default;
+
+ aaudio::AAudioServiceInterface& asAAudioServiceInterface() {
+ return mAdapter;
+ }
static const char* getServiceName() { return AAUDIO_SERVICE_NAME; }
virtual status_t dump(int fd, const Vector<String16>& args) override;
- virtual void registerClient(const sp<IAAudioClient>& client);
+ binder::Status registerClient(const ::android::sp<::aaudio::IAAudioClient>& client) override;
- aaudio::aaudio_handle_t openStream(const aaudio::AAudioStreamRequest &request,
- aaudio::AAudioStreamConfiguration &configurationOutput)
- override;
+ binder::Status openStream(const ::aaudio::StreamRequest& request,
+ ::aaudio::StreamParameters* paramsOut,
+ int32_t* _aidl_return) override;
- /*
- * This is called from Binder. It checks for permissions
- * and converts the handle passed through Binder to a stream pointer.
- */
- aaudio_result_t closeStream(aaudio::aaudio_handle_t streamHandle) override;
+ binder::Status closeStream(int32_t streamHandle, int32_t* _aidl_return) override;
- aaudio_result_t getStreamDescription(
- aaudio::aaudio_handle_t streamHandle,
- aaudio::AudioEndpointParcelable &parcelable) override;
+ binder::Status
+ getStreamDescription(int32_t streamHandle, ::aaudio::Endpoint* endpoint,
+ int32_t* _aidl_return) override;
- aaudio_result_t startStream(aaudio::aaudio_handle_t streamHandle) override;
+ binder::Status startStream(int32_t streamHandle, int32_t* _aidl_return) override;
- aaudio_result_t pauseStream(aaudio::aaudio_handle_t streamHandle) override;
+ binder::Status pauseStream(int32_t streamHandle, int32_t* _aidl_return) override;
- aaudio_result_t stopStream(aaudio::aaudio_handle_t streamHandle) override;
+ binder::Status stopStream(int32_t streamHandle, int32_t* _aidl_return) override;
- aaudio_result_t flushStream(aaudio::aaudio_handle_t streamHandle) override;
+ binder::Status flushStream(int32_t streamHandle, int32_t* _aidl_return) override;
- aaudio_result_t registerAudioThread(aaudio::aaudio_handle_t streamHandle,
- pid_t tid,
- int64_t periodNanoseconds) override;
+ binder::Status
+ registerAudioThread(int32_t streamHandle, int32_t clientThreadId, int64_t periodNanoseconds,
+ int32_t* _aidl_return) override;
- aaudio_result_t unregisterAudioThread(aaudio::aaudio_handle_t streamHandle,
- pid_t tid) override;
+ binder::Status unregisterAudioThread(int32_t streamHandle, int32_t clientThreadId,
+ int32_t* _aidl_return) override;
aaudio_result_t startClient(aaudio::aaudio_handle_t streamHandle,
const android::AudioClient& client,
const audio_attributes_t *attr,
- audio_port_handle_t *clientHandle) override;
+ audio_port_handle_t *clientHandle);
aaudio_result_t stopClient(aaudio::aaudio_handle_t streamHandle,
- audio_port_handle_t clientHandle) override;
+ audio_port_handle_t clientHandle);
// ===============================================================================
// The following public methods are only called from the service and NOT by Binder.
@@ -101,6 +103,29 @@
aaudio_result_t closeStream(sp<aaudio::AAudioServiceStreamBase> serviceStream);
private:
+ class Adapter : public aaudio::AAudioBinderAdapter {
+ public:
+ explicit Adapter(AAudioService *service)
+ : aaudio::AAudioBinderAdapter(service),
+ mService(service) {}
+
+ aaudio_result_t startClient(aaudio::aaudio_handle_t streamHandle,
+ const android::AudioClient &client,
+ const audio_attributes_t *attr,
+ audio_port_handle_t *clientHandle) override {
+ return mService->startClient(streamHandle, client, attr, clientHandle);
+ }
+
+ aaudio_result_t stopClient(aaudio::aaudio_handle_t streamHandle,
+ audio_port_handle_t clientHandle) override {
+ return mService->stopClient(streamHandle, clientHandle);
+ }
+
+ private:
+ AAudioService* const mService;
+ };
+
+ Adapter mAdapter;
/** @return true if the client is the audioserver
*/
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index b139be1..13dd3d3 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -38,7 +38,11 @@
using namespace android; // TODO just import names needed
using namespace aaudio; // TODO just import names needed
-std::string AAudioServiceEndpoint::dump() const {
+AAudioServiceEndpoint::~AAudioServiceEndpoint() {
+ ALOGD("%s() called", __func__);
+}
+
+std::string AAudioServiceEndpoint::dump() const NO_THREAD_SAFETY_ANALYSIS {
std::stringstream result;
const bool isLocked = AAudio_tryUntilTrue(
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index 04b906a..a7f63d3 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -22,6 +22,8 @@
#include <mutex>
#include <vector>
+#include <android-base/thread_annotations.h>
+
#include "client/AudioStreamInternal.h"
#include "client/AudioStreamInternalPlay.h"
#include "core/AAudioStreamParameters.h"
@@ -41,7 +43,7 @@
, public AAudioStreamParameters {
public:
- virtual ~AAudioServiceEndpoint() = default;
+ virtual ~AAudioServiceEndpoint();
virtual std::string dump() const;
@@ -141,7 +143,8 @@
std::vector<android::sp<AAudioServiceStreamBase>> disconnectRegisteredStreams();
mutable std::mutex mLockStreams;
- std::vector<android::sp<AAudioServiceStreamBase>> mRegisteredStreams;
+ std::vector<android::sp<AAudioServiceStreamBase>> mRegisteredStreams
+ GUARDED_BY(mLockStreams);
SimpleDoubleBuffer<Timestamp> mAtomicEndpointTimestamp;
diff --git a/services/oboeservice/AAudioServiceEndpointCapture.cpp b/services/oboeservice/AAudioServiceEndpointCapture.cpp
index 3ba3c28..bc769f0 100644
--- a/services/oboeservice/AAudioServiceEndpointCapture.cpp
+++ b/services/oboeservice/AAudioServiceEndpointCapture.cpp
@@ -35,22 +35,17 @@
using namespace android; // TODO just import names needed
using namespace aaudio; // TODO just import names needed
-AAudioServiceEndpointCapture::AAudioServiceEndpointCapture(AAudioService &audioService)
- : AAudioServiceEndpointShared(
- (AudioStreamInternal *)(new AudioStreamInternalCapture(audioService, true))) {
-}
-
-AAudioServiceEndpointCapture::~AAudioServiceEndpointCapture() {
- delete[] mDistributionBuffer;
+AAudioServiceEndpointCapture::AAudioServiceEndpointCapture(AAudioService& audioService)
+ : AAudioServiceEndpointShared(
+ new AudioStreamInternalCapture(audioService.asAAudioServiceInterface(), true)) {
}
aaudio_result_t AAudioServiceEndpointCapture::open(const aaudio::AAudioStreamRequest &request) {
aaudio_result_t result = AAudioServiceEndpointShared::open(request);
if (result == AAUDIO_OK) {
- delete[] mDistributionBuffer;
int distributionBufferSizeBytes = getStreamInternal()->getFramesPerBurst()
* getStreamInternal()->getBytesPerFrame();
- mDistributionBuffer = new uint8_t[distributionBufferSizeBytes];
+ mDistributionBuffer = std::make_unique<uint8_t[]>(distributionBufferSizeBytes);
}
return result;
}
@@ -67,7 +62,8 @@
int64_t mmapFramesRead = getStreamInternal()->getFramesRead();
// Read audio data from stream using a blocking read.
- result = getStreamInternal()->read(mDistributionBuffer, getFramesPerBurst(), timeoutNanos);
+ result = getStreamInternal()->read(mDistributionBuffer.get(),
+ getFramesPerBurst(), timeoutNanos);
if (result == AAUDIO_ERROR_DISCONNECTED) {
ALOGD("%s() read() returned AAUDIO_ERROR_DISCONNECTED", __func__);
// We do not need the returned vector.
@@ -81,48 +77,14 @@
// Distribute data to each active stream.
{ // brackets are for lock_guard
-
std::lock_guard <std::mutex> lock(mLockStreams);
for (const auto& clientStream : mRegisteredStreams) {
if (clientStream->isRunning() && !clientStream->isSuspended()) {
- int64_t clientFramesWritten = 0;
-
sp<AAudioServiceStreamShared> streamShared =
static_cast<AAudioServiceStreamShared *>(clientStream.get());
-
- {
- // Lock the AudioFifo to protect against close.
- std::lock_guard <std::mutex> lock(streamShared->getAudioDataQueueLock());
-
- FifoBuffer *fifo = streamShared->getAudioDataFifoBuffer_l();
- if (fifo != nullptr) {
-
- // Determine offset between framePosition in client's stream
- // vs the underlying MMAP stream.
- clientFramesWritten = fifo->getWriteCounter();
- // There are two indices that refer to the same frame.
- int64_t positionOffset = mmapFramesRead - clientFramesWritten;
- streamShared->setTimestampPositionOffset(positionOffset);
-
- // Is the buffer too full to write a burst?
- if (fifo->getEmptyFramesAvailable() <
- getFramesPerBurst()) {
- streamShared->incrementXRunCount();
- } else {
- fifo->write(mDistributionBuffer, getFramesPerBurst());
- }
- clientFramesWritten = fifo->getWriteCounter();
- }
- }
-
- if (clientFramesWritten > 0) {
- // This timestamp represents the completion of data being written into the
- // client buffer. It is sent to the client and used in the timing model
- // to decide when data will be available to read.
- Timestamp timestamp(clientFramesWritten, AudioClock::getNanoseconds());
- streamShared->markTransferTime(timestamp);
- }
-
+ streamShared->writeDataIfRoom(mmapFramesRead,
+ mDistributionBuffer.get(),
+ getFramesPerBurst());
}
}
}
diff --git a/services/oboeservice/AAudioServiceEndpointCapture.h b/services/oboeservice/AAudioServiceEndpointCapture.h
index 2bbe81d..2ca43cf 100644
--- a/services/oboeservice/AAudioServiceEndpointCapture.h
+++ b/services/oboeservice/AAudioServiceEndpointCapture.h
@@ -17,6 +17,8 @@
#ifndef AAUDIO_SERVICE_ENDPOINT_CAPTURE_H
#define AAUDIO_SERVICE_ENDPOINT_CAPTURE_H
+#include <memory>
+
#include "client/AudioStreamInternal.h"
#include "client/AudioStreamInternalCapture.h"
@@ -28,15 +30,14 @@
class AAudioServiceEndpointCapture : public AAudioServiceEndpointShared {
public:
explicit AAudioServiceEndpointCapture(android::AAudioService &audioService);
- virtual ~AAudioServiceEndpointCapture();
+ virtual ~AAudioServiceEndpointCapture() = default;
aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
-
void *callbackLoop() override;
private:
- uint8_t *mDistributionBuffer = nullptr;
+ std::unique_ptr<uint8_t[]> mDistributionBuffer;
};
} /* namespace aaudio */
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index 04c6453..b4efd1a 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -51,8 +51,6 @@
: mMmapStream(nullptr)
, mAAudioService(audioService) {}
-AAudioServiceEndpointMMAP::~AAudioServiceEndpointMMAP() {}
-
std::string AAudioServiceEndpointMMAP::dump() const {
std::stringstream result;
@@ -72,24 +70,54 @@
aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamRequest &request) {
aaudio_result_t result = AAUDIO_OK;
+ copyFrom(request.getConstantConfiguration());
+ mMmapClient.identity = request.getIdentity();
+ // TODO b/182392769: use identity util
+ mMmapClient.identity.uid = VALUE_OR_FATAL(
+ legacy2aidl_uid_t_int32_t(IPCThreadState::self()->getCallingUid()));
+ mMmapClient.identity.pid = VALUE_OR_FATAL(
+ legacy2aidl_pid_t_int32_t(IPCThreadState::self()->getCallingPid()));
+
+ audio_format_t audioFormat = getFormat();
+
+ // FLOAT is not directly supported by the HAL so ask for a 32-bit.
+ if (audioFormat == AUDIO_FORMAT_PCM_FLOAT) {
+ // TODO remove these logs when finished debugging.
+ ALOGD("%s() change format from %d to 32_BIT", __func__, audioFormat);
+ audioFormat = AUDIO_FORMAT_PCM_32_BIT;
+ }
+
+ result = openWithFormat(audioFormat);
+ if (result == AAUDIO_OK) return result;
+
+ if (result == AAUDIO_ERROR_UNAVAILABLE && audioFormat == AUDIO_FORMAT_PCM_32_BIT) {
+ ALOGD("%s() 32_BIT failed, perhaps due to format. Try again with 24_BIT_PACKED", __func__);
+ audioFormat = AUDIO_FORMAT_PCM_24_BIT_PACKED;
+ result = openWithFormat(audioFormat);
+ }
+ if (result == AAUDIO_OK) return result;
+
+ // TODO The HAL and AudioFlinger should be recommending a format if the open fails.
+ // But that recommendation is not propagating back from the HAL.
+ // So for now just try something very likely to work.
+ if (result == AAUDIO_ERROR_UNAVAILABLE && audioFormat == AUDIO_FORMAT_PCM_24_BIT_PACKED) {
+ ALOGD("%s() 24_BIT failed, perhaps due to format. Try again with 16_BIT", __func__);
+ audioFormat = AUDIO_FORMAT_PCM_16_BIT;
+ result = openWithFormat(audioFormat);
+ }
+ return result;
+}
+
+aaudio_result_t AAudioServiceEndpointMMAP::openWithFormat(audio_format_t audioFormat) {
+ aaudio_result_t result = AAUDIO_OK;
audio_config_base_t config;
audio_port_handle_t deviceId;
- copyFrom(request.getConstantConfiguration());
-
const audio_attributes_t attributes = getAudioAttributesFrom(this);
- mMmapClient.clientUid = request.getUserId();
- mMmapClient.clientPid = request.getProcessId();
- mMmapClient.packageName.setTo(String16(""));
-
mRequestedDeviceId = deviceId = getDeviceId();
// Fill in config
- audio_format_t audioFormat = getFormat();
- if (audioFormat == AUDIO_FORMAT_DEFAULT || audioFormat == AUDIO_FORMAT_PCM_FLOAT) {
- audioFormat = AUDIO_FORMAT_PCM_16_BIT;
- }
config.format = audioFormat;
int32_t aaudioSampleRate = getSampleRate();
@@ -137,8 +165,8 @@
this, // callback
mMmapStream,
&mPortHandle);
- ALOGD("%s() mMapClient.uid = %d, pid = %d => portHandle = %d\n",
- __func__, mMmapClient.clientUid, mMmapClient.clientPid, mPortHandle);
+ ALOGD("%s() mMapClient.identity = %s => portHandle = %d\n",
+ __func__, mMmapClient.identity.toString().c_str(), mPortHandle);
if (status != OK) {
// This can happen if the resource is busy or the config does
// not match the hardware.
@@ -186,8 +214,9 @@
setBufferCapacity(mMmapBufferinfo.buffer_size_frames);
if (!isBufferShareable) {
// Exclusive mode can only be used by the service because the FD cannot be shared.
- uid_t audioServiceUid = getuid();
- if ((mMmapClient.clientUid != audioServiceUid) &&
+ int32_t audioServiceUid =
+ VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(getuid()));
+ if ((mMmapClient.identity.uid != audioServiceUid) &&
getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
ALOGW("%s() - exclusive FD cannot be used by client", __func__);
result = AAUDIO_ERROR_UNAVAILABLE;
@@ -331,7 +360,10 @@
// This is called by AudioFlinger when it wants to destroy a stream.
void AAudioServiceEndpointMMAP::onTearDown(audio_port_handle_t portHandle) {
ALOGD("%s(portHandle = %d) called", __func__, portHandle);
- std::thread asyncTask(&AAudioServiceEndpointMMAP::handleTearDownAsync, this, portHandle);
+ android::sp<AAudioServiceEndpointMMAP> holdEndpoint(this);
+ std::thread asyncTask([holdEndpoint, portHandle]() {
+ holdEndpoint->handleTearDownAsync(portHandle);
+ });
asyncTask.detach();
}
@@ -352,9 +384,11 @@
ALOGD("%s() called with dev %d, old = %d", __func__, deviceId, getDeviceId());
if (getDeviceId() != deviceId) {
if (getDeviceId() != AUDIO_PORT_HANDLE_NONE) {
- std::thread asyncTask([this, deviceId]() {
- disconnectRegisteredStreams();
- setDeviceId(deviceId);
+ android::sp<AAudioServiceEndpointMMAP> holdEndpoint(this);
+ std::thread asyncTask([holdEndpoint, deviceId]() {
+ ALOGD("onRoutingChanged() asyncTask launched");
+ holdEndpoint->disconnectRegisteredStreams();
+ holdEndpoint->setDeviceId(deviceId);
});
asyncTask.detach();
} else {
@@ -378,3 +412,18 @@
parcelable.mDownDataQueueParcelable.setCapacityInFrames(getBufferCapacity());
return AAUDIO_OK;
}
+
+aaudio_result_t AAudioServiceEndpointMMAP::getExternalPosition(uint64_t *positionFrames,
+ int64_t *timeNanos)
+{
+ if (!mExternalPositionSupported) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+ status_t status = mMmapStream->getExternalPosition(positionFrames, timeNanos);
+ if (status == INVALID_OPERATION) {
+ // getExternalPosition is not supported. Set mExternalPositionSupported as false
+ // so that the call will not go to the HAL next time.
+ mExternalPositionSupported = false;
+ }
+ return AAudioConvert_androidToAAudioResult(status);
+}
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.h b/services/oboeservice/AAudioServiceEndpointMMAP.h
index b6003b6..5a53885 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.h
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.h
@@ -44,7 +44,7 @@
public:
explicit AAudioServiceEndpointMMAP(android::AAudioService &audioService);
- virtual ~AAudioServiceEndpointMMAP();
+ virtual ~AAudioServiceEndpointMMAP() = default;
std::string dump() const override;
@@ -85,7 +85,12 @@
return mHardwareTimeOffsetNanos;
}
+ aaudio_result_t getExternalPosition(uint64_t *positionFrames, int64_t *timeNanos);
+
private:
+
+ aaudio_result_t openWithFormat(audio_format_t audioFormat);
+
MonotonicCounter mFramesTransferred;
// Interface to the AudioFlinger MMAP support.
@@ -101,6 +106,8 @@
int64_t mHardwareTimeOffsetNanos = 0; // TODO get from HAL
+ bool mExternalPositionSupported = true;
+
};
} /* namespace aaudio */
diff --git a/services/oboeservice/AAudioServiceEndpointPlay.cpp b/services/oboeservice/AAudioServiceEndpointPlay.cpp
index 89aa70d..4e46033 100644
--- a/services/oboeservice/AAudioServiceEndpointPlay.cpp
+++ b/services/oboeservice/AAudioServiceEndpointPlay.cpp
@@ -41,10 +41,9 @@
#define BURSTS_PER_BUFFER_DEFAULT 2
-AAudioServiceEndpointPlay::AAudioServiceEndpointPlay(AAudioService &audioService)
- : AAudioServiceEndpointShared(
- (AudioStreamInternal *)(new AudioStreamInternalPlay(audioService, true))) {
-}
+AAudioServiceEndpointPlay::AAudioServiceEndpointPlay(AAudioService& audioService)
+ : AAudioServiceEndpointShared(
+ new AudioStreamInternalPlay(audioService.asAAudioServiceInterface(), true)) {}
aaudio_result_t AAudioServiceEndpointPlay::open(const aaudio::AAudioStreamRequest &request) {
aaudio_result_t result = AAudioServiceEndpointShared::open(request);
@@ -99,10 +98,11 @@
{
// Lock the AudioFifo to protect against close.
- std::lock_guard <std::mutex> lock(streamShared->getAudioDataQueueLock());
-
- FifoBuffer *fifo = streamShared->getAudioDataFifoBuffer_l();
- if (fifo != nullptr) {
+ std::lock_guard <std::mutex> lock(streamShared->audioDataQueueLock);
+ std::shared_ptr<SharedRingBuffer> audioDataQueue
+ = streamShared->getAudioDataQueue_l();
+ std::shared_ptr<FifoBuffer> fifo;
+ if (audioDataQueue && (fifo = audioDataQueue->getFifoBuffer())) {
// Determine offset between framePosition in client's stream
// vs the underlying MMAP stream.
diff --git a/services/oboeservice/AAudioServiceEndpointShared.cpp b/services/oboeservice/AAudioServiceEndpointShared.cpp
index f5de59f..0d453cf 100644
--- a/services/oboeservice/AAudioServiceEndpointShared.cpp
+++ b/services/oboeservice/AAudioServiceEndpointShared.cpp
@@ -88,23 +88,30 @@
}
void AAudioServiceEndpointShared::close() {
- getStreamInternal()->releaseCloseFinal();
+ stopSharingThread();
+ getStreamInternal()->safeReleaseClose();
}
// Glue between C and C++ callbacks.
static void *aaudio_endpoint_thread_proc(void *arg) {
assert(arg != nullptr);
+ ALOGD("%s() called", __func__);
- // The caller passed in a smart pointer to prevent the endpoint from getting deleted
- // while the thread was launching.
- sp<AAudioServiceEndpointShared> *endpointForThread =
- static_cast<sp<AAudioServiceEndpointShared> *>(arg);
- sp<AAudioServiceEndpointShared> endpoint = *endpointForThread;
- delete endpointForThread; // Just use scoped smart pointer. Don't need this anymore.
+ // Prevent the stream from being deleted while being used.
+ // This is just for extra safety. It is probably not needed because
+ // this callback should be joined before the stream is closed.
+ AAudioServiceEndpointShared *endpointPtr =
+ static_cast<AAudioServiceEndpointShared *>(arg);
+ android::sp<AAudioServiceEndpointShared> endpoint(endpointPtr);
+ // Balance the incStrong() in startSharingThread_l().
+ endpoint->decStrong(nullptr);
+
void *result = endpoint->callbackLoop();
// Close now so that the HW resource is freed and we can open a new device.
if (!endpoint->isConnected()) {
- endpoint->close();
+ ALOGD("%s() call safeReleaseCloseFromCallback()", __func__);
+ // Release and close under a lock with no check for callback collisions.
+ endpoint->getStreamInternal()->safeReleaseCloseInternal();
}
return result;
@@ -116,38 +123,39 @@
* AAUDIO_NANOS_PER_SECOND
/ getSampleRate();
mCallbackEnabled.store(true);
- // Pass a smart pointer so the thread can hold a reference.
- sp<AAudioServiceEndpointShared> *endpointForThread = new sp<AAudioServiceEndpointShared>(this);
- aaudio_result_t result = getStreamInternal()->createThread(periodNanos,
- aaudio_endpoint_thread_proc,
- endpointForThread);
+ // Prevent this object from getting deleted before the thread has a chance to create
+ // its strong pointer. Assume the thread will call decStrong().
+ this->incStrong(nullptr);
+ aaudio_result_t result = getStreamInternal()->createThread_l(periodNanos,
+ aaudio_endpoint_thread_proc,
+ this);
if (result != AAUDIO_OK) {
- // The thread can't delete it so we have to do it here.
- delete endpointForThread;
+ this->decStrong(nullptr); // Because the thread won't do it.
}
return result;
}
aaudio_result_t aaudio::AAudioServiceEndpointShared::stopSharingThread() {
mCallbackEnabled.store(false);
- aaudio_result_t result = getStreamInternal()->joinThread(NULL);
- return result;
+ return getStreamInternal()->joinThread(NULL);
}
-aaudio_result_t AAudioServiceEndpointShared::startStream(sp<AAudioServiceStreamBase> sharedStream,
- audio_port_handle_t *clientHandle) {
+aaudio_result_t AAudioServiceEndpointShared::startStream(
+ sp<AAudioServiceStreamBase> sharedStream,
+ audio_port_handle_t *clientHandle)
+ NO_THREAD_SAFETY_ANALYSIS {
aaudio_result_t result = AAUDIO_OK;
{
std::lock_guard<std::mutex> lock(mLockStreams);
if (++mRunningStreamCount == 1) { // atomic
- result = getStreamInternal()->requestStart();
+ result = getStreamInternal()->systemStart();
if (result != AAUDIO_OK) {
--mRunningStreamCount;
} else {
result = startSharingThread_l();
if (result != AAUDIO_OK) {
- getStreamInternal()->requestStop();
+ getStreamInternal()->systemStopFromApp();
--mRunningStreamCount;
}
}
@@ -161,7 +169,7 @@
if (result != AAUDIO_OK) {
if (--mRunningStreamCount == 0) { // atomic
stopSharingThread();
- getStreamInternal()->requestStop();
+ getStreamInternal()->systemStopFromApp();
}
}
}
@@ -176,7 +184,7 @@
if (--mRunningStreamCount == 0) { // atomic
stopSharingThread(); // the sharing thread locks mLockStreams
- getStreamInternal()->requestStop();
+ getStreamInternal()->systemStopFromApp();
}
return AAUDIO_OK;
}
diff --git a/services/oboeservice/AAudioServiceEndpointShared.h b/services/oboeservice/AAudioServiceEndpointShared.h
index 020b926..8357567 100644
--- a/services/oboeservice/AAudioServiceEndpointShared.h
+++ b/services/oboeservice/AAudioServiceEndpointShared.h
@@ -20,6 +20,8 @@
#include <atomic>
#include <mutex>
+#include <android-base/thread_annotations.h>
+
#include "AAudioServiceEndpoint.h"
#include "client/AudioStreamInternal.h"
#include "client/AudioStreamInternalPlay.h"
@@ -37,6 +39,8 @@
public:
explicit AAudioServiceEndpointShared(AudioStreamInternal *streamInternal);
+ virtual ~AAudioServiceEndpointShared() = default;
+
std::string dump() const override;
aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
@@ -55,13 +59,13 @@
virtual void *callbackLoop() = 0;
-protected:
-
AudioStreamInternal *getStreamInternal() const {
return mStreamInternal.get();
};
- aaudio_result_t startSharingThread_l();
+protected:
+
+ aaudio_result_t startSharingThread_l() REQUIRES(mLockStreams);
aaudio_result_t stopSharingThread();
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 663dae2..dbacd75 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -26,7 +26,6 @@
#include <media/TypeConverter.h>
#include <mediautils/SchedulingPolicyService.h>
-#include "binding/IAAudioService.h"
#include "binding/AAudioServiceMessage.h"
#include "core/AudioGlobal.h"
#include "utility/AudioClock.h"
@@ -40,22 +39,23 @@
using namespace android; // TODO just import names needed
using namespace aaudio; // TODO just import names needed
+using media::permission::Identity;
+
/**
* Base class for streams in the service.
* @return
*/
AAudioServiceStreamBase::AAudioServiceStreamBase(AAudioService &audioService)
- : mUpMessageQueue(nullptr)
- , mTimestampThread("AATime")
+ : mTimestampThread("AATime")
, mAtomicStreamTimestamp()
, mAudioService(audioService) {
- mMmapClient.clientUid = -1;
- mMmapClient.clientPid = -1;
- mMmapClient.packageName = String16("");
+ mMmapClient.identity = Identity();
}
AAudioServiceStreamBase::~AAudioServiceStreamBase() {
+ ALOGD("%s() called", __func__);
+
// May not be set if open failed.
if (mMetricsId.size() > 0) {
mediametrics::LogItem(mMetricsId)
@@ -67,8 +67,7 @@
// If the stream is deleted when OPEN or in use then audio resources will leak.
// This would indicate an internal error. So we want to find this ASAP.
LOG_ALWAYS_FATAL_IF(!(getState() == AAUDIO_STREAM_STATE_CLOSED
- || getState() == AAUDIO_STREAM_STATE_UNINITIALIZED
- || getState() == AAUDIO_STREAM_STATE_DISCONNECTED),
+ || getState() == AAUDIO_STREAM_STATE_UNINITIALIZED),
"service stream %p still open, state = %d",
this, getState());
}
@@ -82,7 +81,7 @@
result << " 0x" << std::setfill('0') << std::setw(8) << std::hex << mHandle
<< std::dec << std::setfill(' ') ;
- result << std::setw(6) << mMmapClient.clientUid;
+ result << std::setw(6) << mMmapClient.identity.uid;
result << std::setw(7) << mClientHandle;
result << std::setw(4) << (isRunning() ? "yes" : " no");
result << std::setw(6) << getState();
@@ -128,9 +127,12 @@
AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
aaudio_result_t result = AAUDIO_OK;
- mMmapClient.clientUid = request.getUserId();
- mMmapClient.clientPid = request.getProcessId();
- mMmapClient.packageName.setTo(String16("")); // TODO What should we do here?
+ mMmapClient.identity = request.getIdentity();
+ // TODO b/182392769: use identity util
+ mMmapClient.identity.uid = VALUE_OR_FATAL(
+ legacy2aidl_uid_t_int32_t(IPCThreadState::self()->getCallingUid()));
+ mMmapClient.identity.pid = VALUE_OR_FATAL(
+ legacy2aidl_pid_t_int32_t(IPCThreadState::self()->getCallingPid()));
// Limit scope of lock to avoid recursive lock in close().
{
@@ -140,7 +142,7 @@
return AAUDIO_ERROR_INVALID_STATE;
}
- mUpMessageQueue = new SharedRingBuffer();
+ mUpMessageQueue = std::make_shared<SharedRingBuffer>();
result = mUpMessageQueue->allocate(sizeof(AAudioServiceMessage),
QUEUE_UP_CAPACITY_COMMANDS);
if (result != AAUDIO_OK) {
@@ -179,6 +181,8 @@
return AAUDIO_OK;
}
+ // This will call stopTimestampThread() and also stop the stream,
+ // just in case it was not already stopped.
stop_l();
aaudio_result_t result = AAUDIO_OK;
@@ -194,13 +198,6 @@
mServiceEndpoint.clear(); // endpoint will hold the pointer after this method returns.
}
- {
- std::lock_guard<std::mutex> lock(mUpMessageQueueLock);
- stopTimestampThread();
- delete mUpMessageQueue;
- mUpMessageQueue = nullptr;
- }
-
setState(AAUDIO_STREAM_STATE_CLOSED);
mediametrics::LogItem(mMetricsId)
@@ -231,7 +228,7 @@
aaudio_result_t result = AAUDIO_OK;
if (auto state = getState();
- state == AAUDIO_STREAM_STATE_CLOSED || state == AAUDIO_STREAM_STATE_DISCONNECTED) {
+ state == AAUDIO_STREAM_STATE_CLOSED || isDisconnected_l()) {
ALOGW("%s() already CLOSED, returns INVALID_STATE, handle = %d",
__func__, getHandle());
return AAUDIO_ERROR_INVALID_STATE;
@@ -263,8 +260,14 @@
sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
setState(AAUDIO_STREAM_STATE_STARTED);
mThreadEnabled.store(true);
+ // Make sure this object does not get deleted before the run() method
+ // can protect it by making a strong pointer.
+ incStrong(nullptr); // See run() method.
result = mTimestampThread.start(this);
- if (result != AAUDIO_OK) goto error;
+ if (result != AAUDIO_OK) {
+ decStrong(nullptr); // run() can't do it so we have to do it here.
+ goto error;
+ }
return result;
@@ -293,10 +296,6 @@
.set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result)
.record(); });
- // Send it now because the timestamp gets rounded up when stopStream() is called below.
- // Also we don't need the timestamps while we are shutting down.
- sendCurrentTimestamp();
-
result = stopTimestampThread();
if (result != AAUDIO_OK) {
disconnect_l();
@@ -342,10 +341,12 @@
setState(AAUDIO_STREAM_STATE_STOPPING);
- // Send it now because the timestamp gets rounded up when stopStream() is called below.
- // Also we don't need the timestamps while we are shutting down.
- sendCurrentTimestamp(); // warning - this calls a virtual function
+ // Temporarily unlock because we are joining the timestamp thread and it may try
+ // to acquire mLock.
+ mLock.unlock();
result = stopTimestampThread();
+ mLock.lock();
+
if (result != AAUDIO_OK) {
disconnect_l();
return result;
@@ -405,15 +406,21 @@
__attribute__((no_sanitize("integer")))
void AAudioServiceStreamBase::run() {
ALOGD("%s() %s entering >>>>>>>>>>>>>> TIMESTAMPS", __func__, getTypeText());
+ // Hold onto the ref counted stream until the end.
+ android::sp<AAudioServiceStreamBase> holdStream(this);
TimestampScheduler timestampScheduler;
+ // Balance the incStrong from when the thread was launched.
+ holdStream->decStrong(nullptr);
+
timestampScheduler.setBurstPeriod(mFramesPerBurst, getSampleRate());
timestampScheduler.start(AudioClock::getNanoseconds());
int64_t nextTime = timestampScheduler.nextAbsoluteTime();
int32_t loopCount = 0;
+ aaudio_result_t result = AAUDIO_OK;
while(mThreadEnabled.load()) {
loopCount++;
if (AudioClock::getNanoseconds() >= nextTime) {
- aaudio_result_t result = sendCurrentTimestamp();
+ result = sendCurrentTimestamp();
if (result != AAUDIO_OK) {
ALOGE("%s() timestamp thread got result = %d", __func__, result);
break;
@@ -425,6 +432,11 @@
AudioClock::sleepUntilNanoTime(nextTime);
}
}
+ // This was moved from the calls in stop_l() and pause_l(), which could cause a deadlock
+ // if it resulted in a call to disconnect.
+ if (result == AAUDIO_OK) {
+ (void) sendCurrentTimestamp();
+ }
ALOGD("%s() %s exiting after %d loops <<<<<<<<<<<<<< TIMESTAMPS",
__func__, getTypeText(), loopCount);
}
@@ -435,8 +447,7 @@
}
void AAudioServiceStreamBase::disconnect_l() {
- if (getState() != AAUDIO_STREAM_STATE_DISCONNECTED
- && getState() != AAUDIO_STREAM_STATE_CLOSED) {
+ if (!isDisconnected_l() && getState() != AAUDIO_STREAM_STATE_CLOSED) {
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DISCONNECT)
@@ -444,7 +455,7 @@
.record();
sendServiceEvent(AAUDIO_SERVICE_EVENT_DISCONNECTED);
- setState(AAUDIO_STREAM_STATE_DISCONNECTED);
+ setDisconnected_l(true);
}
}
@@ -514,12 +525,8 @@
ALOGE("%s(): mUpMessageQueue null! - stream not open", __func__);
return true;
}
- int32_t framesAvailable = mUpMessageQueue->getFifoBuffer()
- ->getFullFramesAvailable();
- int32_t capacity = mUpMessageQueue->getFifoBuffer()
- ->getBufferCapacityInFrames();
// Is it half full or more
- return framesAvailable >= (capacity / 2);
+ return mUpMessageQueue->getFractionalFullness() >= 0.5;
}
aaudio_result_t AAudioServiceStreamBase::writeUpMessageQueue(AAudioServiceMessage *command) {
@@ -604,14 +611,3 @@
void AAudioServiceStreamBase::onVolumeChanged(float volume) {
sendServiceEvent(AAUDIO_SERVICE_EVENT_VOLUME, volume);
}
-
-int32_t AAudioServiceStreamBase::incrementServiceReferenceCount_l() {
- return ++mCallingCount;
-}
-
-int32_t AAudioServiceStreamBase::decrementServiceReferenceCount_l() {
- int32_t count = --mCallingCount;
- // Each call to increment should be balanced with one call to decrement.
- assert(count >= 0);
- return count;
-}
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index 94cc980..c42df0f 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -20,13 +20,16 @@
#include <assert.h>
#include <mutex>
+#include <android-base/thread_annotations.h>
+#include <media/AidlConversion.h>
#include <media/AudioClient.h>
#include <utils/RefBase.h>
#include "fifo/FifoBuffer.h"
-#include "binding/IAAudioService.h"
#include "binding/AudioEndpointParcelable.h"
#include "binding/AAudioServiceMessage.h"
+#include "binding/AAudioStreamRequest.h"
+#include "core/AAudioStreamParameters.h"
#include "utility/AAudioUtilities.h"
#include "utility/AudioClock.h"
@@ -77,7 +80,7 @@
// because we had to wait until we generated the handle.
void logOpen(aaudio_handle_t streamHandle);
- aaudio_result_t close();
+ aaudio_result_t close() EXCLUDES(mLock);
/**
* Start the flow of audio data.
@@ -85,7 +88,7 @@
* This is not guaranteed to be synchronous but it currently is.
* An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
*/
- aaudio_result_t start();
+ aaudio_result_t start() EXCLUDES(mLock);
/**
* Stop the flow of data so that start() can resume without loss of data.
@@ -93,7 +96,7 @@
* This is not guaranteed to be synchronous but it currently is.
* An AAUDIO_SERVICE_EVENT_PAUSED will be sent to the client when complete.
*/
- aaudio_result_t pause();
+ aaudio_result_t pause() EXCLUDES(mLock);
/**
* Stop the flow of data after the currently queued data has finished playing.
@@ -102,14 +105,14 @@
* An AAUDIO_SERVICE_EVENT_STOPPED will be sent to the client when complete.
*
*/
- aaudio_result_t stop();
+ aaudio_result_t stop() EXCLUDES(mLock);
/**
* Discard any data held by the underlying HAL or Service.
*
* An AAUDIO_SERVICE_EVENT_FLUSHED will be sent to the client when complete.
*/
- aaudio_result_t flush();
+ aaudio_result_t flush() EXCLUDES(mLock);
virtual aaudio_result_t startClient(const android::AudioClient& client,
const audio_attributes_t *attr __unused,
@@ -123,9 +126,9 @@
return AAUDIO_ERROR_UNAVAILABLE;
}
- aaudio_result_t registerAudioThread(pid_t clientThreadId, int priority);
+ aaudio_result_t registerAudioThread(pid_t clientThreadId, int priority) EXCLUDES(mLock);
- aaudio_result_t unregisterAudioThread(pid_t clientThreadId);
+ aaudio_result_t unregisterAudioThread(pid_t clientThreadId) EXCLUDES(mLock);
bool isRunning() const {
return mState == AAUDIO_STREAM_STATE_STARTED;
@@ -134,7 +137,7 @@
/**
* Fill in a parcelable description of stream.
*/
- aaudio_result_t getDescription(AudioEndpointParcelable &parcelable);
+ aaudio_result_t getDescription(AudioEndpointParcelable &parcelable) EXCLUDES(mLock);
void setRegisteredThread(pid_t pid) {
mRegisteredClientThread = pid;
@@ -150,18 +153,18 @@
void run() override; // to implement Runnable
- void disconnect();
+ void disconnect() EXCLUDES(mLock);
const android::AudioClient &getAudioClient() {
return mMmapClient;
}
uid_t getOwnerUserId() const {
- return mMmapClient.clientUid;
+ return VALUE_OR_FATAL(android::aidl2legacy_int32_t_uid_t(mMmapClient.identity.uid));
}
pid_t getOwnerProcessId() const {
- return mMmapClient.clientPid;
+ return VALUE_OR_FATAL(android::aidl2legacy_int32_t_pid_t(mMmapClient.identity.pid));
}
aaudio_handle_t getHandle() const {
@@ -208,25 +211,6 @@
return mSuspended;
}
- /**
- * Atomically increment the number of active references to the stream by AAudioService.
- *
- * This is called under a global lock in AAudioStreamTracker.
- *
- * @return value after the increment
- */
- int32_t incrementServiceReferenceCount_l();
-
- /**
- * Atomically decrement the number of active references to the stream by AAudioService.
- * This should only be called after incrementServiceReferenceCount_l().
- *
- * This is called under a global lock in AAudioStreamTracker.
- *
- * @return value after the decrement
- */
- int32_t decrementServiceReferenceCount_l();
-
bool isCloseNeeded() const {
return mCloseNeeded.load();
}
@@ -249,11 +233,10 @@
aaudio_result_t open(const aaudio::AAudioStreamRequest &request,
aaudio_sharing_mode_t sharingMode);
- // These must be called under mLock
- virtual aaudio_result_t close_l();
- virtual aaudio_result_t pause_l();
- virtual aaudio_result_t stop_l();
- void disconnect_l();
+ virtual aaudio_result_t close_l() REQUIRES(mLock);
+ virtual aaudio_result_t pause_l() REQUIRES(mLock);
+ virtual aaudio_result_t stop_l() REQUIRES(mLock);
+ void disconnect_l() REQUIRES(mLock);
void setState(aaudio_stream_state_t state);
@@ -265,7 +248,7 @@
aaudio_result_t writeUpMessageQueue(AAudioServiceMessage *command);
- aaudio_result_t sendCurrentTimestamp();
+ aaudio_result_t sendCurrentTimestamp() EXCLUDES(mLock);
aaudio_result_t sendXRunCount(int32_t xRunCount);
@@ -282,10 +265,17 @@
aaudio_stream_state_t mState = AAUDIO_STREAM_STATE_UNINITIALIZED;
+ bool isDisconnected_l() const REQUIRES(mLock) {
+ return mDisconnected;
+ }
+ void setDisconnected_l(bool flag) REQUIRES(mLock) {
+ mDisconnected = flag;
+ }
+
pid_t mRegisteredClientThread = ILLEGAL_THREAD_ID;
- SharedRingBuffer* mUpMessageQueue;
std::mutex mUpMessageQueueLock;
+ std::shared_ptr<SharedRingBuffer> mUpMessageQueue;
AAudioThread mTimestampThread;
// This is used by one thread to tell another thread to exit. So it must be atomic.
@@ -331,18 +321,19 @@
aaudio_handle_t mHandle = -1;
bool mFlowing = false;
- // This is modified under a global lock in AAudioStreamTracker.
- int32_t mCallingCount = 0;
-
- // This indicates that a stream that is being referenced by a binder call needs to closed.
- std::atomic<bool> mCloseNeeded{false};
+ // This indicates that a stream that is being referenced by a binder call
+ // and needs to closed.
+ std::atomic<bool> mCloseNeeded{false}; // TODO remove
// This indicate that a running stream should not be processed because of an error,
// for example a full message queue. Note that this atomic is unrelated to mCloseNeeded.
std::atomic<bool> mSuspended{false};
+ bool mDisconnected GUARDED_BY(mLock) {false};
+
+protected:
// Locking order is important.
- // Always acquire mLock before acquiring AAudioServiceEndpoint::mLockStreams
+ // Acquire mLock before acquiring AAudioServiceEndpoint::mLockStreams
std::mutex mLock; // Prevent start/stop/close etcetera from colliding
};
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 54d7d06..57dc1ab 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -19,6 +19,7 @@
#include <utils/Log.h>
#include <atomic>
+#include <inttypes.h>
#include <iomanip>
#include <iostream>
#include <stdint.h>
@@ -162,7 +163,8 @@
return result;
}
-// Get timestamp that was written by getFreeRunningPosition()
+// Get timestamp from presentation position.
+// If it fails, get timestamp that was written by getFreeRunningPosition()
aaudio_result_t AAudioServiceStreamMMAP::getHardwareTimestamp(int64_t *positionFrames,
int64_t *timeNanos) {
@@ -174,7 +176,17 @@
sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP =
static_cast<AAudioServiceEndpointMMAP *>(endpoint.get());
- // TODO Get presentation timestamp from the HAL
+ // Disable this code temporarily because the HAL is not returning
+ // a useful result.
+#if 0
+ uint64_t position;
+ if (serviceEndpointMMAP->getExternalPosition(&position, timeNanos) == AAUDIO_OK) {
+ ALOGD("%s() getExternalPosition() says pos = %" PRIi64 ", time = %" PRIi64,
+ __func__, position, *timeNanos);
+ *positionFrames = (int64_t) position;
+ return AAUDIO_OK;
+ } else
+#endif
if (mAtomicStreamTimestamp.isValid()) {
Timestamp timestamp = mAtomicStreamTimestamp.read();
*positionFrames = timestamp.getPosition();
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index 5902613..667465a 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -19,6 +19,7 @@
#include <atomic>
+#include <android-base/thread_annotations.h>
#include <android-base/unique_fd.h>
#include <media/audiohal/StreamHalInterface.h>
#include <media/MmapStreamCallback.h>
@@ -34,10 +35,8 @@
#include "TimestampScheduler.h"
#include "utility/MonotonicCounter.h"
-
namespace aaudio {
-
/**
* These corresponds to an EXCLUSIVE mode MMAP client stream.
* It has exclusive use of one AAudioServiceEndpointMMAP to communicate with the underlying
@@ -68,13 +67,14 @@
* This is not guaranteed to be synchronous but it currently is.
* An AAUDIO_SERVICE_EVENT_PAUSED will be sent to the client when complete.
*/
- aaudio_result_t pause_l() override;
+ aaudio_result_t pause_l() REQUIRES(mLock) override;
- aaudio_result_t stop_l() override;
+ aaudio_result_t stop_l() REQUIRES(mLock) override;
aaudio_result_t getAudioDataDescription(AudioEndpointParcelable &parcelable) override;
- aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) override;
+ aaudio_result_t getFreeRunningPosition(int64_t *positionFrames,
+ int64_t *timeNanos) EXCLUDES(mLock) override;
aaudio_result_t getHardwareTimestamp(int64_t *positionFrames, int64_t *timeNanos) override;
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index f2cf016..c665cda 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -24,8 +24,6 @@
#include <aaudio/AAudio.h>
-#include "binding/IAAudioService.h"
-
#include "binding/AAudioServiceMessage.h"
#include "AAudioServiceStreamBase.h"
#include "AAudioServiceStreamShared.h"
@@ -54,19 +52,26 @@
return result.str();
}
-std::string AAudioServiceStreamShared::dump() const {
+std::string AAudioServiceStreamShared::dump() const NO_THREAD_SAFETY_ANALYSIS {
std::stringstream result;
+ const bool isLocked = AAudio_tryUntilTrue(
+ [this]()->bool { return audioDataQueueLock.try_lock(); } /* f */,
+ 50 /* times */,
+ 20 /* sleepMs */);
+ if (!isLocked) {
+ result << "AAudioServiceStreamShared may be deadlocked\n";
+ }
+
result << AAudioServiceStreamBase::dump();
- auto fifo = mAudioDataQueue->getFifoBuffer();
- int32_t readCounter = fifo->getReadCounter();
- int32_t writeCounter = fifo->getWriteCounter();
- result << std::setw(10) << writeCounter;
- result << std::setw(10) << readCounter;
- result << std::setw(8) << (writeCounter - readCounter);
+ result << mAudioDataQueue->dump();
result << std::setw(8) << getXRunCount();
+ if (isLocked) {
+ audioDataQueueLock.unlock();
+ }
+
return result.str();
}
@@ -178,9 +183,9 @@
}
{
- std::lock_guard<std::mutex> lock(mAudioDataQueueLock);
+ std::lock_guard<std::mutex> lock(audioDataQueueLock);
// Create audio data shared memory buffer for client.
- mAudioDataQueue = new SharedRingBuffer();
+ mAudioDataQueue = std::make_shared<SharedRingBuffer>();
result = mAudioDataQueue->allocate(calculateBytesPerFrame(), getBufferCapacity());
if (result != AAUDIO_OK) {
ALOGE("%s() could not allocate FIFO with %d frames",
@@ -203,25 +208,13 @@
return result;
}
-aaudio_result_t AAudioServiceStreamShared::close_l() {
- aaudio_result_t result = AAudioServiceStreamBase::close_l();
-
- {
- std::lock_guard<std::mutex> lock(mAudioDataQueueLock);
- delete mAudioDataQueue;
- mAudioDataQueue = nullptr;
- }
-
- return result;
-}
-
/**
* Get an immutable description of the data queue created by this service.
*/
aaudio_result_t AAudioServiceStreamShared::getAudioDataDescription(
AudioEndpointParcelable &parcelable)
{
- std::lock_guard<std::mutex> lock(mAudioDataQueueLock);
+ std::lock_guard<std::mutex> lock(audioDataQueueLock);
if (mAudioDataQueue == nullptr) {
ALOGW("%s(): mUpMessageQueue null! - stream not open", __func__);
return AAUDIO_ERROR_NULL;
@@ -273,3 +266,37 @@
*positionFrames = position;
return result;
}
+
+void AAudioServiceStreamShared::writeDataIfRoom(int64_t mmapFramesRead,
+ const void *buffer, int32_t numFrames) {
+ int64_t clientFramesWritten = 0;
+
+ // Lock the AudioFifo to protect against close.
+ std::lock_guard <std::mutex> lock(audioDataQueueLock);
+
+ if (mAudioDataQueue != nullptr) {
+ std::shared_ptr<FifoBuffer> fifo = mAudioDataQueue->getFifoBuffer();
+ // Determine offset between framePosition in client's stream
+ // vs the underlying MMAP stream.
+ clientFramesWritten = fifo->getWriteCounter();
+ // There are two indices that refer to the same frame.
+ int64_t positionOffset = mmapFramesRead - clientFramesWritten;
+ setTimestampPositionOffset(positionOffset);
+
+ // Is the buffer too full to write a burst?
+ if (fifo->getEmptyFramesAvailable() < getFramesPerBurst()) {
+ incrementXRunCount();
+ } else {
+ fifo->write(buffer, numFrames);
+ }
+ clientFramesWritten = fifo->getWriteCounter();
+ }
+
+ if (clientFramesWritten > 0) {
+ // This timestamp represents the completion of data being written into the
+ // client buffer. It is sent to the client and used in the timing model
+ // to decide when data will be available to read.
+ Timestamp timestamp(clientFramesWritten, AudioClock::getNanoseconds());
+ markTransferTime(timestamp);
+ }
+}
diff --git a/services/oboeservice/AAudioServiceStreamShared.h b/services/oboeservice/AAudioServiceStreamShared.h
index abcb782..4fae5b4 100644
--- a/services/oboeservice/AAudioServiceStreamShared.h
+++ b/services/oboeservice/AAudioServiceStreamShared.h
@@ -52,23 +52,16 @@
aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
- aaudio_result_t close_l() override;
+ void writeDataIfRoom(int64_t mmapFramesRead, const void *buffer, int32_t numFrames);
/**
- * This must be locked when calling getAudioDataFifoBuffer_l() and while
- * using the FifoBuffer it returns.
- */
- std::mutex &getAudioDataQueueLock() {
- return mAudioDataQueueLock;
- }
-
- /**
- * This must only be call under getAudioDataQueueLock().
+ * This must only be called under getAudioDataQueueLock().
* @return
*/
- android::FifoBuffer *getAudioDataFifoBuffer_l() { return (mAudioDataQueue == nullptr)
- ? nullptr
- : mAudioDataQueue->getFifoBuffer(); }
+ std::shared_ptr<SharedRingBuffer> getAudioDataQueue_l()
+ REQUIRES(audioDataQueueLock) {
+ return mAudioDataQueue;
+ }
/* Keep a record of when a buffer transfer completed.
* This allows for a more accurate timing model.
@@ -89,6 +82,10 @@
const char *getTypeText() const override { return "Shared"; }
+ // This is public so that the thread safety annotation, GUARDED_BY(),
+ // Can work when another object takes the lock.
+ mutable std::mutex audioDataQueueLock;
+
protected:
aaudio_result_t getAudioDataDescription(AudioEndpointParcelable &parcelable) override;
@@ -106,8 +103,8 @@
int32_t framesPerBurst);
private:
- SharedRingBuffer *mAudioDataQueue = nullptr; // protected by mAudioDataQueueLock
- std::mutex mAudioDataQueueLock;
+
+ std::shared_ptr<SharedRingBuffer> mAudioDataQueue GUARDED_BY(audioDataQueueLock);
std::atomic<int64_t> mTimestampPositionOffset;
std::atomic<int32_t> mXRunCount;
diff --git a/services/oboeservice/AAudioStreamTracker.cpp b/services/oboeservice/AAudioStreamTracker.cpp
index 8e66b94..9bbbc73 100644
--- a/services/oboeservice/AAudioStreamTracker.cpp
+++ b/services/oboeservice/AAudioStreamTracker.cpp
@@ -96,7 +96,7 @@
return handle;
}
-std::string AAudioStreamTracker::dump() const {
+std::string AAudioStreamTracker::dump() const NO_THREAD_SAFETY_ANALYSIS {
std::stringstream result;
const bool isLocked = AAudio_tryUntilTrue(
[this]()->bool { return mHandleLock.try_lock(); } /* f */,
diff --git a/services/oboeservice/AAudioStreamTracker.h b/services/oboeservice/AAudioStreamTracker.h
index d1301a2..43870fc 100644
--- a/services/oboeservice/AAudioStreamTracker.h
+++ b/services/oboeservice/AAudioStreamTracker.h
@@ -17,13 +17,13 @@
#ifndef AAUDIO_AAUDIO_STREAM_TRACKER_H
#define AAUDIO_AAUDIO_STREAM_TRACKER_H
+#include <mutex>
#include <time.h>
-#include <pthread.h>
+#include <android-base/thread_annotations.h>
#include <aaudio/AAudio.h>
#include "binding/AAudioCommon.h"
-
#include "AAudioServiceStreamBase.h"
namespace aaudio {
@@ -75,11 +75,10 @@
static aaudio_handle_t bumpHandle(aaudio_handle_t handle);
// Track stream using a unique handle that wraps. Only use positive half.
- mutable std::mutex mHandleLock;
- // protected by mHandleLock
- aaudio_handle_t mPreviousHandle = 0;
- // protected by mHandleLock
- std::map<aaudio_handle_t, android::sp<aaudio::AAudioServiceStreamBase>> mStreamsByHandle;
+ mutable std::mutex mHandleLock;
+ aaudio_handle_t mPreviousHandle GUARDED_BY(mHandleLock) = 0;
+ std::map<aaudio_handle_t, android::sp<aaudio::AAudioServiceStreamBase>>
+ mStreamsByHandle GUARDED_BY(mHandleLock);
};
diff --git a/services/oboeservice/AAudioThread.cpp b/services/oboeservice/AAudioThread.cpp
index ed7895b..68496ac 100644
--- a/services/oboeservice/AAudioThread.cpp
+++ b/services/oboeservice/AAudioThread.cpp
@@ -37,10 +37,13 @@
setup("AAudio");
}
-void AAudioThread::setup(const char *prefix) {
- // mThread is a pthread_t of unknown size so we need memset().
- memset(&mThread, 0, sizeof(mThread));
+AAudioThread::~AAudioThread() {
+ ALOGE_IF(pthread_equal(pthread_self(), mThread),
+ "%s() destructor running in thread", __func__);
+ ALOGE_IF(mHasThread, "%s() thread never joined", __func__);
+}
+void AAudioThread::setup(const char *prefix) {
// Name the thread with an increasing index, "prefix_#", for debugging.
uint32_t index = mNextThreadIndex++;
// Wrap the index so that we do not hit the 16 char limit
@@ -57,7 +60,7 @@
}
}
-// This is the entry point for the new thread created by createThread().
+// This is the entry point for the new thread created by createThread_l().
// It converts the 'C' function call to a C++ method call.
static void * AAudioThread_internalThreadProc(void *arg) {
AAudioThread *aaudioThread = (AAudioThread *) arg;
@@ -90,13 +93,18 @@
ALOGE("stop() but no thread running");
return AAUDIO_ERROR_INVALID_STATE;
}
+ // Check to see if the thread is trying to stop itself.
+ if (pthread_equal(pthread_self(), mThread)) {
+ ALOGE("%s() attempt to pthread_join() from launched thread!", __func__);
+ return AAUDIO_ERROR_INTERNAL;
+ }
+
int err = pthread_join(mThread, nullptr);
- mHasThread = false;
if (err != 0) {
ALOGE("stop() - pthread_join() returned %d %s", err, strerror(err));
return AAudioConvert_androidToAAudioResult(-err);
} else {
+ mHasThread = false;
return AAUDIO_OK;
}
}
-
diff --git a/services/oboeservice/AAudioThread.h b/services/oboeservice/AAudioThread.h
index dcce68a..08a8a98 100644
--- a/services/oboeservice/AAudioThread.h
+++ b/services/oboeservice/AAudioThread.h
@@ -46,7 +46,7 @@
explicit AAudioThread(const char *prefix);
- virtual ~AAudioThread() = default;
+ virtual ~AAudioThread();
/**
* Start the thread running.
@@ -73,7 +73,7 @@
Runnable *mRunnable = nullptr;
bool mHasThread = false;
- pthread_t mThread; // initialized in constructor
+ pthread_t mThread = {};
static std::atomic<uint32_t> mNextThreadIndex;
char mName[16]; // max length for a pthread_name
diff --git a/services/oboeservice/Android.bp b/services/oboeservice/Android.bp
index c40a79e..a419dd5 100644
--- a/services/oboeservice/Android.bp
+++ b/services/oboeservice/Android.bp
@@ -46,6 +46,7 @@
],
cflags: [
+ "-Wthread-safety",
"-Wno-unused-parameter",
"-Wall",
"-Werror",
@@ -64,6 +65,14 @@
"libcutils",
"liblog",
"libutils",
+ "aaudio-aidl-cpp",
+ "media_permission-aidl-cpp",
+ "libaudioclient_aidl_conversion",
+ ],
+
+ export_shared_lib_headers: [
+ "libaaudio_internal",
+ "media_permission-aidl-cpp",
],
header_libs: [
diff --git a/services/oboeservice/SharedRingBuffer.cpp b/services/oboeservice/SharedRingBuffer.cpp
index 2454446..c1d4e16 100644
--- a/services/oboeservice/SharedRingBuffer.cpp
+++ b/services/oboeservice/SharedRingBuffer.cpp
@@ -18,6 +18,8 @@
//#define LOG_NDEBUG 0
#include <utils/Log.h>
+#include <iomanip>
+#include <iostream>
#include <sys/mman.h>
#include "binding/RingBufferParcelable.h"
@@ -30,8 +32,8 @@
SharedRingBuffer::~SharedRingBuffer()
{
+ mFifoBuffer.reset(); // uses mSharedMemory
if (mSharedMemory != nullptr) {
- delete mFifoBuffer;
munmap(mSharedMemory, mSharedMemorySizeInBytes);
mSharedMemory = nullptr;
}
@@ -58,16 +60,18 @@
return AAUDIO_ERROR_INTERNAL; // TODO convert errno to a better AAUDIO_ERROR;
}
- // Map the fd to memory addresses.
- mSharedMemory = (uint8_t *) mmap(0, mSharedMemorySizeInBytes,
+ // Map the fd to memory addresses. Use a temporary pointer to keep the mmap result and update
+ // it to `mSharedMemory` only when mmap operate successfully.
+ uint8_t* tmpPtr = (uint8_t *) mmap(0, mSharedMemorySizeInBytes,
PROT_READ|PROT_WRITE,
MAP_SHARED,
mFileDescriptor.get(), 0);
- if (mSharedMemory == MAP_FAILED) {
+ if (tmpPtr == MAP_FAILED) {
ALOGE("allocate() mmap() failed %d", errno);
mFileDescriptor.reset();
return AAUDIO_ERROR_INTERNAL; // TODO convert errno to a better AAUDIO_ERROR;
}
+ mSharedMemory = tmpPtr;
// Get addresses for our counters and data from the shared memory.
fifo_counter_t *readCounterAddress =
@@ -76,7 +80,7 @@
(fifo_counter_t *) &mSharedMemory[SHARED_RINGBUFFER_WRITE_OFFSET];
uint8_t *dataAddress = &mSharedMemory[SHARED_RINGBUFFER_DATA_OFFSET];
- mFifoBuffer = new FifoBuffer(bytesPerFrame, capacityInFrames,
+ mFifoBuffer = std::make_shared<FifoBufferIndirect>(bytesPerFrame, capacityInFrames,
readCounterAddress, writeCounterAddress, dataAddress);
return AAUDIO_OK;
}
@@ -94,3 +98,19 @@
ringBufferParcelable.setFramesPerBurst(1);
ringBufferParcelable.setCapacityInFrames(mCapacityInFrames);
}
+
+double SharedRingBuffer::getFractionalFullness() const {
+ int32_t framesAvailable = mFifoBuffer->getFullFramesAvailable();
+ int32_t capacity = mFifoBuffer->getBufferCapacityInFrames();
+ return framesAvailable / (double) capacity;
+}
+
+std::string SharedRingBuffer::dump() const {
+ std::stringstream result;
+ int32_t readCounter = mFifoBuffer->getReadCounter();
+ int32_t writeCounter = mFifoBuffer->getWriteCounter();
+ result << std::setw(10) << writeCounter;
+ result << std::setw(10) << readCounter;
+ result << std::setw(8) << (writeCounter - readCounter);
+ return result.str();
+}
diff --git a/services/oboeservice/SharedRingBuffer.h b/services/oboeservice/SharedRingBuffer.h
index 79169bc..c3a9bb7 100644
--- a/services/oboeservice/SharedRingBuffer.h
+++ b/services/oboeservice/SharedRingBuffer.h
@@ -18,8 +18,9 @@
#define AAUDIO_SHARED_RINGBUFFER_H
#include <android-base/unique_fd.h>
-#include <stdint.h>
#include <cutils/ashmem.h>
+#include <stdint.h>
+#include <string>
#include <sys/mman.h>
#include "fifo/FifoBuffer.h"
@@ -47,15 +48,25 @@
void fillParcelable(AudioEndpointParcelable &endpointParcelable,
RingBufferParcelable &ringBufferParcelable);
- android::FifoBuffer * getFifoBuffer() {
+ /**
+ * Return available frames as a fraction of the capacity.
+ * @return fullness between 0.0 and 1.0
+ */
+ double getFractionalFullness() const;
+
+ // dump: write# read# available
+ std::string dump() const;
+
+ std::shared_ptr<android::FifoBuffer> getFifoBuffer() {
return mFifoBuffer;
}
private:
android::base::unique_fd mFileDescriptor;
- android::FifoBuffer *mFifoBuffer = nullptr;
- uint8_t *mSharedMemory = nullptr;
+ std::shared_ptr<android::FifoBufferIndirect> mFifoBuffer;
+ uint8_t *mSharedMemory = nullptr; // mmap
int32_t mSharedMemorySizeInBytes = 0;
+ // size of memory used for data vs counters
int32_t mDataMemorySizeInBytes = 0;
android::fifo_frames_t mCapacityInFrames = 0;
};
diff --git a/services/oboeservice/fuzzer/Android.bp b/services/oboeservice/fuzzer/Android.bp
new file mode 100644
index 0000000..f4e8a81
--- /dev/null
+++ b/services/oboeservice/fuzzer/Android.bp
@@ -0,0 +1,72 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_fuzz {
+ name: "oboeservice_fuzzer",
+ srcs: [
+ "oboeservice_fuzzer.cpp",
+ ],
+ shared_libs: [
+ "libaaudio_internal",
+ "libaudioclient",
+ "libaudioflinger",
+ "libaudioutils",
+ "libmedia_helper",
+ "libmediametrics",
+ "libmediautils",
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "liblog",
+ "libutils",
+ "aaudio-aidl-cpp",
+ "media_permission-aidl-cpp",
+ "libaudioclient_aidl_conversion",
+ ],
+ static_libs: [
+ "libaaudioservice",
+ ],
+ include_dirs: [
+ "frameworks/av/services/oboeservice",
+ ],
+ header_libs: [
+ "libaudiohal_headers",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ ],
+ fuzz_config: {
+ cc: [
+ "android-media-fuzzing-reports@google.com",
+ ],
+ componentid: 155276,
+ },
+}
diff --git a/services/oboeservice/fuzzer/README.md b/services/oboeservice/fuzzer/README.md
new file mode 100644
index 0000000..00b85df
--- /dev/null
+++ b/services/oboeservice/fuzzer/README.md
@@ -0,0 +1,65 @@
+# Fuzzer for libaaudioservice
+
+## Plugin Design Considerations
+The fuzzer plugin for libaaudioservice is designed based on the
+understanding of the service and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+AAudio Service request contains the following parameters:
+1. AAudioFormat
+2. UserId
+3. ProcessId
+4. InService
+5. DeviceId
+6. SampleRate
+7. SamplesPerFrame
+8. Direction
+9. SharingMode
+10. Usage
+11. ContentType
+12. InputPreset
+13. BufferCapacity
+
+| Parameter| Valid Input Values| Configured Value|
+|------------- |-------------| ----- |
+| `AAudioFormat` | `AAUDIO_FORMAT_UNSPECIFIED`, `AAUDIO_FORMAT_PCM_I16`, `AAUDIO_FORMAT_PCM_FLOAT` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `UserId` | `INT32_MIN` to `INT32_MAX` | Value obtained from getuid() |
+| `ProcessId` | `INT32_MIN` to `INT32_MAX` | Value obtained from getpid() |
+| `InService` | `bool` | Value obtained from FuzzedDataProvider |
+| `DeviceId` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider |
+| `SampleRate` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider |
+| `SamplesPerFrame` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider |
+| `Direction` | `AAUDIO_DIRECTION_OUTPUT`, `AAUDIO_DIRECTION_INPUT` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `SharingMode` | `AAUDIO_SHARING_MODE_EXCLUSIVE`, `AAUDIO_SHARING_MODE_SHARED` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `Usage` | `AAUDIO_USAGE_MEDIA`, `AAUDIO_USAGE_VOICE_COMMUNICATION`, `AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING`, `AAUDIO_USAGE_ALARM`, `AAUDIO_USAGE_NOTIFICATION`, `AAUDIO_USAGE_NOTIFICATION_RINGTONE`, `AAUDIO_USAGE_NOTIFICATION_EVENT`, `AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY`, `AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE`, `AAUDIO_USAGE_ASSISTANCE_SONIFICATION`, `AAUDIO_USAGE_GAME`, `AAUDIO_USAGE_ASSISTANT`, `AAUDIO_SYSTEM_USAGE_EMERGENCY`, `AAUDIO_SYSTEM_USAGE_SAFETY`, `AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS`, `AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `ContentType` | `AAUDIO_CONTENT_TYPE_SPEECH`, `AAUDIO_CONTENT_TYPE_MUSIC`, `AAUDIO_CONTENT_TYPE_MOVIE`, `AAUDIO_CONTENT_TYPE_SONIFICATION` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `InputPreset` | `AAUDIO_INPUT_PRESET_GENERIC`, `AAUDIO_INPUT_PRESET_CAMCORDER`, `AAUDIO_INPUT_PRESET_VOICE_RECOGNITION`, `AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION`, `AAUDIO_INPUT_PRESET_UNPROCESSED`, `AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `BufferCapacity` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider |
+
+This also ensures that the plugin is always deterministic for any given input.
+
+## Build
+
+This describes steps to build oboeservice_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+ $ mm -j$(nproc) oboeservice_fuzzer
+```
+
+#### Steps to run
+To run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/oboeservice_fuzzer/oboeservice_fuzzer
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/services/oboeservice/fuzzer/oboeservice_fuzzer.cpp b/services/oboeservice/fuzzer/oboeservice_fuzzer.cpp
new file mode 100644
index 0000000..8e508d3
--- /dev/null
+++ b/services/oboeservice/fuzzer/oboeservice_fuzzer.cpp
@@ -0,0 +1,369 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+#include <fuzzer/FuzzedDataProvider.h>
+#include <stdio.h>
+
+#include <AAudioService.h>
+#include <aaudio/AAudio.h>
+#include "aaudio/BnAAudioClient.h"
+#include <android/media/permission/Identity.h>
+
+#define UNUSED_PARAM __attribute__((unused))
+
+using namespace android;
+using namespace aaudio;
+
+aaudio_format_t kAAudioFormats[] = {
+ AAUDIO_FORMAT_UNSPECIFIED,
+ AAUDIO_FORMAT_PCM_I16,
+ AAUDIO_FORMAT_PCM_FLOAT,
+};
+
+aaudio_usage_t kAAudioUsages[] = {
+ AAUDIO_USAGE_MEDIA,
+ AAUDIO_USAGE_VOICE_COMMUNICATION,
+ AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING,
+ AAUDIO_USAGE_ALARM,
+ AAUDIO_USAGE_NOTIFICATION,
+ AAUDIO_USAGE_NOTIFICATION_RINGTONE,
+ AAUDIO_USAGE_NOTIFICATION_EVENT,
+ AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY,
+ AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
+ AAUDIO_USAGE_ASSISTANCE_SONIFICATION,
+ AAUDIO_USAGE_GAME,
+ AAUDIO_USAGE_ASSISTANT,
+ AAUDIO_SYSTEM_USAGE_EMERGENCY,
+ AAUDIO_SYSTEM_USAGE_SAFETY,
+ AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS,
+ AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT,
+};
+
+aaudio_content_type_t kAAudioContentTypes[] = {
+ AAUDIO_CONTENT_TYPE_SPEECH,
+ AAUDIO_CONTENT_TYPE_MUSIC,
+ AAUDIO_CONTENT_TYPE_MOVIE,
+ AAUDIO_CONTENT_TYPE_SONIFICATION,
+};
+
+aaudio_input_preset_t kAAudioInputPresets[] = {
+ AAUDIO_INPUT_PRESET_GENERIC, AAUDIO_INPUT_PRESET_CAMCORDER,
+ AAUDIO_INPUT_PRESET_VOICE_RECOGNITION, AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION,
+ AAUDIO_INPUT_PRESET_UNPROCESSED, AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE,
+};
+
+const size_t kNumAAudioFormats = std::size(kAAudioFormats);
+const size_t kNumAAudioUsages = std::size(kAAudioUsages);
+const size_t kNumAAudioContentTypes = std::size(kAAudioContentTypes);
+const size_t kNumAAudioInputPresets = std::size(kAAudioInputPresets);
+
+class FuzzAAudioClient : public virtual RefBase, public AAudioServiceInterface {
+ public:
+ FuzzAAudioClient(sp<AAudioService> service);
+
+ virtual ~FuzzAAudioClient();
+
+ AAudioServiceInterface *getAAudioService();
+
+ void dropAAudioService();
+
+ void registerClient(const sp<IAAudioClient> &client UNUSED_PARAM) override {}
+
+ aaudio_handle_t openStream(const AAudioStreamRequest &request,
+ AAudioStreamConfiguration &configurationOutput) override;
+
+ aaudio_result_t closeStream(aaudio_handle_t streamHandle) override;
+
+ aaudio_result_t getStreamDescription(aaudio_handle_t streamHandle,
+ AudioEndpointParcelable &parcelable) override;
+
+ aaudio_result_t startStream(aaudio_handle_t streamHandle) override;
+
+ aaudio_result_t pauseStream(aaudio_handle_t streamHandle) override;
+
+ aaudio_result_t stopStream(aaudio_handle_t streamHandle) override;
+
+ aaudio_result_t flushStream(aaudio_handle_t streamHandle) override;
+
+ aaudio_result_t registerAudioThread(aaudio_handle_t streamHandle, pid_t clientThreadId,
+ int64_t periodNanoseconds) override;
+
+ aaudio_result_t unregisterAudioThread(aaudio_handle_t streamHandle,
+ pid_t clientThreadId) override;
+
+ aaudio_result_t startClient(aaudio_handle_t streamHandle UNUSED_PARAM,
+ const AudioClient &client UNUSED_PARAM,
+ const audio_attributes_t *attr UNUSED_PARAM,
+ audio_port_handle_t *clientHandle UNUSED_PARAM) override {
+ return AAUDIO_ERROR_UNAVAILABLE;
+ }
+
+ aaudio_result_t stopClient(aaudio_handle_t streamHandle UNUSED_PARAM,
+ audio_port_handle_t clientHandle UNUSED_PARAM) override {
+ return AAUDIO_ERROR_UNAVAILABLE;
+ }
+
+ void onStreamChange(aaudio_handle_t handle, int32_t opcode, int32_t value) {}
+
+ int getDeathCount() { return mDeathCount; }
+
+ void incDeathCount() { ++mDeathCount; }
+
+ class AAudioClient : public IBinder::DeathRecipient, public BnAAudioClient {
+ public:
+ AAudioClient(wp<FuzzAAudioClient> fuzzAAudioClient) : mBinderClient(fuzzAAudioClient) {}
+
+ virtual void binderDied(const wp<IBinder> &who UNUSED_PARAM) {
+ sp<FuzzAAudioClient> client = mBinderClient.promote();
+ if (client.get()) {
+ client->dropAAudioService();
+ client->incDeathCount();
+ }
+ }
+
+ android::binder::Status onStreamChange(int32_t handle, int32_t opcode, int32_t value) {
+ static_assert(std::is_same_v<aaudio_handle_t, int32_t>);
+ android::sp<FuzzAAudioClient> client = mBinderClient.promote();
+ if (client.get() != nullptr) {
+ client->onStreamChange(handle, opcode, value);
+ }
+ return android::binder::Status::ok();
+ }
+
+ private:
+ wp<FuzzAAudioClient> mBinderClient;
+ };
+
+ private:
+ sp<AAudioService> mAAudioService;
+ sp<AAudioClient> mAAudioClient;
+ AAudioServiceInterface *mAAudioServiceInterface;
+ int mDeathCount;
+};
+
+FuzzAAudioClient::FuzzAAudioClient(sp<AAudioService> service) : AAudioServiceInterface() {
+ mAAudioService = service;
+ mAAudioServiceInterface = &service->asAAudioServiceInterface();
+ mAAudioClient = new AAudioClient(this);
+ mDeathCount = 0;
+ if (mAAudioClient.get() && mAAudioService.get()) {
+ mAAudioService->linkToDeath(mAAudioClient);
+ mAAudioService->registerClient(mAAudioClient);
+ }
+}
+
+FuzzAAudioClient::~FuzzAAudioClient() { dropAAudioService(); }
+
+AAudioServiceInterface *FuzzAAudioClient::getAAudioService() {
+ if (!mAAudioServiceInterface && mAAudioService.get()) {
+ mAAudioServiceInterface = &mAAudioService->asAAudioServiceInterface();
+ }
+ return mAAudioServiceInterface;
+}
+
+void FuzzAAudioClient::dropAAudioService() {
+ mAAudioService.clear();
+}
+
+aaudio_handle_t FuzzAAudioClient::openStream(const AAudioStreamRequest &request,
+ AAudioStreamConfiguration &configurationOutput) {
+ aaudio_handle_t stream;
+ for (int i = 0; i < 2; ++i) {
+ AAudioServiceInterface *service = getAAudioService();
+ if (!service) {
+ return AAUDIO_ERROR_NO_SERVICE;
+ }
+
+ stream = service->openStream(request, configurationOutput);
+
+ if (stream == AAUDIO_ERROR_NO_SERVICE) {
+ dropAAudioService();
+ } else {
+ break;
+ }
+ }
+ return stream;
+}
+
+aaudio_result_t FuzzAAudioClient::closeStream(aaudio_handle_t streamHandle) {
+ AAudioServiceInterface *service = getAAudioService();
+ if (!service) {
+ return AAUDIO_ERROR_NO_SERVICE;
+ }
+ return service->closeStream(streamHandle);
+}
+
+aaudio_result_t FuzzAAudioClient::getStreamDescription(aaudio_handle_t streamHandle,
+ AudioEndpointParcelable &parcelable) {
+ AAudioServiceInterface *service = getAAudioService();
+ if (!service) {
+ return AAUDIO_ERROR_NO_SERVICE;
+ }
+ return service->getStreamDescription(streamHandle, parcelable);
+}
+
+aaudio_result_t FuzzAAudioClient::startStream(aaudio_handle_t streamHandle) {
+ AAudioServiceInterface *service = getAAudioService();
+ if (!service) {
+ return AAUDIO_ERROR_NO_SERVICE;
+ }
+ return service->startStream(streamHandle);
+}
+
+aaudio_result_t FuzzAAudioClient::pauseStream(aaudio_handle_t streamHandle) {
+ AAudioServiceInterface *service = getAAudioService();
+ if (!service) {
+ return AAUDIO_ERROR_NO_SERVICE;
+ }
+ return service->pauseStream(streamHandle);
+}
+
+aaudio_result_t FuzzAAudioClient::stopStream(aaudio_handle_t streamHandle) {
+ AAudioServiceInterface *service = getAAudioService();
+ if (!service) {
+ return AAUDIO_ERROR_NO_SERVICE;
+ }
+ return service->stopStream(streamHandle);
+}
+
+aaudio_result_t FuzzAAudioClient::flushStream(aaudio_handle_t streamHandle) {
+ AAudioServiceInterface *service = getAAudioService();
+ if (!service) {
+ return AAUDIO_ERROR_NO_SERVICE;
+ }
+ return service->flushStream(streamHandle);
+}
+
+aaudio_result_t FuzzAAudioClient::registerAudioThread(aaudio_handle_t streamHandle,
+ pid_t clientThreadId,
+ int64_t periodNanoseconds) {
+ AAudioServiceInterface *service = getAAudioService();
+ if (!service) {
+ return AAUDIO_ERROR_NO_SERVICE;
+ }
+ return service->registerAudioThread(streamHandle, clientThreadId, periodNanoseconds);
+}
+
+aaudio_result_t FuzzAAudioClient::unregisterAudioThread(aaudio_handle_t streamHandle,
+ pid_t clientThreadId) {
+ AAudioServiceInterface *service = getAAudioService();
+ if (!service) {
+ return AAUDIO_ERROR_NO_SERVICE;
+ }
+ return service->unregisterAudioThread(streamHandle, clientThreadId);
+}
+
+class OboeserviceFuzzer {
+ public:
+ OboeserviceFuzzer();
+ ~OboeserviceFuzzer() = default;
+ void process(const uint8_t *data, size_t size);
+
+ private:
+ sp<FuzzAAudioClient> mClient;
+};
+
+OboeserviceFuzzer::OboeserviceFuzzer() {
+ sp<AAudioService> service = new AAudioService();
+ mClient = new FuzzAAudioClient(service);
+}
+
+void OboeserviceFuzzer::process(const uint8_t *data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ AAudioStreamRequest request;
+ AAudioStreamConfiguration configurationOutput;
+
+ // Initialize stream request
+ request.getConfiguration().setFormat((audio_format_t)(
+ fdp.ConsumeBool()
+ ? fdp.ConsumeIntegral<int32_t>()
+ : kAAudioFormats[fdp.ConsumeIntegralInRange<int32_t>(0, kNumAAudioFormats - 1)]));
+
+ // TODO b/182392769: use identity util
+ media::permission::Identity identity;
+ identity.uid = getuid();
+ identity.pid = getpid();
+ request.setIdentity(identity);
+ request.setInService(fdp.ConsumeBool());
+
+ request.getConfiguration().setDeviceId(fdp.ConsumeIntegral<int32_t>());
+ request.getConfiguration().setSampleRate(fdp.ConsumeIntegral<int32_t>());
+ request.getConfiguration().setSamplesPerFrame(fdp.ConsumeIntegral<int32_t>());
+ request.getConfiguration().setDirection(
+ fdp.ConsumeBool() ? fdp.ConsumeIntegral<int32_t>()
+ : (fdp.ConsumeBool() ? AAUDIO_DIRECTION_OUTPUT : AAUDIO_DIRECTION_INPUT));
+ request.getConfiguration().setSharingMode(
+ fdp.ConsumeBool()
+ ? fdp.ConsumeIntegral<int32_t>()
+ : (fdp.ConsumeBool() ? AAUDIO_SHARING_MODE_EXCLUSIVE : AAUDIO_SHARING_MODE_SHARED));
+
+ request.getConfiguration().setUsage(
+ fdp.ConsumeBool()
+ ? fdp.ConsumeIntegral<int32_t>()
+ : kAAudioUsages[fdp.ConsumeIntegralInRange<int32_t>(0, kNumAAudioUsages - 1)]);
+ request.getConfiguration().setContentType(
+ fdp.ConsumeBool() ? fdp.ConsumeIntegral<int32_t>()
+ : kAAudioContentTypes[fdp.ConsumeIntegralInRange<int32_t>(
+ 0, kNumAAudioContentTypes - 1)]);
+ request.getConfiguration().setInputPreset(
+ fdp.ConsumeBool() ? fdp.ConsumeIntegral<int32_t>()
+ : kAAudioInputPresets[fdp.ConsumeIntegralInRange<int32_t>(
+ 0, kNumAAudioInputPresets - 1)]);
+ request.getConfiguration().setPrivacySensitive(fdp.ConsumeBool());
+
+ request.getConfiguration().setBufferCapacity(fdp.ConsumeIntegral<int32_t>());
+
+ aaudio_handle_t stream = mClient->openStream(request, configurationOutput);
+ if (stream < 0) {
+ // invalid request, stream not opened.
+ return;
+ }
+ while (fdp.remaining_bytes()) {
+ AudioEndpointParcelable audioEndpointParcelable;
+ int action = fdp.ConsumeIntegralInRange<int32_t>(0, 4);
+ switch (action) {
+ case 0:
+ mClient->getStreamDescription(stream, audioEndpointParcelable);
+ break;
+ case 1:
+ mClient->startStream(stream);
+ break;
+ case 2:
+ mClient->pauseStream(stream);
+ break;
+ case 3:
+ mClient->stopStream(stream);
+ break;
+ case 4:
+ mClient->flushStream(stream);
+ break;
+ }
+ }
+ mClient->closeStream(stream);
+ assert(mClient->getDeathCount() == 0);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 1) {
+ return 0;
+ }
+ OboeserviceFuzzer oboeserviceFuzzer;
+ oboeserviceFuzzer.process(data, size);
+ return 0;
+}
diff --git a/services/tuner/Android.bp b/services/tuner/Android.bp
new file mode 100644
index 0000000..df2b4a3
--- /dev/null
+++ b/services/tuner/Android.bp
@@ -0,0 +1,145 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+filegroup {
+ name: "tv_tuner_aidl",
+ srcs: [
+ "aidl/android/media/tv/tuner/*.aidl",
+ ],
+ path: "aidl",
+}
+
+filegroup {
+ name: "tv_tuner_frontend_info",
+ srcs: [
+ "aidl/android/media/tv/tuner/TunerFrontendInfo.aidl",
+ "aidl/android/media/tv/tuner/TunerFrontend*Capabilities.aidl",
+ ],
+ path: "aidl",
+}
+
+aidl_interface {
+ name: "tv_tuner_aidl_interface",
+ unstable: true,
+ local_include_dir: "aidl",
+ srcs: [
+ ":tv_tuner_aidl",
+ ],
+ imports: [
+ "android.hardware.common",
+ "android.hardware.common.fmq",
+ ],
+
+ backend: {
+ java: {
+ enabled: false,
+ },
+ cpp: {
+ enabled: false,
+ },
+ ndk: {
+ enabled: true,
+ },
+ },
+}
+
+aidl_interface {
+ name: "tv_tuner_frontend_info_aidl_interface",
+ unstable: true,
+ local_include_dir: "aidl",
+ srcs: [
+ ":tv_tuner_frontend_info",
+ ],
+
+ backend: {
+ java: {
+ enabled: true,
+ },
+ cpp: {
+ enabled: true,
+ },
+ ndk: {
+ enabled: true,
+ },
+ },
+}
+
+cc_library {
+ name: "libtunerservice",
+
+ srcs: [
+ "Tuner*.cpp",
+ ],
+
+ shared_libs: [
+ "android.hardware.tv.tuner@1.0",
+ "android.hardware.tv.tuner@1.1",
+ "libbase",
+ "libbinder",
+ "libbinder_ndk",
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "liblog",
+ "libmedia",
+ "libutils",
+ "tv_tuner_aidl_interface-ndk_platform",
+ "tv_tuner_resource_manager_aidl_interface-ndk_platform",
+ "tv_tuner_resource_manager_aidl_interface-cpp",
+ ],
+
+ static_libs: [
+ "android.hardware.common.fmq-V1-ndk_platform",
+ "libaidlcommonsupport",
+ ],
+
+ include_dirs: [
+ "frameworks/av/include"
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ export_include_dirs: ["."],
+}
+
+
+cc_binary {
+ name: "mediatuner",
+
+ srcs: [
+ "main_tunerservice.cpp",
+ ],
+
+ shared_libs: [
+ "android.hardware.tv.tuner@1.0",
+ "android.hardware.tv.tuner@1.1",
+ "libbase",
+ "libbinder",
+ "libfmq",
+ "liblog",
+ "libtunerservice",
+ "libutils",
+ "tv_tuner_resource_manager_aidl_interface-ndk_platform",
+ "tv_tuner_resource_manager_aidl_interface-cpp",
+ ],
+
+ static_libs: [
+ "tv_tuner_aidl_interface-ndk_platform",
+ ],
+
+ init_rc: ["mediatuner.rc"],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+}
diff --git a/services/tuner/OWNERS b/services/tuner/OWNERS
new file mode 100644
index 0000000..0ceb8e8
--- /dev/null
+++ b/services/tuner/OWNERS
@@ -0,0 +1,2 @@
+nchalko@google.com
+quxiangfang@google.com
diff --git a/services/tuner/TunerDemux.cpp b/services/tuner/TunerDemux.cpp
new file mode 100644
index 0000000..1122368
--- /dev/null
+++ b/services/tuner/TunerDemux.cpp
@@ -0,0 +1,232 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TunerDemux"
+
+#include "TunerDvr.h"
+#include "TunerDemux.h"
+#include "TunerTimeFilter.h"
+
+using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterType;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
+using ::android::hardware::tv::tuner::V1_0::DemuxIpFilterType;
+using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
+using ::android::hardware::tv::tuner::V1_0::DemuxTlvFilterType;
+using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
+using ::android::hardware::tv::tuner::V1_0::DvrType;
+using ::android::hardware::tv::tuner::V1_0::Result;
+
+namespace android {
+
+TunerDemux::TunerDemux(sp<IDemux> demux, int id) {
+ mDemux = demux;
+ mDemuxId = id;
+}
+
+TunerDemux::~TunerDemux() {
+ mDemux = nullptr;
+}
+
+Status TunerDemux::setFrontendDataSource(const std::shared_ptr<ITunerFrontend>& frontend) {
+ if (mDemux == nullptr) {
+ ALOGE("IDemux is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ int frontendId;
+ frontend->getFrontendId(&frontendId);
+ Result res = mDemux->setFrontendDataSource(frontendId);
+ if (res != Result::SUCCESS) {
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+Status TunerDemux::openFilter(
+ int type, int subType, int bufferSize, const std::shared_ptr<ITunerFilterCallback>& cb,
+ std::shared_ptr<ITunerFilter>* _aidl_return) {
+ if (mDemux == nullptr) {
+ ALOGE("IDemux is not initialized.");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ DemuxFilterMainType mainType = static_cast<DemuxFilterMainType>(type);
+ DemuxFilterType filterType {
+ .mainType = mainType,
+ };
+
+ switch(mainType) {
+ case DemuxFilterMainType::TS:
+ filterType.subType.tsFilterType(static_cast<DemuxTsFilterType>(subType));
+ break;
+ case DemuxFilterMainType::MMTP:
+ filterType.subType.mmtpFilterType(static_cast<DemuxMmtpFilterType>(subType));
+ break;
+ case DemuxFilterMainType::IP:
+ filterType.subType.ipFilterType(static_cast<DemuxIpFilterType>(subType));
+ break;
+ case DemuxFilterMainType::TLV:
+ filterType.subType.tlvFilterType(static_cast<DemuxTlvFilterType>(subType));
+ break;
+ case DemuxFilterMainType::ALP:
+ filterType.subType.alpFilterType(static_cast<DemuxAlpFilterType>(subType));
+ break;
+ }
+ Result status;
+ sp<IFilter> filterSp;
+ sp<IFilterCallback> cbSp = new TunerFilter::FilterCallback(cb);
+ mDemux->openFilter(filterType, bufferSize, cbSp,
+ [&](Result r, const sp<IFilter>& filter) {
+ filterSp = filter;
+ status = r;
+ });
+ if (status != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+ }
+
+ *_aidl_return = ::ndk::SharedRefBase::make<TunerFilter>(filterSp, type, subType);
+ return Status::ok();
+}
+
+Status TunerDemux::openTimeFilter(shared_ptr<ITunerTimeFilter>* _aidl_return) {
+ if (mDemux == nullptr) {
+ ALOGE("IDemux is not initialized.");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result status;
+ sp<ITimeFilter> filterSp;
+ mDemux->openTimeFilter([&](Result r, const sp<ITimeFilter>& filter) {
+ filterSp = filter;
+ status = r;
+ });
+ if (status != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+ }
+
+ *_aidl_return = ::ndk::SharedRefBase::make<TunerTimeFilter>(filterSp);
+ return Status::ok();
+}
+
+Status TunerDemux::getAvSyncHwId(const shared_ptr<ITunerFilter>& tunerFilter, int* _aidl_return) {
+ if (mDemux == nullptr) {
+ ALOGE("IDemux is not initialized.");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ uint32_t avSyncHwId;
+ Result res;
+ sp<IFilter> halFilter = static_cast<TunerFilter*>(tunerFilter.get())->getHalFilter();
+ mDemux->getAvSyncHwId(halFilter,
+ [&](Result r, uint32_t id) {
+ res = r;
+ avSyncHwId = id;
+ });
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+
+ *_aidl_return = (int)avSyncHwId;
+ return Status::ok();
+}
+
+Status TunerDemux::getAvSyncTime(int avSyncHwId, int64_t* _aidl_return) {
+ if (mDemux == nullptr) {
+ ALOGE("IDemux is not initialized.");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ uint64_t time;
+ Result res;
+ mDemux->getAvSyncTime(static_cast<uint32_t>(avSyncHwId),
+ [&](Result r, uint64_t ts) {
+ res = r;
+ time = ts;
+ });
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+
+ *_aidl_return = (int64_t)time;
+ return Status::ok();
+}
+
+Status TunerDemux::openDvr(int dvrType, int bufferSize, const shared_ptr<ITunerDvrCallback>& cb,
+ shared_ptr<ITunerDvr>* _aidl_return) {
+ if (mDemux == nullptr) {
+ ALOGE("IDemux is not initialized.");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res;
+ sp<IDvrCallback> callback = new TunerDvr::DvrCallback(cb);
+ sp<IDvr> hidlDvr;
+ mDemux->openDvr(static_cast<DvrType>(dvrType), bufferSize, callback,
+ [&](Result r, const sp<IDvr>& dvr) {
+ hidlDvr = dvr;
+ res = r;
+ });
+ if (res != Result::SUCCESS) {
+ *_aidl_return = NULL;
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+
+ *_aidl_return = ::ndk::SharedRefBase::make<TunerDvr>(hidlDvr, dvrType);
+ return Status::ok();
+}
+
+Status TunerDemux::connectCiCam(int ciCamId) {
+ if (mDemux == nullptr) {
+ ALOGE("IDemux is not initialized.");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res = mDemux->connectCiCam(static_cast<uint32_t>(ciCamId));
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+Status TunerDemux::disconnectCiCam() {
+ if (mDemux == nullptr) {
+ ALOGE("IDemux is not initialized.");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res = mDemux->disconnectCiCam();
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+Status TunerDemux::close() {
+ if (mDemux == nullptr) {
+ ALOGE("IDemux is not initialized.");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res = mDemux->close();
+ mDemux = NULL;
+
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+} // namespace android
diff --git a/services/tuner/TunerDemux.h b/services/tuner/TunerDemux.h
new file mode 100644
index 0000000..2a9836b
--- /dev/null
+++ b/services/tuner/TunerDemux.h
@@ -0,0 +1,68 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TUNERDEMUX_H
+#define ANDROID_MEDIA_TUNERDEMUX_H
+
+#include <aidl/android/media/tv/tuner/BnTunerDemux.h>
+#include <android/hardware/tv/tuner/1.0/ITuner.h>
+
+using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::media::tv::tuner::BnTunerDemux;
+using ::aidl::android::media::tv::tuner::ITunerDvr;
+using ::aidl::android::media::tv::tuner::ITunerDvrCallback;
+using ::aidl::android::media::tv::tuner::ITunerFilter;
+using ::aidl::android::media::tv::tuner::ITunerFilterCallback;
+using ::aidl::android::media::tv::tuner::ITunerFrontend;
+using ::aidl::android::media::tv::tuner::ITunerTimeFilter;
+using ::android::hardware::tv::tuner::V1_0::IDemux;
+using ::android::hardware::tv::tuner::V1_0::IDvr;
+using ::android::hardware::tv::tuner::V1_0::IDvrCallback;
+using ::android::hardware::tv::tuner::V1_0::ITimeFilter;
+
+using namespace std;
+
+namespace android {
+
+class TunerDemux : public BnTunerDemux {
+
+public:
+ TunerDemux(sp<IDemux> demux, int demuxId);
+ virtual ~TunerDemux();
+ Status setFrontendDataSource(const shared_ptr<ITunerFrontend>& frontend) override;
+ Status openFilter(
+ int mainType, int subtype, int bufferSize, const shared_ptr<ITunerFilterCallback>& cb,
+ shared_ptr<ITunerFilter>* _aidl_return) override;
+ Status openTimeFilter(shared_ptr<ITunerTimeFilter>* _aidl_return) override;
+ Status getAvSyncHwId(const shared_ptr<ITunerFilter>& tunerFilter, int* _aidl_return) override;
+ Status getAvSyncTime(int avSyncHwId, int64_t* _aidl_return) override;
+ Status openDvr(
+ int dvbType, int bufferSize, const shared_ptr<ITunerDvrCallback>& cb,
+ shared_ptr<ITunerDvr>* _aidl_return) override;
+ Status connectCiCam(int ciCamId) override;
+ Status disconnectCiCam() override;
+ Status close() override;
+
+ int getId() { return mDemuxId; }
+
+private:
+ sp<IDemux> mDemux;
+ int mDemuxId;
+};
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_TUNERDEMUX_H
diff --git a/services/tuner/TunerDescrambler.cpp b/services/tuner/TunerDescrambler.cpp
new file mode 100644
index 0000000..b7ae167
--- /dev/null
+++ b/services/tuner/TunerDescrambler.cpp
@@ -0,0 +1,124 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TunerDescrambler"
+
+#include "TunerFilter.h"
+#include "TunerDemux.h"
+#include "TunerDescrambler.h"
+
+using ::android::hardware::tv::tuner::V1_0::Result;
+
+using namespace std;
+
+namespace android {
+
+TunerDescrambler::TunerDescrambler(sp<IDescrambler> descrambler) {
+ mDescrambler = descrambler;
+}
+
+TunerDescrambler::~TunerDescrambler() {
+ mDescrambler = nullptr;
+}
+
+Status TunerDescrambler::setDemuxSource(const std::shared_ptr<ITunerDemux>& demux) {
+ if (mDescrambler == nullptr) {
+ ALOGE("IDescrambler is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res = mDescrambler->setDemuxSource(static_cast<TunerDemux*>(demux.get())->getId());
+ if (res != Result::SUCCESS) {
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+Status TunerDescrambler::setKeyToken(const vector<uint8_t>& keyToken) {
+ if (mDescrambler == nullptr) {
+ ALOGE("IDescrambler is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res = mDescrambler->setKeyToken(keyToken);
+ if (res != Result::SUCCESS) {
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+Status TunerDescrambler::addPid(const TunerDemuxPid& pid,
+ const shared_ptr<ITunerFilter>& optionalSourceFilter) {
+ if (mDescrambler == nullptr) {
+ ALOGE("IDescrambler is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ sp<IFilter> halFilter = (optionalSourceFilter == NULL)
+ ? NULL : static_cast<TunerFilter*>(optionalSourceFilter.get())->getHalFilter();
+ Result res = mDescrambler->addPid(getHidlDemuxPid(pid), halFilter);
+ if (res != Result::SUCCESS) {
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+Status TunerDescrambler::removePid(const TunerDemuxPid& pid,
+ const shared_ptr<ITunerFilter>& optionalSourceFilter) {
+ if (mDescrambler == nullptr) {
+ ALOGE("IDescrambler is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ sp<IFilter> halFilter = (optionalSourceFilter == NULL)
+ ? NULL : static_cast<TunerFilter*>(optionalSourceFilter.get())->getHalFilter();
+ Result res = mDescrambler->removePid(getHidlDemuxPid(pid), halFilter);
+ if (res != Result::SUCCESS) {
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+Status TunerDescrambler::close() {
+ if (mDescrambler == nullptr) {
+ ALOGE("IDescrambler is not initialized.");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res = mDescrambler->close();
+ mDescrambler = NULL;
+
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+DemuxPid TunerDescrambler::getHidlDemuxPid(const TunerDemuxPid& pid) {
+ DemuxPid hidlPid;
+ switch (pid.getTag()) {
+ case TunerDemuxPid::tPid: {
+ hidlPid.tPid((uint16_t)pid.get<TunerDemuxPid::tPid>());
+ break;
+ }
+ case TunerDemuxPid::mmtpPid: {
+ hidlPid.mmtpPid((uint16_t)pid.get<TunerDemuxPid::mmtpPid>());
+ break;
+ }
+ }
+ return hidlPid;
+}
+} // namespace android
diff --git a/services/tuner/TunerDescrambler.h b/services/tuner/TunerDescrambler.h
new file mode 100644
index 0000000..1970fb7
--- /dev/null
+++ b/services/tuner/TunerDescrambler.h
@@ -0,0 +1,54 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TUNERDESCRAMBLER_H
+#define ANDROID_MEDIA_TUNERDESCRAMBLER_H
+
+#include <aidl/android/media/tv/tuner/BnTunerDescrambler.h>
+#include <android/hardware/tv/tuner/1.0/ITuner.h>
+
+using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::media::tv::tuner::BnTunerDescrambler;
+using ::aidl::android::media::tv::tuner::ITunerDemux;
+using ::aidl::android::media::tv::tuner::ITunerFilter;
+using ::aidl::android::media::tv::tuner::TunerDemuxPid;
+using ::android::hardware::tv::tuner::V1_0::DemuxPid;
+using ::android::hardware::tv::tuner::V1_0::IDescrambler;
+
+namespace android {
+
+class TunerDescrambler : public BnTunerDescrambler {
+
+public:
+ TunerDescrambler(sp<IDescrambler> descrambler);
+ virtual ~TunerDescrambler();
+ Status setDemuxSource(const shared_ptr<ITunerDemux>& demux) override;
+ Status setKeyToken(const vector<uint8_t>& keyToken) override;
+ Status addPid(const TunerDemuxPid& pid,
+ const shared_ptr<ITunerFilter>& optionalSourceFilter) override;
+ Status removePid(const TunerDemuxPid& pid,
+ const shared_ptr<ITunerFilter>& optionalSourceFilter) override;
+ Status close() override;
+
+private:
+ DemuxPid getHidlDemuxPid(const TunerDemuxPid& pid);
+
+ sp<IDescrambler> mDescrambler;
+};
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_TUNERDESCRAMBLER_H
diff --git a/services/tuner/TunerDvr.cpp b/services/tuner/TunerDvr.cpp
new file mode 100644
index 0000000..db4e07b
--- /dev/null
+++ b/services/tuner/TunerDvr.cpp
@@ -0,0 +1,209 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TunerDvr"
+
+#include <fmq/ConvertMQDescriptors.h>
+#include "TunerDvr.h"
+#include "TunerFilter.h"
+
+using ::android::hardware::tv::tuner::V1_0::DataFormat;
+using ::android::hardware::tv::tuner::V1_0::Result;
+
+namespace android {
+
+TunerDvr::TunerDvr(sp<IDvr> dvr, int type) {
+ mDvr = dvr;
+ mType = static_cast<DvrType>(type);
+}
+
+TunerDvr::~TunerDvr() {
+ mDvr = NULL;
+}
+
+Status TunerDvr::getQueueDesc(AidlMQDesc* _aidl_return) {
+ if (mDvr == NULL) {
+ ALOGE("IDvr is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ MQDesc dvrMQDesc;
+ Result res;
+ mDvr->getQueueDesc([&](Result r, const MQDesc& desc) {
+ dvrMQDesc = desc;
+ res = r;
+ });
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+
+ AidlMQDesc aidlMQDesc;
+ unsafeHidlToAidlMQDescriptor<uint8_t, int8_t, SynchronizedReadWrite>(
+ dvrMQDesc, &aidlMQDesc);
+ *_aidl_return = move(aidlMQDesc);
+ return Status::ok();
+}
+
+Status TunerDvr::configure(const TunerDvrSettings& settings) {
+ if (mDvr == NULL) {
+ ALOGE("IDvr is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res = mDvr->configure(getHidlDvrSettingsFromAidl(settings));
+ if (res != Result::SUCCESS) {
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+Status TunerDvr::attachFilter(const shared_ptr<ITunerFilter>& filter) {
+ if (mDvr == NULL) {
+ ALOGE("IDvr is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ ITunerFilter* tunerFilter = filter.get();
+ sp<IFilter> hidlFilter = static_cast<TunerFilter*>(tunerFilter)->getHalFilter();
+ if (hidlFilter == NULL) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_ARGUMENT));
+ }
+
+ Result res = mDvr->attachFilter(hidlFilter);
+ if (res != Result::SUCCESS) {
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+Status TunerDvr::detachFilter(const shared_ptr<ITunerFilter>& filter) {
+ if (mDvr == NULL) {
+ ALOGE("IDvr is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ ITunerFilter* tunerFilter = filter.get();
+ sp<IFilter> hidlFilter = static_cast<TunerFilter*>(tunerFilter)->getHalFilter();
+ if (hidlFilter == NULL) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_ARGUMENT));
+ }
+
+ Result res = mDvr->detachFilter(hidlFilter);
+ if (res != Result::SUCCESS) {
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+Status TunerDvr::start() {
+ if (mDvr == NULL) {
+ ALOGE("IDvr is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res = mDvr->start();
+ if (res != Result::SUCCESS) {
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+Status TunerDvr::stop() {
+ if (mDvr == NULL) {
+ ALOGE("IDvr is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res = mDvr->stop();
+ if (res != Result::SUCCESS) {
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+Status TunerDvr::flush() {
+ if (mDvr == NULL) {
+ ALOGE("IDvr is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res = mDvr->flush();
+ if (res != Result::SUCCESS) {
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+Status TunerDvr::close() {
+ if (mDvr == NULL) {
+ ALOGE("IDvr is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res = mDvr->close();
+ mDvr = NULL;
+
+ if (res != Result::SUCCESS) {
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+DvrSettings TunerDvr::getHidlDvrSettingsFromAidl(TunerDvrSettings settings) {
+ DvrSettings s;
+ switch (mType) {
+ case DvrType::PLAYBACK: {
+ s.playback({
+ .statusMask = static_cast<uint8_t>(settings.statusMask),
+ .lowThreshold = static_cast<uint32_t>(settings.lowThreshold),
+ .highThreshold = static_cast<uint32_t>(settings.highThreshold),
+ .dataFormat = static_cast<DataFormat>(settings.dataFormat),
+ .packetSize = static_cast<uint8_t>(settings.packetSize),
+ });
+ return s;
+ }
+ case DvrType::RECORD: {
+ s.record({
+ .statusMask = static_cast<uint8_t>(settings.statusMask),
+ .lowThreshold = static_cast<uint32_t>(settings.lowThreshold),
+ .highThreshold = static_cast<uint32_t>(settings.highThreshold),
+ .dataFormat = static_cast<DataFormat>(settings.dataFormat),
+ .packetSize = static_cast<uint8_t>(settings.packetSize),
+ });
+ return s;
+ }
+ default:
+ break;
+ }
+ return s;
+}
+
+/////////////// IDvrCallback ///////////////////////
+
+Return<void> TunerDvr::DvrCallback::onRecordStatus(const RecordStatus status) {
+ if (mTunerDvrCallback != NULL) {
+ mTunerDvrCallback->onRecordStatus(static_cast<int>(status));
+ }
+ return Void();
+}
+
+Return<void> TunerDvr::DvrCallback::onPlaybackStatus(const PlaybackStatus status) {
+ if (mTunerDvrCallback != NULL) {
+ mTunerDvrCallback->onPlaybackStatus(static_cast<int>(status));
+ }
+ return Void();
+}
+} // namespace android
diff --git a/services/tuner/TunerDvr.h b/services/tuner/TunerDvr.h
new file mode 100644
index 0000000..a508e99
--- /dev/null
+++ b/services/tuner/TunerDvr.h
@@ -0,0 +1,97 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TUNERDVR_H
+#define ANDROID_MEDIA_TUNERDVR_H
+
+#include <aidl/android/media/tv/tuner/BnTunerDvr.h>
+#include <aidl/android/media/tv/tuner/ITunerDvrCallback.h>
+#include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include <fmq/MessageQueue.h>
+
+#include <TunerFilter.h>
+
+using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::hardware::common::fmq::GrantorDescriptor;
+using ::aidl::android::hardware::common::fmq::MQDescriptor;
+using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
+using ::aidl::android::media::tv::tuner::BnTunerDvr;
+using ::aidl::android::media::tv::tuner::ITunerDvrCallback;
+using ::aidl::android::media::tv::tuner::ITunerFilter;
+using ::aidl::android::media::tv::tuner::TunerDvrSettings;
+
+using ::android::hardware::MQDescriptorSync;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+using ::android::hardware::tv::tuner::V1_0::DvrSettings;
+using ::android::hardware::tv::tuner::V1_0::DvrType;
+using ::android::hardware::tv::tuner::V1_0::IDvr;
+using ::android::hardware::tv::tuner::V1_0::IDvrCallback;
+using ::android::hardware::tv::tuner::V1_0::PlaybackStatus;
+using ::android::hardware::tv::tuner::V1_0::RecordStatus;
+
+using namespace std;
+
+namespace android {
+
+using MQDesc = MQDescriptorSync<uint8_t>;
+using AidlMQDesc = MQDescriptor<int8_t, SynchronizedReadWrite>;
+
+class TunerDvr : public BnTunerDvr {
+
+public:
+ TunerDvr(sp<IDvr> dvr, int type);
+ ~TunerDvr();
+
+ Status getQueueDesc(AidlMQDesc* _aidl_return) override;
+
+ Status configure(const TunerDvrSettings& settings) override;
+
+ Status attachFilter(const shared_ptr<ITunerFilter>& filter) override;
+
+ Status detachFilter(const shared_ptr<ITunerFilter>& filter) override;
+
+ Status start() override;
+
+ Status stop() override;
+
+ Status flush() override;
+
+ Status close() override;
+
+ struct DvrCallback : public IDvrCallback {
+ DvrCallback(const shared_ptr<ITunerDvrCallback> tunerDvrCallback)
+ : mTunerDvrCallback(tunerDvrCallback) {};
+
+ virtual Return<void> onRecordStatus(const RecordStatus status);
+ virtual Return<void> onPlaybackStatus(const PlaybackStatus status);
+
+ private:
+ shared_ptr<ITunerDvrCallback> mTunerDvrCallback;
+ };
+
+private:
+ DvrSettings getHidlDvrSettingsFromAidl(TunerDvrSettings settings);
+
+ sp<IDvr> mDvr;
+ DvrType mType;
+};
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_TUNERDVR_H
diff --git a/services/tuner/TunerFilter.cpp b/services/tuner/TunerFilter.cpp
new file mode 100644
index 0000000..039fd31
--- /dev/null
+++ b/services/tuner/TunerFilter.cpp
@@ -0,0 +1,910 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TunerFilter"
+
+#include "TunerFilter.h"
+
+using ::aidl::android::media::tv::tuner::TunerFilterSectionCondition;
+
+using ::android::hardware::hidl_handle;
+using ::android::hardware::tv::tuner::V1_0::DemuxAlpLengthType;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
+using ::android::hardware::tv::tuner::V1_0::DemuxIpAddress;
+using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
+using ::android::hardware::tv::tuner::V1_0::DemuxMmtpPid;
+using ::android::hardware::tv::tuner::V1_0::DemuxRecordScIndexType;
+using ::android::hardware::tv::tuner::V1_0::DemuxStreamId;
+using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
+using ::android::hardware::tv::tuner::V1_0::Result;
+using ::android::hardware::tv::tuner::V1_1::AudioStreamType;
+using ::android::hardware::tv::tuner::V1_1::Constant;
+using ::android::hardware::tv::tuner::V1_1::VideoStreamType;
+
+namespace android {
+
+using namespace std;
+
+TunerFilter::TunerFilter(
+ sp<IFilter> filter, int mainType, int subType) {
+ mFilter = filter;
+ mFilter_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(filter);
+ mMainType = mainType;
+ mSubType = subType;
+}
+
+TunerFilter::~TunerFilter() {
+ mFilter = nullptr;
+ mFilter_1_1 = nullptr;
+}
+
+Status TunerFilter::getQueueDesc(AidlMQDesc* _aidl_return) {
+ if (mFilter == NULL) {
+ ALOGE("IFilter is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ MQDesc filterMQDesc;
+ Result res;
+ mFilter->getQueueDesc([&](Result r, const MQDesc& desc) {
+ filterMQDesc = desc;
+ res = r;
+ });
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+
+ AidlMQDesc aidlMQDesc;
+ unsafeHidlToAidlMQDescriptor<uint8_t, int8_t, SynchronizedReadWrite>(
+ filterMQDesc, &aidlMQDesc);
+ *_aidl_return = move(aidlMQDesc);
+ return Status::ok();
+}
+
+Status TunerFilter::getId(int32_t* _aidl_return) {
+ if (mFilter == nullptr) {
+ ALOGE("IFilter is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res;
+ mFilter->getId([&](Result r, uint32_t filterId) {
+ res = r;
+ mId = filterId;
+ });
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ *_aidl_return = mId;
+ return Status::ok();
+}
+
+Status TunerFilter::getId64Bit(int64_t* _aidl_return) {
+ if (mFilter_1_1 == nullptr) {
+ ALOGE("IFilter_1_1 is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res;
+ mFilter_1_1->getId64Bit([&](Result r, uint64_t filterId) {
+ res = r;
+ mId64Bit = filterId;
+ });
+ if (res != Result::SUCCESS) {
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ *_aidl_return = mId64Bit;
+ return Status::ok();
+}
+
+Status TunerFilter::configure(const TunerFilterConfiguration& config) {
+ if (mFilter == nullptr) {
+ ALOGE("IFilter is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ DemuxFilterSettings settings;
+ switch (config.getTag()) {
+ case TunerFilterConfiguration::ts: {
+ getHidlTsSettings(config, settings);
+ break;
+ }
+ case TunerFilterConfiguration::mmtp: {
+ getHidlMmtpSettings(config, settings);
+ break;
+ }
+ case TunerFilterConfiguration::ip: {
+ getHidlIpSettings(config, settings);
+ break;
+ }
+ case TunerFilterConfiguration::tlv: {
+ getHidlTlvSettings(config, settings);
+ break;
+ }
+ case TunerFilterConfiguration::alp: {
+ getHidlAlpSettings(config, settings);
+ break;
+ }
+ }
+
+ Result res = mFilter->configure(settings);
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+Status TunerFilter::configureMonitorEvent(int monitorEventType) {
+ if (mFilter_1_1 == nullptr) {
+ ALOGE("IFilter_1_1 is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res = mFilter_1_1->configureMonitorEvent(monitorEventType);
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+Status TunerFilter::configureIpFilterContextId(int cid) {
+ if (mFilter_1_1 == nullptr) {
+ ALOGE("IFilter_1_1 is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res = mFilter_1_1->configureIpCid(cid);
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+Status TunerFilter::configureAvStreamType(int avStreamType) {
+ if (mFilter_1_1 == nullptr) {
+ ALOGE("IFilter_1_1 is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ AvStreamType type;
+ if (!getHidlAvStreamType(avStreamType, type)) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_STATE));
+ }
+
+ Result res = mFilter_1_1->configureAvStreamType(type);
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+Status TunerFilter::setDataSource(const shared_ptr<ITunerFilter>& filter) {
+ if (mFilter == nullptr) {
+ ALOGE("IFilter is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ ITunerFilter* tunerFilter = filter.get();
+ sp<IFilter> hidlFilter = static_cast<TunerFilter*>(tunerFilter)->getHalFilter();
+ Result res = mFilter->setDataSource(hidlFilter);
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+void TunerFilter::getHidlTsSettings(
+ const TunerFilterConfiguration& config, DemuxFilterSettings& settings) {
+ auto tsConf = config.get<TunerFilterConfiguration::ts>();
+ DemuxTsFilterSettings ts{
+ .tpid = static_cast<uint16_t>(tsConf.tpid),
+ };
+
+ TunerFilterSettings tunerSettings = tsConf.filterSettings;
+ switch (tunerSettings.getTag()) {
+ case TunerFilterSettings::av: {
+ ts.filterSettings.av(getAvSettings(tunerSettings));
+ break;
+ }
+ case TunerFilterSettings::section: {
+ ts.filterSettings.section(getSectionSettings(tunerSettings));
+ break;
+ }
+ case TunerFilterSettings::pesData: {
+ ts.filterSettings.pesData(getPesDataSettings(tunerSettings));
+ break;
+ }
+ case TunerFilterSettings::record: {
+ ts.filterSettings.record(getRecordSettings(tunerSettings));
+ break;
+ }
+ default: {
+ ts.filterSettings.noinit();
+ break;
+ }
+ }
+ settings.ts(ts);
+}
+
+void TunerFilter::getHidlMmtpSettings(
+ const TunerFilterConfiguration& config, DemuxFilterSettings& settings) {
+ auto mmtpConf = config.get<TunerFilterConfiguration::mmtp>();
+ DemuxMmtpFilterSettings mmtp{
+ .mmtpPid = static_cast<DemuxMmtpPid>(mmtpConf.mmtpPid),
+ };
+
+ TunerFilterSettings tunerSettings = mmtpConf.filterSettings;
+ switch (tunerSettings.getTag()) {
+ case TunerFilterSettings::av: {
+ mmtp.filterSettings.av(getAvSettings(tunerSettings));
+ break;
+ }
+ case TunerFilterSettings::section: {
+ mmtp.filterSettings.section(getSectionSettings(tunerSettings));
+ break;
+ }
+ case TunerFilterSettings::pesData: {
+ mmtp.filterSettings.pesData(getPesDataSettings(tunerSettings));
+ break;
+ }
+ case TunerFilterSettings::record: {
+ mmtp.filterSettings.record(getRecordSettings(tunerSettings));
+ break;
+ }
+ case TunerFilterSettings::download: {
+ mmtp.filterSettings.download(getDownloadSettings(tunerSettings));
+ break;
+ }
+ default: {
+ mmtp.filterSettings.noinit();
+ break;
+ }
+ }
+ settings.mmtp(mmtp);
+}
+
+void TunerFilter::getHidlIpSettings(
+ const TunerFilterConfiguration& config, DemuxFilterSettings& settings) {
+ auto ipConf = config.get<TunerFilterConfiguration::ip>();
+ DemuxIpAddress ipAddr{
+ .srcPort = static_cast<uint16_t>(ipConf.ipAddr.srcPort),
+ .dstPort = static_cast<uint16_t>(ipConf.ipAddr.dstPort),
+ };
+
+ ipConf.ipAddr.srcIpAddress.isIpV6
+ ? ipAddr.srcIpAddress.v6(getIpV6Address(ipConf.ipAddr.srcIpAddress))
+ : ipAddr.srcIpAddress.v4(getIpV4Address(ipConf.ipAddr.srcIpAddress));
+ ipConf.ipAddr.dstIpAddress.isIpV6
+ ? ipAddr.dstIpAddress.v6(getIpV6Address(ipConf.ipAddr.dstIpAddress))
+ : ipAddr.dstIpAddress.v4(getIpV4Address(ipConf.ipAddr.dstIpAddress));
+ DemuxIpFilterSettings ip{
+ .ipAddr = ipAddr,
+ };
+
+ TunerFilterSettings tunerSettings = ipConf.filterSettings;
+ switch (tunerSettings.getTag()) {
+ case TunerFilterSettings::section: {
+ ip.filterSettings.section(getSectionSettings(tunerSettings));
+ break;
+ }
+ case TunerFilterSettings::isPassthrough: {
+ ip.filterSettings.bPassthrough(tunerSettings.isPassthrough);
+ break;
+ }
+ default: {
+ ip.filterSettings.noinit();
+ break;
+ }
+ }
+ settings.ip(ip);
+}
+
+hidl_array<uint8_t, IP_V6_LENGTH> TunerFilter::getIpV6Address(TunerDemuxIpAddress addr) {
+ hidl_array<uint8_t, IP_V6_LENGTH> ip;
+ if (addr.addr.size() != IP_V6_LENGTH) {
+ return ip;
+ }
+ copy(addr.addr.begin(), addr.addr.end(), ip.data());
+ return ip;
+}
+
+hidl_array<uint8_t, IP_V4_LENGTH> TunerFilter::getIpV4Address(TunerDemuxIpAddress addr) {
+ hidl_array<uint8_t, IP_V4_LENGTH> ip;
+ if (addr.addr.size() != IP_V4_LENGTH) {
+ return ip;
+ }
+ copy(addr.addr.begin(), addr.addr.end(), ip.data());
+ return ip;
+}
+
+void TunerFilter::getHidlTlvSettings(
+ const TunerFilterConfiguration& config, DemuxFilterSettings& settings) {
+ auto tlvConf = config.get<TunerFilterConfiguration::tlv>();
+ DemuxTlvFilterSettings tlv{
+ .packetType = static_cast<uint8_t>(tlvConf.packetType),
+ .isCompressedIpPacket = tlvConf.isCompressedIpPacket,
+ };
+
+ TunerFilterSettings tunerSettings = tlvConf.filterSettings;
+ switch (tunerSettings.getTag()) {
+ case TunerFilterSettings::section: {
+ tlv.filterSettings.section(getSectionSettings(tunerSettings));
+ break;
+ }
+ case TunerFilterSettings::isPassthrough: {
+ tlv.filterSettings.bPassthrough(tunerSettings.isPassthrough);
+ break;
+ }
+ default: {
+ tlv.filterSettings.noinit();
+ break;
+ }
+ }
+ settings.tlv(tlv);
+}
+
+void TunerFilter::getHidlAlpSettings(
+ const TunerFilterConfiguration& config, DemuxFilterSettings& settings) {
+ auto alpConf = config.get<TunerFilterConfiguration::alp>();
+ DemuxAlpFilterSettings alp{
+ .packetType = static_cast<uint8_t>(alpConf.packetType),
+ .lengthType = static_cast<DemuxAlpLengthType>(alpConf.lengthType),
+ };
+
+ TunerFilterSettings tunerSettings = alpConf.filterSettings;
+ switch (tunerSettings.getTag()) {
+ case TunerFilterSettings::section: {
+ alp.filterSettings.section(getSectionSettings(tunerSettings));
+ break;
+ }
+ default: {
+ alp.filterSettings.noinit();
+ break;
+ }
+ }
+ settings.alp(alp);
+}
+
+DemuxFilterAvSettings TunerFilter::getAvSettings(const TunerFilterSettings& settings) {
+ DemuxFilterAvSettings av {
+ .isPassthrough = settings.get<TunerFilterSettings::av>().isPassthrough,
+ };
+ return av;
+}
+
+DemuxFilterSectionSettings TunerFilter::getSectionSettings(const TunerFilterSettings& settings) {
+ auto s = settings.get<TunerFilterSettings::section>();
+ DemuxFilterSectionSettings section{
+ .isCheckCrc = s.isCheckCrc,
+ .isRepeat = s.isRepeat,
+ .isRaw = s.isRaw,
+ };
+
+ switch (s.condition.getTag()) {
+ case TunerFilterSectionCondition::sectionBits: {
+ auto sectionBits = s.condition.get<TunerFilterSectionCondition::sectionBits>();
+ vector<uint8_t> filter(sectionBits.filter.begin(), sectionBits.filter.end());
+ vector<uint8_t> mask(sectionBits.mask.begin(), sectionBits.mask.end());
+ vector<uint8_t> mode(sectionBits.mode.begin(), sectionBits.mode.end());
+ section.condition.sectionBits({
+ .filter = filter,
+ .mask = mask,
+ .mode = mode,
+ });
+ break;
+ }
+ case TunerFilterSectionCondition::tableInfo: {
+ auto tableInfo = s.condition.get<TunerFilterSectionCondition::tableInfo>();
+ section.condition.tableInfo({
+ .tableId = static_cast<uint16_t>(tableInfo.tableId),
+ .version = static_cast<uint16_t>(tableInfo.version),
+ });
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ return section;
+}
+
+DemuxFilterPesDataSettings TunerFilter::getPesDataSettings(const TunerFilterSettings& settings) {
+ DemuxFilterPesDataSettings pes{
+ .streamId = static_cast<DemuxStreamId>(
+ settings.get<TunerFilterSettings::pesData>().streamId),
+ .isRaw = settings.get<TunerFilterSettings::pesData>().isRaw,
+ };
+ return pes;
+}
+
+DemuxFilterRecordSettings TunerFilter::getRecordSettings(const TunerFilterSettings& settings) {
+ auto r = settings.get<TunerFilterSettings::record>();
+ DemuxFilterRecordSettings record{
+ .tsIndexMask = static_cast<uint32_t>(r.tsIndexMask),
+ .scIndexType = static_cast<DemuxRecordScIndexType>(r.scIndexType),
+ };
+
+ switch (r.scIndexMask.getTag()) {
+ case TunerFilterScIndexMask::sc: {
+ record.scIndexMask.sc(static_cast<uint32_t>(
+ r.scIndexMask.get<TunerFilterScIndexMask::sc>()));
+ break;
+ }
+ case TunerFilterScIndexMask::scHevc: {
+ record.scIndexMask.scHevc(static_cast<uint32_t>(
+ r.scIndexMask.get<TunerFilterScIndexMask::scHevc>()));
+ break;
+ }
+ }
+ return record;
+}
+
+DemuxFilterDownloadSettings TunerFilter::getDownloadSettings(const TunerFilterSettings& settings) {
+ DemuxFilterDownloadSettings download {
+ .downloadId = static_cast<uint32_t>(
+ settings.get<TunerFilterSettings::download>().downloadId),
+ };
+ return download;
+}
+
+Status TunerFilter::getAvSharedHandleInfo(TunerFilterSharedHandleInfo* _aidl_return) {
+ if (mFilter_1_1 == nullptr) {
+ ALOGE("IFilter_1_1 is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res;
+ mFilter_1_1->getAvSharedHandle([&](Result r, hidl_handle avMemory, uint64_t avMemSize) {
+ res = r;
+ if (res == Result::SUCCESS) {
+ TunerFilterSharedHandleInfo info{
+ .handle = dupToAidl(avMemory),
+ .size = static_cast<int64_t>(avMemSize),
+ };
+ *_aidl_return = move(info);
+ } else {
+ _aidl_return = NULL;
+ }
+ });
+
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+Status TunerFilter::releaseAvHandle(
+ const ::aidl::android::hardware::common::NativeHandle& handle, int64_t avDataId) {
+ if (mFilter == nullptr) {
+ ALOGE("IFilter is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res = mFilter->releaseAvHandle(hidl_handle(makeFromAidl(handle)), avDataId);
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+Status TunerFilter::start() {
+ if (mFilter == nullptr) {
+ ALOGE("IFilter is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+ Result res = mFilter->start();
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+Status TunerFilter::stop() {
+ if (mFilter == nullptr) {
+ ALOGE("IFilter is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+ Result res = mFilter->stop();
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+Status TunerFilter::flush() {
+ if (mFilter == nullptr) {
+ ALOGE("IFilter is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+ Result res = mFilter->flush();
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+Status TunerFilter::close() {
+ if (mFilter == nullptr) {
+ ALOGE("IFilter is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+ Result res = mFilter->close();
+ mFilter = NULL;
+ mFilter_1_1 = NULL;
+
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+sp<IFilter> TunerFilter::getHalFilter() {
+ return mFilter;
+}
+
+bool TunerFilter::isAudioFilter() {
+ return (mMainType == (int)DemuxFilterMainType::TS
+ && mSubType == (int)DemuxTsFilterType::AUDIO)
+ || (mMainType == (int)DemuxFilterMainType::MMTP
+ && mSubType == (int)DemuxMmtpFilterType::AUDIO);
+}
+
+bool TunerFilter::isVideoFilter() {
+ return (mMainType == (int)DemuxFilterMainType::TS
+ && mSubType == (int)DemuxTsFilterType::VIDEO)
+ || (mMainType == (int)DemuxFilterMainType::MMTP
+ && mSubType == (int)DemuxMmtpFilterType::VIDEO);
+}
+
+bool TunerFilter::getHidlAvStreamType(int avStreamType, AvStreamType& type) {
+ if (isAudioFilter()) {
+ type.audio(static_cast<AudioStreamType>(avStreamType));
+ return true;
+ }
+
+ if (isVideoFilter()) {
+ type.video(static_cast<VideoStreamType>(avStreamType));
+ return true;
+ }
+
+ return false;
+}
+
+/////////////// FilterCallback ///////////////////////
+
+Return<void> TunerFilter::FilterCallback::onFilterStatus(DemuxFilterStatus status) {
+ if (mTunerFilterCallback != NULL) {
+ mTunerFilterCallback->onFilterStatus((int)status);
+ }
+ return Void();
+}
+
+Return<void> TunerFilter::FilterCallback::onFilterEvent(const DemuxFilterEvent& filterEvent) {
+ vector<DemuxFilterEventExt::Event> emptyEventsExt;
+ DemuxFilterEventExt emptyFilterEventExt {
+ .events = emptyEventsExt,
+ };
+ onFilterEvent_1_1(filterEvent, emptyFilterEventExt);
+ return Void();
+}
+
+Return<void> TunerFilter::FilterCallback::onFilterEvent_1_1(const DemuxFilterEvent& filterEvent,
+ const DemuxFilterEventExt& filterEventExt) {
+ if (mTunerFilterCallback != NULL) {
+ vector<DemuxFilterEvent::Event> events = filterEvent.events;
+ vector<DemuxFilterEventExt::Event> eventsExt = filterEventExt.events;
+ vector<TunerFilterEvent> tunerEvent;
+
+ getAidlFilterEvent(events, eventsExt, tunerEvent);
+ mTunerFilterCallback->onFilterEvent(tunerEvent);
+ }
+ return Void();
+}
+
+/////////////// FilterCallback Helper Methods ///////////////////////
+
+void TunerFilter::FilterCallback::getAidlFilterEvent(vector<DemuxFilterEvent::Event>& events,
+ vector<DemuxFilterEventExt::Event>& eventsExt,
+ vector<TunerFilterEvent>& tunerEvent) {
+ if (events.empty() && !eventsExt.empty()) {
+ auto eventExt = eventsExt[0];
+ switch (eventExt.getDiscriminator()) {
+ case DemuxFilterEventExt::Event::hidl_discriminator::monitorEvent: {
+ getMonitorEvent(eventsExt, tunerEvent);
+ return;
+ }
+ case DemuxFilterEventExt::Event::hidl_discriminator::startId: {
+ getRestartEvent(eventsExt, tunerEvent);
+ return;
+ }
+ default: {
+ break;
+ }
+ }
+ return;
+ }
+
+ if (!events.empty()) {
+ auto event = events[0];
+ switch (event.getDiscriminator()) {
+ case DemuxFilterEvent::Event::hidl_discriminator::media: {
+ getMediaEvent(events, tunerEvent);
+ break;
+ }
+ case DemuxFilterEvent::Event::hidl_discriminator::section: {
+ getSectionEvent(events, tunerEvent);
+ break;
+ }
+ case DemuxFilterEvent::Event::hidl_discriminator::pes: {
+ getPesEvent(events, tunerEvent);
+ break;
+ }
+ case DemuxFilterEvent::Event::hidl_discriminator::tsRecord: {
+ getTsRecordEvent(events, eventsExt, tunerEvent);
+ break;
+ }
+ case DemuxFilterEvent::Event::hidl_discriminator::mmtpRecord: {
+ getMmtpRecordEvent(events, eventsExt, tunerEvent);
+ break;
+ }
+ case DemuxFilterEvent::Event::hidl_discriminator::download: {
+ getDownloadEvent(events, tunerEvent);
+ break;
+ }
+ case DemuxFilterEvent::Event::hidl_discriminator::ipPayload: {
+ getIpPayloadEvent(events, tunerEvent);
+ break;
+ }
+ case DemuxFilterEvent::Event::hidl_discriminator::temi: {
+ getTemiEvent(events, tunerEvent);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ }
+}
+
+void TunerFilter::FilterCallback::getMediaEvent(
+ vector<DemuxFilterEvent::Event>& events, vector<TunerFilterEvent>& res) {
+ for (DemuxFilterEvent::Event e : events) {
+ DemuxFilterMediaEvent mediaEvent = e.media();
+ TunerFilterMediaEvent tunerMedia;
+
+ tunerMedia.streamId = static_cast<char16_t>(mediaEvent.streamId);
+ tunerMedia.isPtsPresent = mediaEvent.isPtsPresent;
+ tunerMedia.pts = static_cast<long>(mediaEvent.pts);
+ tunerMedia.dataLength = static_cast<int>(mediaEvent.dataLength);
+ tunerMedia.offset = static_cast<int>(mediaEvent.offset);
+ tunerMedia.isSecureMemory = mediaEvent.isSecureMemory;
+ tunerMedia.avDataId = static_cast<long>(mediaEvent.avDataId);
+ tunerMedia.mpuSequenceNumber = static_cast<int>(mediaEvent.mpuSequenceNumber);
+ tunerMedia.isPesPrivateData = mediaEvent.isPesPrivateData;
+
+ if (mediaEvent.extraMetaData.getDiscriminator() ==
+ DemuxFilterMediaEvent::ExtraMetaData::hidl_discriminator::audio) {
+ tunerMedia.isAudioExtraMetaData = true;
+ tunerMedia.audio = {
+ .adFade = static_cast<int8_t>(
+ mediaEvent.extraMetaData.audio().adFade),
+ .adPan = static_cast<int8_t>(
+ mediaEvent.extraMetaData.audio().adPan),
+ .versionTextTag = static_cast<int8_t>(
+ mediaEvent.extraMetaData.audio().versionTextTag),
+ .adGainCenter = static_cast<int8_t>(
+ mediaEvent.extraMetaData.audio().adGainCenter),
+ .adGainFront = static_cast<int8_t>(
+ mediaEvent.extraMetaData.audio().adGainFront),
+ .adGainSurround = static_cast<int8_t>(
+ mediaEvent.extraMetaData.audio().adGainSurround),
+ };
+ } else {
+ tunerMedia.isAudioExtraMetaData = false;
+ }
+
+ if (mediaEvent.avMemory.getNativeHandle() != nullptr) {
+ tunerMedia.avMemory = dupToAidl(mediaEvent.avMemory.getNativeHandle());
+ }
+
+ TunerFilterEvent tunerEvent;
+ tunerEvent.set<TunerFilterEvent::media>(move(tunerMedia));
+ res.push_back(move(tunerEvent));
+ }
+}
+
+void TunerFilter::FilterCallback::getSectionEvent(
+ vector<DemuxFilterEvent::Event>& events, vector<TunerFilterEvent>& res) {
+ for (DemuxFilterEvent::Event e : events) {
+ DemuxFilterSectionEvent sectionEvent = e.section();
+ TunerFilterSectionEvent tunerSection;
+
+ tunerSection.tableId = static_cast<char16_t>(sectionEvent.tableId);
+ tunerSection.version = static_cast<char16_t>(sectionEvent.version);
+ tunerSection.sectionNum = static_cast<char16_t>(sectionEvent.sectionNum);
+ tunerSection.dataLength = static_cast<char16_t>(sectionEvent.dataLength);
+
+ TunerFilterEvent tunerEvent;
+ tunerEvent.set<TunerFilterEvent::section>(move(tunerSection));
+ res.push_back(move(tunerEvent));
+ }
+}
+
+void TunerFilter::FilterCallback::getPesEvent(
+ vector<DemuxFilterEvent::Event>& events, vector<TunerFilterEvent>& res) {
+ for (DemuxFilterEvent::Event e : events) {
+ DemuxFilterPesEvent pesEvent = e.pes();
+ TunerFilterPesEvent tunerPes;
+
+ tunerPes.streamId = static_cast<char16_t>(pesEvent.streamId);
+ tunerPes.dataLength = static_cast<char16_t>(pesEvent.dataLength);
+ tunerPes.mpuSequenceNumber = static_cast<int>(pesEvent.mpuSequenceNumber);
+
+ TunerFilterEvent tunerEvent;
+ tunerEvent.set<TunerFilterEvent::pes>(move(tunerPes));
+ res.push_back(move(tunerEvent));
+ }
+}
+
+void TunerFilter::FilterCallback::getTsRecordEvent(vector<DemuxFilterEvent::Event>& events,
+ vector<DemuxFilterEventExt::Event>& eventsExt, vector<TunerFilterEvent>& res) {
+ for (int i = 0; i < events.size(); i++) {
+ TunerFilterTsRecordEvent tunerTsRecord;
+ DemuxFilterTsRecordEvent tsRecordEvent = events[i].tsRecord();
+
+ TunerFilterScIndexMask scIndexMask;
+ if (tsRecordEvent.scIndexMask.getDiscriminator()
+ == DemuxFilterTsRecordEvent::ScIndexMask::hidl_discriminator::sc) {
+ scIndexMask.set<TunerFilterScIndexMask::sc>(
+ static_cast<int>(tsRecordEvent.scIndexMask.sc()));
+ } else if (tsRecordEvent.scIndexMask.getDiscriminator()
+ == DemuxFilterTsRecordEvent::ScIndexMask::hidl_discriminator::scHevc) {
+ scIndexMask.set<TunerFilterScIndexMask::scHevc>(
+ static_cast<int>(tsRecordEvent.scIndexMask.scHevc()));
+ }
+
+ if (tsRecordEvent.pid.getDiscriminator() == DemuxPid::hidl_discriminator::tPid) {
+ tunerTsRecord.pid = static_cast<char16_t>(tsRecordEvent.pid.tPid());
+ } else {
+ tunerTsRecord.pid = static_cast<char16_t>(Constant::INVALID_TS_PID);
+ }
+
+ tunerTsRecord.scIndexMask = scIndexMask;
+ tunerTsRecord.tsIndexMask = static_cast<int>(tsRecordEvent.tsIndexMask);
+ tunerTsRecord.byteNumber = static_cast<long>(tsRecordEvent.byteNumber);
+
+ if (eventsExt.size() > i && eventsExt[i].getDiscriminator() ==
+ DemuxFilterEventExt::Event::hidl_discriminator::tsRecord) {
+ tunerTsRecord.isExtended = true;
+ tunerTsRecord.pts = static_cast<long>(eventsExt[i].tsRecord().pts);
+ tunerTsRecord.firstMbInSlice = static_cast<int>(eventsExt[i].tsRecord().firstMbInSlice);
+ } else {
+ tunerTsRecord.isExtended = false;
+ }
+
+ TunerFilterEvent tunerEvent;
+ tunerEvent.set<TunerFilterEvent::tsRecord>(move(tunerTsRecord));
+ res.push_back(move(tunerEvent));
+ }
+}
+
+void TunerFilter::FilterCallback::getMmtpRecordEvent(vector<DemuxFilterEvent::Event>& events,
+ vector<DemuxFilterEventExt::Event>& eventsExt, vector<TunerFilterEvent>& res) {
+ for (int i = 0; i < events.size(); i++) {
+ TunerFilterMmtpRecordEvent tunerMmtpRecord;
+ DemuxFilterMmtpRecordEvent mmtpRecordEvent = events[i].mmtpRecord();
+
+ tunerMmtpRecord.scHevcIndexMask = static_cast<int>(mmtpRecordEvent.scHevcIndexMask);
+ tunerMmtpRecord.byteNumber = static_cast<long>(mmtpRecordEvent.byteNumber);
+
+ if (eventsExt.size() > i && eventsExt[i].getDiscriminator() ==
+ DemuxFilterEventExt::Event::hidl_discriminator::mmtpRecord) {
+ tunerMmtpRecord.isExtended = true;
+ tunerMmtpRecord.pts = static_cast<long>(eventsExt[i].mmtpRecord().pts);
+ tunerMmtpRecord.mpuSequenceNumber =
+ static_cast<int>(eventsExt[i].mmtpRecord().mpuSequenceNumber);
+ tunerMmtpRecord.firstMbInSlice =
+ static_cast<int>(eventsExt[i].mmtpRecord().firstMbInSlice);
+ tunerMmtpRecord.tsIndexMask = static_cast<int>(eventsExt[i].mmtpRecord().tsIndexMask);
+ } else {
+ tunerMmtpRecord.isExtended = false;
+ }
+
+ TunerFilterEvent tunerEvent;
+ tunerEvent.set<TunerFilterEvent::mmtpRecord>(move(tunerMmtpRecord));
+ res.push_back(move(tunerEvent));
+ }
+}
+
+void TunerFilter::FilterCallback::getDownloadEvent(
+ vector<DemuxFilterEvent::Event>& events, vector<TunerFilterEvent>& res) {
+ for (DemuxFilterEvent::Event e : events) {
+ DemuxFilterDownloadEvent downloadEvent = e.download();
+ TunerFilterDownloadEvent tunerDownload;
+
+ tunerDownload.itemId = static_cast<int>(downloadEvent.itemId);
+ tunerDownload.itemFragmentIndex = static_cast<int>(downloadEvent.itemFragmentIndex);
+ tunerDownload.mpuSequenceNumber = static_cast<int>(downloadEvent.mpuSequenceNumber);
+ tunerDownload.lastItemFragmentIndex = static_cast<int>(downloadEvent.lastItemFragmentIndex);
+ tunerDownload.dataLength = static_cast<char16_t>(downloadEvent.dataLength);
+
+ TunerFilterEvent tunerEvent;
+ tunerEvent.set<TunerFilterEvent::download>(move(tunerDownload));
+ res.push_back(move(tunerEvent));
+ }
+}
+
+void TunerFilter::FilterCallback::getIpPayloadEvent(
+ vector<DemuxFilterEvent::Event>& events, vector<TunerFilterEvent>& res) {
+ for (DemuxFilterEvent::Event e : events) {
+ DemuxFilterIpPayloadEvent ipPayloadEvent = e.ipPayload();
+ TunerFilterIpPayloadEvent tunerIpPayload;
+
+ tunerIpPayload.dataLength = static_cast<char16_t>(ipPayloadEvent.dataLength);
+
+ TunerFilterEvent tunerEvent;
+ tunerEvent.set<TunerFilterEvent::ipPayload>(move(tunerIpPayload));
+ res.push_back(move(tunerEvent));
+ }
+}
+
+void TunerFilter::FilterCallback::getTemiEvent(
+ vector<DemuxFilterEvent::Event>& events, vector<TunerFilterEvent>& res) {
+ for (DemuxFilterEvent::Event e : events) {
+ DemuxFilterTemiEvent temiEvent = e.temi();
+ TunerFilterTemiEvent tunerTemi;
+
+ tunerTemi.pts = static_cast<long>(temiEvent.pts);
+ tunerTemi.descrTag = static_cast<int8_t>(temiEvent.descrTag);
+ vector<uint8_t> descrData = temiEvent.descrData;
+ tunerTemi.descrData.resize(descrData.size());
+ copy(descrData.begin(), descrData.end(), tunerTemi.descrData.begin());
+
+ TunerFilterEvent tunerEvent;
+ tunerEvent.set<TunerFilterEvent::temi>(move(tunerTemi));
+ res.push_back(move(tunerEvent));
+ }
+}
+
+void TunerFilter::FilterCallback::getMonitorEvent(
+ vector<DemuxFilterEventExt::Event>& eventsExt, vector<TunerFilterEvent>& res) {
+ DemuxFilterMonitorEvent monitorEvent = eventsExt[0].monitorEvent();
+ TunerFilterMonitorEvent tunerMonitor;
+
+ switch (monitorEvent.getDiscriminator()) {
+ case DemuxFilterMonitorEvent::hidl_discriminator::scramblingStatus: {
+ tunerMonitor.set<TunerFilterMonitorEvent::scramblingStatus>(
+ static_cast<int>(monitorEvent.scramblingStatus()));
+ break;
+ }
+ case DemuxFilterMonitorEvent::hidl_discriminator::cid: {
+ tunerMonitor.set<TunerFilterMonitorEvent::cid>(static_cast<int>(monitorEvent.cid()));
+ break;
+ }
+ }
+
+ TunerFilterEvent tunerEvent;
+ tunerEvent.set<TunerFilterEvent::monitor>(move(tunerMonitor));
+ res.push_back(move(tunerEvent));
+}
+
+void TunerFilter::FilterCallback::getRestartEvent(
+ vector<DemuxFilterEventExt::Event>& eventsExt, vector<TunerFilterEvent>& res) {
+ TunerFilterEvent tunerEvent;
+ tunerEvent.set<TunerFilterEvent::startId>(static_cast<int>(eventsExt[0].startId()));
+ res.push_back(move(tunerEvent));
+}
+} // namespace android
diff --git a/services/tuner/TunerFilter.h b/services/tuner/TunerFilter.h
new file mode 100644
index 0000000..ff4728c
--- /dev/null
+++ b/services/tuner/TunerFilter.h
@@ -0,0 +1,192 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TUNERFILTER_H
+#define ANDROID_MEDIA_TUNERFILTER_H
+
+#include <aidl/android/media/tv/tuner/BnTunerFilter.h>
+#include <aidl/android/media/tv/tuner/ITunerFilterCallback.h>
+#include <aidlcommonsupport/NativeHandle.h>
+#include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include <android/hardware/tv/tuner/1.1/IFilter.h>
+#include <android/hardware/tv/tuner/1.1/IFilterCallback.h>
+#include <android/hardware/tv/tuner/1.1/types.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <fmq/ConvertMQDescriptors.h>
+#include <fmq/MessageQueue.h>
+
+using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::hardware::common::fmq::MQDescriptor;
+using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
+using ::aidl::android::media::tv::tuner::BnTunerFilter;
+using ::aidl::android::media::tv::tuner::ITunerFilterCallback;
+using ::aidl::android::media::tv::tuner::TunerDemuxIpAddress;
+using ::aidl::android::media::tv::tuner::TunerFilterConfiguration;
+using ::aidl::android::media::tv::tuner::TunerFilterDownloadEvent;
+using ::aidl::android::media::tv::tuner::TunerFilterIpPayloadEvent;
+using ::aidl::android::media::tv::tuner::TunerFilterEvent;
+using ::aidl::android::media::tv::tuner::TunerFilterMediaEvent;
+using ::aidl::android::media::tv::tuner::TunerFilterMmtpRecordEvent;
+using ::aidl::android::media::tv::tuner::TunerFilterMonitorEvent;
+using ::aidl::android::media::tv::tuner::TunerFilterPesEvent;
+using ::aidl::android::media::tv::tuner::TunerFilterScIndexMask;
+using ::aidl::android::media::tv::tuner::TunerFilterSectionEvent;
+using ::aidl::android::media::tv::tuner::TunerFilterSharedHandleInfo;
+using ::aidl::android::media::tv::tuner::TunerFilterSettings;
+using ::aidl::android::media::tv::tuner::TunerFilterTemiEvent;
+using ::aidl::android::media::tv::tuner::TunerFilterTsRecordEvent;
+using ::android::hardware::MQDescriptorSync;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_array;
+using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterAvSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadEvent;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterIpPayloadEvent;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterMediaEvent;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterMmtpRecordEvent;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterPesEvent;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterRecordSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionEvent;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterStatus;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterTemiEvent;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterTsRecordEvent;
+using ::android::hardware::tv::tuner::V1_0::DemuxIpFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxTlvFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxPid;
+using ::android::hardware::tv::tuner::V1_0::IFilter;
+using ::android::hardware::tv::tuner::V1_1::AvStreamType;
+using ::android::hardware::tv::tuner::V1_1::DemuxFilterEventExt;
+using ::android::hardware::tv::tuner::V1_1::DemuxFilterMonitorEvent;
+using ::android::hardware::tv::tuner::V1_1::DemuxFilterTsRecordEventExt;
+using ::android::hardware::tv::tuner::V1_1::IFilterCallback;
+
+namespace android {
+
+using MQDesc = MQDescriptorSync<uint8_t>;
+using AidlMQDesc = MQDescriptor<int8_t, SynchronizedReadWrite>;
+
+const static int IP_V4_LENGTH = 4;
+const static int IP_V6_LENGTH = 16;
+
+class TunerFilter : public BnTunerFilter {
+
+public:
+ TunerFilter(sp<IFilter> filter, int mainType, int subTyp);
+ virtual ~TunerFilter();
+ Status getId(int32_t* _aidl_return) override;
+ Status getId64Bit(int64_t* _aidl_return) override;
+ Status getQueueDesc(AidlMQDesc* _aidl_return) override;
+ Status configure(const TunerFilterConfiguration& config) override;
+ Status configureMonitorEvent(int monitorEventType) override;
+ Status configureIpFilterContextId(int cid) override;
+ Status configureAvStreamType(int avStreamType) override;
+ Status getAvSharedHandleInfo(TunerFilterSharedHandleInfo* _aidl_return) override;
+ Status releaseAvHandle(const ::aidl::android::hardware::common::NativeHandle& handle,
+ int64_t avDataId) override;
+ Status setDataSource(const std::shared_ptr<ITunerFilter>& filter) override;
+ Status start() override;
+ Status stop() override;
+ Status flush() override;
+ Status close() override;
+ sp<IFilter> getHalFilter();
+
+ struct FilterCallback : public IFilterCallback {
+ FilterCallback(const std::shared_ptr<ITunerFilterCallback> tunerFilterCallback)
+ : mTunerFilterCallback(tunerFilterCallback) {};
+
+ virtual Return<void> onFilterEvent(const DemuxFilterEvent& filterEvent);
+ virtual Return<void> onFilterEvent_1_1(const DemuxFilterEvent& filterEvent,
+ const DemuxFilterEventExt& filterEventExt);
+ virtual Return<void> onFilterStatus(DemuxFilterStatus status);
+
+ void getAidlFilterEvent(std::vector<DemuxFilterEvent::Event>& events,
+ std::vector<DemuxFilterEventExt::Event>& eventsExt,
+ std::vector<TunerFilterEvent>& tunerEvent);
+
+ void getMediaEvent(
+ std::vector<DemuxFilterEvent::Event>& events, std::vector<TunerFilterEvent>& res);
+ void getSectionEvent(
+ std::vector<DemuxFilterEvent::Event>& events, std::vector<TunerFilterEvent>& res);
+ void getPesEvent(
+ std::vector<DemuxFilterEvent::Event>& events, std::vector<TunerFilterEvent>& res);
+ void getTsRecordEvent(
+ std::vector<DemuxFilterEvent::Event>& events,
+ std::vector<DemuxFilterEventExt::Event>& eventsExt,
+ std::vector<TunerFilterEvent>& res);
+ void getMmtpRecordEvent(
+ std::vector<DemuxFilterEvent::Event>& events,
+ std::vector<DemuxFilterEventExt::Event>& eventsExt,
+ std::vector<TunerFilterEvent>& res);
+ void getDownloadEvent(
+ std::vector<DemuxFilterEvent::Event>& events, std::vector<TunerFilterEvent>& res);
+ void getIpPayloadEvent(
+ std::vector<DemuxFilterEvent::Event>& events, std::vector<TunerFilterEvent>& res);
+ void getTemiEvent(
+ std::vector<DemuxFilterEvent::Event>& events, std::vector<TunerFilterEvent>& res);
+ void getMonitorEvent(
+ std::vector<DemuxFilterEventExt::Event>& eventsExt,
+ std::vector<TunerFilterEvent>& res);
+ void getRestartEvent(
+ std::vector<DemuxFilterEventExt::Event>& eventsExt,
+ std::vector<TunerFilterEvent>& res);
+
+ std::shared_ptr<ITunerFilterCallback> mTunerFilterCallback;
+ };
+
+private:
+ DemuxFilterAvSettings getAvSettings(const TunerFilterSettings& settings);
+ DemuxFilterSectionSettings getSectionSettings(const TunerFilterSettings& settings);
+ DemuxFilterPesDataSettings getPesDataSettings(const TunerFilterSettings& settings);
+ DemuxFilterRecordSettings getRecordSettings(const TunerFilterSettings& settings);
+ DemuxFilterDownloadSettings getDownloadSettings(const TunerFilterSettings& settings);
+
+ bool isAudioFilter();
+ bool isVideoFilter();
+ bool getHidlAvStreamType(int avStreamType, AvStreamType& type);
+
+ void getHidlTsSettings(
+ const TunerFilterConfiguration& config, DemuxFilterSettings& settings);
+ void getHidlMmtpSettings(
+ const TunerFilterConfiguration& config, DemuxFilterSettings& settings);
+ void getHidlIpSettings(
+ const TunerFilterConfiguration& config, DemuxFilterSettings& settings);
+ void getHidlTlvSettings(
+ const TunerFilterConfiguration& config, DemuxFilterSettings& settings);
+ void getHidlAlpSettings(
+ const TunerFilterConfiguration& config, DemuxFilterSettings& settings);
+
+ hidl_array<uint8_t, IP_V4_LENGTH> getIpV4Address(TunerDemuxIpAddress addr);
+ hidl_array<uint8_t, IP_V6_LENGTH> getIpV6Address(TunerDemuxIpAddress addr);
+
+ sp<IFilter> mFilter;
+ sp<::android::hardware::tv::tuner::V1_1::IFilter> mFilter_1_1;
+ int32_t mId;
+ int64_t mId64Bit;
+ int mMainType;
+ int mSubType;
+};
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_TUNERFILTER_H
diff --git a/services/tuner/TunerFrontend.cpp b/services/tuner/TunerFrontend.cpp
new file mode 100644
index 0000000..74b5519
--- /dev/null
+++ b/services/tuner/TunerFrontend.cpp
@@ -0,0 +1,1094 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TunerFrontend"
+
+#include "TunerFrontend.h"
+#include "TunerLnb.h"
+
+using ::aidl::android::media::tv::tuner::TunerFrontendAtsc3PlpSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendScanAtsc3PlpInfo;
+using ::aidl::android::media::tv::tuner::TunerFrontendStatusAtsc3PlpInfo;
+using ::aidl::android::media::tv::tuner::TunerFrontendUnionSettings;
+using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSifStandard;
+using ::android::hardware::tv::tuner::V1_0::FrontendAnalogType;
+using ::android::hardware::tv::tuner::V1_0::FrontendAtscModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Bandwidth;
+using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3CodeRate;
+using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3DemodOutputFormat;
+using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Fec;
+using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Modulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3TimeInterleaveMode;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbcAnnex;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbcModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbcOuterFec;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbcSpectralInversion;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbsModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbsPilot;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbsRolloff;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbsSettings;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbsStandard;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbsVcmMode;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbtBandwidth;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbtCoderate;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbtConstellation;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbtGuardInterval;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbtHierarchy;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbtPlpMode;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbtSettings;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbtStandard;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbtTransmissionMode;
+using ::android::hardware::tv::tuner::V1_0::FrontendInnerFec;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Coderate;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Modulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Rolloff;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Settings;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsCoderate;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsRolloff;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsSettings;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsStreamIdType;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtBandwidth;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtCoderate;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtGuardInterval;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtMode;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtSettings;
+using ::android::hardware::tv::tuner::V1_0::FrontendModulationStatus;
+using ::android::hardware::tv::tuner::V1_0::FrontendScanAtsc3PlpInfo;
+using ::android::hardware::tv::tuner::V1_0::FrontendScanType;
+using ::android::hardware::tv::tuner::V1_0::FrontendStatusType;
+using ::android::hardware::tv::tuner::V1_0::Result;
+using ::android::hardware::tv::tuner::V1_1::FrontendAnalogAftFlag;
+using ::android::hardware::tv::tuner::V1_1::FrontendBandwidth;
+using ::android::hardware::tv::tuner::V1_1::FrontendCableTimeInterleaveMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbcBandwidth;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbBandwidth;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbCodeRate;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbGuardInterval;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbModulation;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTimeInterleaveMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTransmissionMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbsScanType;
+using ::android::hardware::tv::tuner::V1_1::FrontendGuardInterval;
+using ::android::hardware::tv::tuner::V1_1::FrontendInterleaveMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendModulation;
+using ::android::hardware::tv::tuner::V1_1::FrontendRollOff;
+using ::android::hardware::tv::tuner::V1_1::FrontendTransmissionMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendSpectralInversion;
+using ::android::hardware::tv::tuner::V1_1::FrontendStatusTypeExt1_1;
+
+namespace android {
+
+TunerFrontend::TunerFrontend(sp<IFrontend> frontend, int id) {
+ mFrontend = frontend;
+ mFrontend_1_1 = ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFrontend);
+ mId = id;
+}
+
+TunerFrontend::~TunerFrontend() {
+ mFrontend = NULL;
+ mFrontend_1_1 = NULL;
+ mId = -1;
+}
+
+Status TunerFrontend::setCallback(
+ const shared_ptr<ITunerFrontendCallback>& tunerFrontendCallback) {
+ if (mFrontend == NULL) {
+ ALOGE("IFrontend is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ if (tunerFrontendCallback == NULL) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_ARGUMENT));
+ }
+
+ sp<IFrontendCallback> frontendCallback = new FrontendCallback(tunerFrontendCallback);
+ Result status = mFrontend->setCallback(frontendCallback);
+ if (status == Result::SUCCESS) {
+ return Status::ok();
+ }
+
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+}
+
+Status TunerFrontend::tune(const TunerFrontendSettings& settings) {
+ if (mFrontend == NULL) {
+ ALOGE("IFrontend is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result status;
+ FrontendSettings frontendSettings = getHidlFrontendSettings(settings);
+ if (settings.isExtended) {
+ if (mFrontend_1_1 == NULL) {
+ ALOGE("IFrontend_1_1 is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+ FrontendSettingsExt1_1 frontendSettingsExt = getHidlFrontendSettingsExt(settings);
+ status = mFrontend_1_1->tune_1_1(frontendSettings, frontendSettingsExt);
+ } else {
+ status = mFrontend->tune(frontendSettings);
+ }
+
+ if (status == Result::SUCCESS) {
+ return Status::ok();
+ }
+
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+}
+
+Status TunerFrontend::stopTune() {
+ if (mFrontend == NULL) {
+ ALOGD("IFrontend is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result status = mFrontend->stopTune();
+ if (status == Result::SUCCESS) {
+ return Status::ok();
+ }
+
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+}
+
+Status TunerFrontend::scan(const TunerFrontendSettings& settings, int frontendScanType) {
+ if (mFrontend == NULL) {
+ ALOGD("IFrontend is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result status;
+ FrontendSettings frontendSettings = getHidlFrontendSettings(settings);
+ if (settings.isExtended) {
+ if (mFrontend_1_1 == NULL) {
+ ALOGE("IFrontend_1_1 is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+ FrontendSettingsExt1_1 frontendSettingsExt = getHidlFrontendSettingsExt(settings);
+ status = mFrontend_1_1->scan_1_1(frontendSettings,
+ static_cast<FrontendScanType>(frontendScanType), frontendSettingsExt);
+ } else {
+ status = mFrontend->scan(
+ frontendSettings, static_cast<FrontendScanType>(frontendScanType));
+ }
+
+ if (status == Result::SUCCESS) {
+ return Status::ok();
+ }
+
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+}
+
+Status TunerFrontend::stopScan() {
+ if (mFrontend == NULL) {
+ ALOGD("IFrontend is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result status = mFrontend->stopScan();
+ if (status == Result::SUCCESS) {
+ return Status::ok();
+ }
+
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+}
+
+Status TunerFrontend::setLnb(const shared_ptr<ITunerLnb>& lnb) {
+ if (mFrontend == NULL) {
+ ALOGD("IFrontend is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result status = mFrontend->setLnb(static_cast<TunerLnb*>(lnb.get())->getId());
+ if (status == Result::SUCCESS) {
+ return Status::ok();
+ }
+
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+}
+
+Status TunerFrontend::setLna(bool bEnable) {
+ if (mFrontend == NULL) {
+ ALOGD("IFrontend is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result status = mFrontend->setLna(bEnable);
+ if (status == Result::SUCCESS) {
+ return Status::ok();
+ }
+
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+}
+
+Status TunerFrontend::linkCiCamToFrontend(int ciCamId, int32_t* _aidl_return) {
+ if (mFrontend_1_1 == NULL) {
+ ALOGD("IFrontend_1_1 is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ int ltsId;
+ Result status;
+ mFrontend_1_1->linkCiCam(static_cast<uint32_t>(ciCamId),
+ [&](Result r, uint32_t id) {
+ status = r;
+ ltsId = id;
+ });
+
+ if (status == Result::SUCCESS) {
+ *_aidl_return = ltsId;
+ return Status::ok();
+ }
+
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+}
+
+Status TunerFrontend::unlinkCiCamToFrontend(int ciCamId) {
+ if (mFrontend_1_1 == NULL) {
+ ALOGD("IFrontend_1_1 is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result status = mFrontend_1_1->unlinkCiCam(ciCamId);
+ if (status == Result::SUCCESS) {
+ return Status::ok();
+ }
+
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+}
+
+Status TunerFrontend::close() {
+ if (mFrontend == NULL) {
+ ALOGD("IFrontend is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result status = mFrontend->close();
+ mFrontend = NULL;
+ mFrontend_1_1 = NULL;
+
+ if (status != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+ }
+ return Status::ok();
+}
+
+Status TunerFrontend::getStatus(const vector<int32_t>& statusTypes,
+ vector<TunerFrontendStatus>* _aidl_return) {
+ if (mFrontend == NULL) {
+ ALOGD("IFrontend is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res;
+ vector<FrontendStatus> status;
+ vector<FrontendStatusType> types;
+ for (auto s : statusTypes) {
+ types.push_back(static_cast<FrontendStatusType>(s));
+ }
+
+ mFrontend->getStatus(types, [&](Result r, const hidl_vec<FrontendStatus>& s) {
+ res = r;
+ status = s;
+ });
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+
+ getAidlFrontendStatus(status, *_aidl_return);
+ return Status::ok();
+}
+
+Status TunerFrontend::getStatusExtended_1_1(const vector<int32_t>& statusTypes,
+ vector<TunerFrontendStatus>* _aidl_return) {
+ if (mFrontend_1_1 == NULL) {
+ ALOGD("IFrontend_1_1 is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res;
+ vector<FrontendStatusExt1_1> status;
+ vector<FrontendStatusTypeExt1_1> types;
+ for (auto s : statusTypes) {
+ types.push_back(static_cast<FrontendStatusTypeExt1_1>(s));
+ }
+
+ mFrontend_1_1->getStatusExt1_1(types, [&](Result r, const hidl_vec<FrontendStatusExt1_1>& s) {
+ res = r;
+ status = s;
+ });
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+
+ getAidlFrontendStatusExt(status, *_aidl_return);
+ return Status::ok();
+}
+
+Status TunerFrontend::getFrontendId(int* _aidl_return) {
+ *_aidl_return = mId;
+ return Status::ok();
+}
+
+/////////////// FrontendCallback ///////////////////////
+
+Return<void> TunerFrontend::FrontendCallback::onEvent(FrontendEventType frontendEventType) {
+ ALOGD("FrontendCallback::onEvent, type=%d", frontendEventType);
+ mTunerFrontendCallback->onEvent((int)frontendEventType);
+ return Void();
+}
+
+Return<void> TunerFrontend::FrontendCallback::onScanMessage(
+ FrontendScanMessageType type, const FrontendScanMessage& message) {
+ ALOGD("FrontendCallback::onScanMessage, type=%d", type);
+ TunerFrontendScanMessage scanMessage;
+ switch(type) {
+ case FrontendScanMessageType::LOCKED: {
+ scanMessage.set<TunerFrontendScanMessage::isLocked>(message.isLocked());
+ break;
+ }
+ case FrontendScanMessageType::END: {
+ scanMessage.set<TunerFrontendScanMessage::isEnd>(message.isEnd());
+ break;
+ }
+ case FrontendScanMessageType::PROGRESS_PERCENT: {
+ scanMessage.set<TunerFrontendScanMessage::progressPercent>(message.progressPercent());
+ break;
+ }
+ case FrontendScanMessageType::FREQUENCY: {
+ auto f = message.frequencies();
+ vector<int> frequencies(begin(f), end(f));
+ scanMessage.set<TunerFrontendScanMessage::frequencies>(frequencies);
+ break;
+ }
+ case FrontendScanMessageType::SYMBOL_RATE: {
+ auto s = message.symbolRates();
+ vector<int> symbolRates(begin(s), end(s));
+ scanMessage.set<TunerFrontendScanMessage::symbolRates>(symbolRates);
+ break;
+ }
+ case FrontendScanMessageType::HIERARCHY: {
+ scanMessage.set<TunerFrontendScanMessage::hierarchy>((int)message.hierarchy());
+ break;
+ }
+ case FrontendScanMessageType::ANALOG_TYPE: {
+ scanMessage.set<TunerFrontendScanMessage::analogType>((int)message.analogType());
+ break;
+ }
+ case FrontendScanMessageType::PLP_IDS: {
+ auto p = message.plpIds();
+ vector<uint8_t> plpIds(begin(p), end(p));
+ scanMessage.set<TunerFrontendScanMessage::plpIds>(plpIds);
+ break;
+ }
+ case FrontendScanMessageType::GROUP_IDS: {
+ auto g = message.groupIds();
+ vector<uint8_t> groupIds(begin(g), end(g));
+ scanMessage.set<TunerFrontendScanMessage::groupIds>(groupIds);
+ break;
+ }
+ case FrontendScanMessageType::INPUT_STREAM_IDS: {
+ auto i = message.inputStreamIds();
+ vector<char16_t> streamIds(begin(i), end(i));
+ scanMessage.set<TunerFrontendScanMessage::inputStreamIds>(streamIds);
+ break;
+ }
+ case FrontendScanMessageType::STANDARD: {
+ FrontendScanMessage::Standard std = message.std();
+ int standard;
+ if (std.getDiscriminator() == FrontendScanMessage::Standard::hidl_discriminator::sStd) {
+ standard = (int) std.sStd();
+ } else if (std.getDiscriminator() ==
+ FrontendScanMessage::Standard::hidl_discriminator::tStd) {
+ standard = (int) std.tStd();
+ } else if (std.getDiscriminator() ==
+ FrontendScanMessage::Standard::hidl_discriminator::sifStd) {
+ standard = (int) std.sifStd();
+ }
+ scanMessage.set<TunerFrontendScanMessage::std>(standard);
+ break;
+ }
+ case FrontendScanMessageType::ATSC3_PLP_INFO: {
+ vector<FrontendScanAtsc3PlpInfo> plpInfos = message.atsc3PlpInfos();
+ vector<TunerFrontendScanAtsc3PlpInfo> tunerPlpInfos;
+ for (int i = 0; i < plpInfos.size(); i++) {
+ auto info = plpInfos[i];
+ int8_t plpId = (int8_t) info.plpId;
+ bool lls = (bool) info.bLlsFlag;
+ TunerFrontendScanAtsc3PlpInfo plpInfo{
+ .plpId = plpId,
+ .llsFlag = lls,
+ };
+ tunerPlpInfos.push_back(plpInfo);
+ }
+ scanMessage.set<TunerFrontendScanMessage::atsc3PlpInfos>(tunerPlpInfos);
+ break;
+ }
+ default:
+ break;
+ }
+ mTunerFrontendCallback->onScanMessage((int)type, scanMessage);
+ return Void();
+}
+
+Return<void> TunerFrontend::FrontendCallback::onScanMessageExt1_1(
+ FrontendScanMessageTypeExt1_1 type, const FrontendScanMessageExt1_1& message) {
+ ALOGD("onScanMessageExt1_1::onScanMessage, type=%d", type);
+ TunerFrontendScanMessage scanMessage;
+ switch(type) {
+ case FrontendScanMessageTypeExt1_1::MODULATION: {
+ FrontendModulation m = message.modulation();
+ int modulation;
+ switch (m.getDiscriminator()) {
+ case FrontendModulation::hidl_discriminator::dvbc:
+ modulation = (int) m.dvbc();
+ break;
+ case FrontendModulation::hidl_discriminator::dvbt:
+ modulation = (int) m.dvbt();
+ break;
+ case FrontendModulation::hidl_discriminator::dvbs:
+ modulation = (int) m.dvbs();
+ break;
+ case FrontendModulation::hidl_discriminator::isdbs:
+ modulation = (int) m.isdbs();
+ break;
+ case FrontendModulation::hidl_discriminator::isdbs3:
+ modulation = (int) m.isdbs3();
+ break;
+ case FrontendModulation::hidl_discriminator::isdbt:
+ modulation = (int) m.isdbt();
+ break;
+ case FrontendModulation::hidl_discriminator::atsc:
+ modulation = (int) m.atsc();
+ break;
+ case FrontendModulation::hidl_discriminator::atsc3:
+ modulation = (int) m.atsc3();
+ break;
+ case FrontendModulation::hidl_discriminator::dtmb:
+ modulation = (int) m.dtmb();
+ break;
+ }
+ scanMessage.set<TunerFrontendScanMessage::modulation>(modulation);
+ break;
+ }
+ case FrontendScanMessageTypeExt1_1::DVBC_ANNEX: {
+ scanMessage.set<TunerFrontendScanMessage::annex>((int)message.annex());
+ break;
+ }
+ case FrontendScanMessageTypeExt1_1::HIGH_PRIORITY: {
+ scanMessage.set<TunerFrontendScanMessage::isHighPriority>(message.isHighPriority());
+ break;
+ }
+ default:
+ break;
+ }
+ mTunerFrontendCallback->onScanMessage((int)type, scanMessage);
+ return Void();
+}
+
+/////////////// TunerFrontend Helper Methods ///////////////////////
+
+void TunerFrontend::getAidlFrontendStatus(
+ vector<FrontendStatus>& hidlStatus, vector<TunerFrontendStatus>& aidlStatus) {
+ for (FrontendStatus s : hidlStatus) {
+ TunerFrontendStatus status;
+ switch (s.getDiscriminator()) {
+ case FrontendStatus::hidl_discriminator::isDemodLocked: {
+ status.set<TunerFrontendStatus::isDemodLocked>(s.isDemodLocked());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatus::hidl_discriminator::snr: {
+ status.set<TunerFrontendStatus::snr>((int)s.snr());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatus::hidl_discriminator::ber: {
+ status.set<TunerFrontendStatus::ber>((int)s.ber());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatus::hidl_discriminator::per: {
+ status.set<TunerFrontendStatus::per>((int)s.per());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatus::hidl_discriminator::preBer: {
+ status.set<TunerFrontendStatus::preBer>((int)s.preBer());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatus::hidl_discriminator::signalQuality: {
+ status.set<TunerFrontendStatus::signalQuality>((int)s.signalQuality());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatus::hidl_discriminator::signalStrength: {
+ status.set<TunerFrontendStatus::signalStrength>((int)s.signalStrength());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatus::hidl_discriminator::symbolRate: {
+ status.set<TunerFrontendStatus::symbolRate>((int)s.symbolRate());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatus::hidl_discriminator::innerFec: {
+ status.set<TunerFrontendStatus::innerFec>((long)s.innerFec());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatus::hidl_discriminator::modulation: {
+ switch (s.modulation().getDiscriminator()) {
+ case FrontendModulationStatus::hidl_discriminator::dvbc:
+ status.set<TunerFrontendStatus::modulation>((int)s.modulation().dvbc());
+ aidlStatus.push_back(status);
+ break;
+ case FrontendModulationStatus::hidl_discriminator::dvbs:
+ status.set<TunerFrontendStatus::modulation>((int)s.modulation().dvbs());
+ aidlStatus.push_back(status);
+ break;
+ case FrontendModulationStatus::hidl_discriminator::isdbs:
+ status.set<TunerFrontendStatus::modulation>((int)s.modulation().isdbs());
+ aidlStatus.push_back(status);
+ break;
+ case FrontendModulationStatus::hidl_discriminator::isdbs3:
+ status.set<TunerFrontendStatus::modulation>((int)s.modulation().isdbs3());
+ aidlStatus.push_back(status);
+ break;
+ case FrontendModulationStatus::hidl_discriminator::isdbt:
+ status.set<TunerFrontendStatus::modulation>((int)s.modulation().isdbt());
+ aidlStatus.push_back(status);
+ break;
+ }
+ break;
+ }
+ case FrontendStatus::hidl_discriminator::inversion: {
+ status.set<TunerFrontendStatus::inversion>((int)s.inversion());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatus::hidl_discriminator::lnbVoltage: {
+ status.set<TunerFrontendStatus::lnbVoltage>((int)s.lnbVoltage());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatus::hidl_discriminator::plpId: {
+ status.set<TunerFrontendStatus::plpId>((int8_t)s.plpId());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatus::hidl_discriminator::isEWBS: {
+ status.set<TunerFrontendStatus::isEWBS>(s.isEWBS());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatus::hidl_discriminator::agc: {
+ status.set<TunerFrontendStatus::agc>((int8_t)s.agc());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatus::hidl_discriminator::isLnaOn: {
+ status.set<TunerFrontendStatus::isLnaOn>(s.isLnaOn());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatus::hidl_discriminator::isLayerError: {
+ vector<bool> e(s.isLayerError().begin(), s.isLayerError().end());
+ status.set<TunerFrontendStatus::isLayerError>(e);
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatus::hidl_discriminator::mer: {
+ status.set<TunerFrontendStatus::mer>((int)s.mer());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatus::hidl_discriminator::freqOffset: {
+ status.set<TunerFrontendStatus::freqOffset>((int)s.freqOffset());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatus::hidl_discriminator::hierarchy: {
+ status.set<TunerFrontendStatus::hierarchy>((int)s.hierarchy());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatus::hidl_discriminator::isRfLocked: {
+ status.set<TunerFrontendStatus::isRfLocked>(s.isRfLocked());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatus::hidl_discriminator::plpInfo: {
+ vector<TunerFrontendStatusAtsc3PlpInfo> info;
+ for (auto i : s.plpInfo()) {
+ info.push_back({
+ .plpId = (int8_t)i.plpId,
+ .isLocked = i.isLocked,
+ .uec = (int)i.uec,
+ });
+ }
+ status.set<TunerFrontendStatus::plpInfo>(info);
+ aidlStatus.push_back(status);
+ break;
+ }
+ }
+ }
+}
+
+void TunerFrontend::getAidlFrontendStatusExt(
+ vector<FrontendStatusExt1_1>& hidlStatus, vector<TunerFrontendStatus>& aidlStatus) {
+ for (FrontendStatusExt1_1 s : hidlStatus) {
+ TunerFrontendStatus status;
+ switch (s.getDiscriminator()) {
+ case FrontendStatusExt1_1::hidl_discriminator::modulations: {
+ vector<int> aidlMod;
+ for (auto m : s.modulations()) {
+ switch (m.getDiscriminator()) {
+ case FrontendModulation::hidl_discriminator::dvbc:
+ aidlMod.push_back((int)m.dvbc());
+ break;
+ case FrontendModulation::hidl_discriminator::dvbs:
+ aidlMod.push_back((int)m.dvbs());
+ break;
+ case FrontendModulation::hidl_discriminator::dvbt:
+ aidlMod.push_back((int)m.dvbt());
+ break;
+ case FrontendModulation::hidl_discriminator::isdbs:
+ aidlMod.push_back((int)m.isdbs());
+ break;
+ case FrontendModulation::hidl_discriminator::isdbs3:
+ aidlMod.push_back((int)m.isdbs3());
+ break;
+ case FrontendModulation::hidl_discriminator::isdbt:
+ aidlMod.push_back((int)m.isdbt());
+ break;
+ case FrontendModulation::hidl_discriminator::atsc:
+ aidlMod.push_back((int)m.atsc());
+ break;
+ case FrontendModulation::hidl_discriminator::atsc3:
+ aidlMod.push_back((int)m.atsc3());
+ break;
+ case FrontendModulation::hidl_discriminator::dtmb:
+ aidlMod.push_back((int)m.dtmb());
+ break;
+ }
+ }
+ status.set<TunerFrontendStatus::modulations>(aidlMod);
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatusExt1_1::hidl_discriminator::bers: {
+ vector<int> b(s.bers().begin(), s.bers().end());
+ status.set<TunerFrontendStatus::bers>(b);
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatusExt1_1::hidl_discriminator::codeRates: {
+ vector<int64_t> codeRates;
+ for (auto c : s.codeRates()) {
+ codeRates.push_back((long)c);
+ }
+ status.set<TunerFrontendStatus::codeRates>(codeRates);
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatusExt1_1::hidl_discriminator::bandwidth: {
+ switch (s.bandwidth().getDiscriminator()) {
+ case FrontendBandwidth::hidl_discriminator::atsc3:
+ status.set<TunerFrontendStatus::bandwidth>((int)s.bandwidth().atsc3());
+ break;
+ case FrontendBandwidth::hidl_discriminator::dvbc:
+ status.set<TunerFrontendStatus::bandwidth>((int)s.bandwidth().dvbc());
+ break;
+ case FrontendBandwidth::hidl_discriminator::dvbt:
+ status.set<TunerFrontendStatus::bandwidth>((int)s.bandwidth().dvbt());
+ break;
+ case FrontendBandwidth::hidl_discriminator::isdbt:
+ status.set<TunerFrontendStatus::bandwidth>((int)s.bandwidth().isdbt());
+ break;
+ case FrontendBandwidth::hidl_discriminator::dtmb:
+ status.set<TunerFrontendStatus::bandwidth>((int)s.bandwidth().dtmb());
+ break;
+ }
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatusExt1_1::hidl_discriminator::interval: {
+ switch (s.interval().getDiscriminator()) {
+ case FrontendGuardInterval::hidl_discriminator::dvbt:
+ status.set<TunerFrontendStatus::interval>((int)s.interval().dvbt());
+ break;
+ case FrontendGuardInterval::hidl_discriminator::isdbt:
+ status.set<TunerFrontendStatus::interval>((int)s.interval().isdbt());
+ break;
+ case FrontendGuardInterval::hidl_discriminator::dtmb:
+ status.set<TunerFrontendStatus::interval>((int)s.interval().dtmb());
+ break;
+ }
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatusExt1_1::hidl_discriminator::transmissionMode: {
+ switch (s.transmissionMode().getDiscriminator()) {
+ case FrontendTransmissionMode::hidl_discriminator::dvbt:
+ status.set<TunerFrontendStatus::transmissionMode>(
+ (int)s.transmissionMode().dvbt());
+ break;
+ case FrontendTransmissionMode::hidl_discriminator::isdbt:
+ status.set<TunerFrontendStatus::transmissionMode>(
+ (int)s.transmissionMode().isdbt());
+ break;
+ case FrontendTransmissionMode::hidl_discriminator::dtmb:
+ status.set<TunerFrontendStatus::transmissionMode>(
+ (int)s.transmissionMode().dtmb());
+ break;
+ }
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatusExt1_1::hidl_discriminator::uec: {
+ status.set<TunerFrontendStatus::uec>((int)s.uec());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatusExt1_1::hidl_discriminator::systemId: {
+ status.set<TunerFrontendStatus::systemId>((char16_t)s.systemId());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatusExt1_1::hidl_discriminator::interleaving: {
+ vector<int> aidlInter;
+ for (auto i : s.interleaving()) {
+ switch (i.getDiscriminator()) {
+ case FrontendInterleaveMode::hidl_discriminator::atsc3:
+ aidlInter.push_back((int)i.atsc3());
+ break;
+ case FrontendInterleaveMode::hidl_discriminator::dvbc:
+ aidlInter.push_back((int)i.dvbc());
+ break;
+ case FrontendInterleaveMode::hidl_discriminator::dtmb:
+ aidlInter.push_back((int)i.dtmb());
+ break;
+ }
+ }
+ status.set<TunerFrontendStatus::interleaving>(aidlInter);
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatusExt1_1::hidl_discriminator::isdbtSegment: {
+ auto seg = s.isdbtSegment();
+ vector<uint8_t> i(seg.begin(), seg.end());
+ status.set<TunerFrontendStatus::isdbtSegment>(i);
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatusExt1_1::hidl_discriminator::tsDataRate: {
+ vector<int> ts(s.tsDataRate().begin(), s.tsDataRate().end());
+ status.set<TunerFrontendStatus::tsDataRate>(ts);
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatusExt1_1::hidl_discriminator::rollOff: {
+ switch (s.rollOff().getDiscriminator()) {
+ case FrontendRollOff::hidl_discriminator::dvbs:
+ status.set<TunerFrontendStatus::rollOff>((int)s.rollOff().dvbs());
+ break;
+ case FrontendRollOff::hidl_discriminator::isdbs:
+ status.set<TunerFrontendStatus::rollOff>((int)s.rollOff().isdbs());
+ break;
+ case FrontendRollOff::hidl_discriminator::isdbs3:
+ status.set<TunerFrontendStatus::rollOff>((int)s.rollOff().isdbs3());
+ break;
+ }
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatusExt1_1::hidl_discriminator::isMiso: {
+ status.set<TunerFrontendStatus::isMiso>(s.isMiso());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatusExt1_1::hidl_discriminator::isLinear: {
+ status.set<TunerFrontendStatus::isLinear>(s.isLinear());
+ aidlStatus.push_back(status);
+ break;
+ }
+ case FrontendStatusExt1_1::hidl_discriminator::isShortFrames: {
+ status.set<TunerFrontendStatus::isShortFrames>(s.isShortFrames());
+ aidlStatus.push_back(status);
+ break;
+ }
+ }
+ }
+}
+
+hidl_vec<FrontendAtsc3PlpSettings> TunerFrontend::getAtsc3PlpSettings(
+ const TunerFrontendAtsc3Settings& settings) {
+ int len = settings.plpSettings.size();
+ hidl_vec<FrontendAtsc3PlpSettings> plps = hidl_vec<FrontendAtsc3PlpSettings>(len);
+ // parse PLP settings
+ for (int i = 0; i < len; i++) {
+ uint8_t plpId = static_cast<uint8_t>(settings.plpSettings[i].plpId);
+ FrontendAtsc3Modulation modulation =
+ static_cast<FrontendAtsc3Modulation>(settings.plpSettings[i].modulation);
+ FrontendAtsc3TimeInterleaveMode interleaveMode =
+ static_cast<FrontendAtsc3TimeInterleaveMode>(
+ settings.plpSettings[i].interleaveMode);
+ FrontendAtsc3CodeRate codeRate =
+ static_cast<FrontendAtsc3CodeRate>(settings.plpSettings[i].codeRate);
+ FrontendAtsc3Fec fec =
+ static_cast<FrontendAtsc3Fec>(settings.plpSettings[i].fec);
+ FrontendAtsc3PlpSettings frontendAtsc3PlpSettings {
+ .plpId = plpId,
+ .modulation = modulation,
+ .interleaveMode = interleaveMode,
+ .codeRate = codeRate,
+ .fec = fec,
+ };
+ plps[i] = frontendAtsc3PlpSettings;
+ }
+ return plps;
+}
+
+FrontendDvbsCodeRate TunerFrontend::getDvbsCodeRate(const TunerFrontendDvbsCodeRate& codeRate) {
+ FrontendInnerFec innerFec = static_cast<FrontendInnerFec>(codeRate.fec);
+ bool isLinear = codeRate.isLinear;
+ bool isShortFrames = codeRate.isShortFrames;
+ uint32_t bitsPer1000Symbol = static_cast<uint32_t>(codeRate.bitsPer1000Symbol);
+ FrontendDvbsCodeRate coderate {
+ .fec = innerFec,
+ .isLinear = isLinear,
+ .isShortFrames = isShortFrames,
+ .bitsPer1000Symbol = bitsPer1000Symbol,
+ };
+ return coderate;
+}
+
+FrontendSettings TunerFrontend::getHidlFrontendSettings(const TunerFrontendSettings& aidlSettings) {
+ auto settings = aidlSettings.settings;
+ FrontendSettings frontendSettings;
+
+ switch (settings.getTag()) {
+ case TunerFrontendUnionSettings::analog: {
+ auto analog = settings.get<TunerFrontendUnionSettings::analog>();
+ frontendSettings.analog({
+ .frequency = static_cast<uint32_t>(analog.frequency),
+ .type = static_cast<FrontendAnalogType>(analog.signalType),
+ .sifStandard = static_cast<FrontendAnalogSifStandard>(analog.sifStandard),
+ });
+ break;
+ }
+ case TunerFrontendUnionSettings::atsc: {
+ auto atsc = settings.get<TunerFrontendUnionSettings::atsc>();
+ frontendSettings.atsc({
+ .frequency = static_cast<uint32_t>(atsc.frequency),
+ .modulation = static_cast<FrontendAtscModulation>(atsc.modulation),
+ });
+ break;
+ }
+ case TunerFrontendUnionSettings::atsc3: {
+ auto atsc3 = settings.get<TunerFrontendUnionSettings::atsc3>();
+ frontendSettings.atsc3({
+ .frequency = static_cast<uint32_t>(atsc3.frequency),
+ .bandwidth = static_cast<FrontendAtsc3Bandwidth>(atsc3.bandwidth),
+ .demodOutputFormat = static_cast<FrontendAtsc3DemodOutputFormat>(
+ atsc3.demodOutputFormat),
+ .plpSettings = getAtsc3PlpSettings(atsc3),
+ });
+ break;
+ }
+ case TunerFrontendUnionSettings::cable: {
+ auto dvbc = settings.get<TunerFrontendUnionSettings::cable>();
+ frontendSettings.dvbc({
+ .frequency = static_cast<uint32_t>(dvbc.frequency),
+ .modulation = static_cast<FrontendDvbcModulation>(dvbc.modulation),
+ .fec = static_cast<FrontendInnerFec>(dvbc.innerFec),
+ .symbolRate = static_cast<uint32_t>(dvbc.symbolRate),
+ .outerFec = static_cast<FrontendDvbcOuterFec>(dvbc.outerFec),
+ .annex = static_cast<FrontendDvbcAnnex>(dvbc.annex),
+ .spectralInversion = static_cast<FrontendDvbcSpectralInversion>(
+ dvbc.spectralInversion),
+ });
+ break;
+ }
+ case TunerFrontendUnionSettings::dvbs: {
+ auto dvbs = settings.get<TunerFrontendUnionSettings::dvbs>();
+ frontendSettings.dvbs({
+ .frequency = static_cast<uint32_t>(dvbs.frequency),
+ .modulation = static_cast<FrontendDvbsModulation>(dvbs.modulation),
+ .coderate = getDvbsCodeRate(dvbs.codeRate),
+ .symbolRate = static_cast<uint32_t>(dvbs.symbolRate),
+ .rolloff = static_cast<FrontendDvbsRolloff>(dvbs.rolloff),
+ .pilot = static_cast<FrontendDvbsPilot>(dvbs.pilot),
+ .inputStreamId = static_cast<uint32_t>(dvbs.inputStreamId),
+ .standard = static_cast<FrontendDvbsStandard>(dvbs.standard),
+ .vcmMode = static_cast<FrontendDvbsVcmMode>(dvbs.vcm),
+ });
+ break;
+ }
+ case TunerFrontendUnionSettings::dvbt: {
+ auto dvbt = settings.get<TunerFrontendUnionSettings::dvbt>();
+ frontendSettings.dvbt({
+ .frequency = static_cast<uint32_t>(dvbt.frequency),
+ .transmissionMode = static_cast<FrontendDvbtTransmissionMode>(
+ dvbt.transmissionMode),
+ .bandwidth = static_cast<FrontendDvbtBandwidth>(dvbt.bandwidth),
+ .constellation = static_cast<FrontendDvbtConstellation>(dvbt.constellation),
+ .hierarchy = static_cast<FrontendDvbtHierarchy>(dvbt.hierarchy),
+ .hpCoderate = static_cast<FrontendDvbtCoderate>(dvbt.hpCodeRate),
+ .lpCoderate = static_cast<FrontendDvbtCoderate>(dvbt.lpCodeRate),
+ .guardInterval = static_cast<FrontendDvbtGuardInterval>(dvbt.guardInterval),
+ .isHighPriority = dvbt.isHighPriority,
+ .standard = static_cast<FrontendDvbtStandard>(dvbt.standard),
+ .isMiso = dvbt.isMiso,
+ .plpMode = static_cast<FrontendDvbtPlpMode>(dvbt.plpMode),
+ .plpId = static_cast<uint8_t>(dvbt.plpId),
+ .plpGroupId = static_cast<uint8_t>(dvbt.plpGroupId),
+ });
+ break;
+ }
+ case TunerFrontendUnionSettings::isdbs: {
+ auto isdbs = settings.get<TunerFrontendUnionSettings::isdbs>();
+ frontendSettings.isdbs({
+ .frequency = static_cast<uint32_t>(isdbs.frequency),
+ .streamId = static_cast<uint16_t>(isdbs.streamId),
+ .streamIdType = static_cast<FrontendIsdbsStreamIdType>(isdbs.streamIdType),
+ .modulation = static_cast<FrontendIsdbsModulation>(isdbs.modulation),
+ .coderate = static_cast<FrontendIsdbsCoderate>(isdbs.codeRate),
+ .symbolRate = static_cast<uint32_t>(isdbs.symbolRate),
+ .rolloff = static_cast<FrontendIsdbsRolloff>(isdbs.rolloff),
+ });
+ break;
+ }
+ case TunerFrontendUnionSettings::isdbs3: {
+ auto isdbs3 = settings.get<TunerFrontendUnionSettings::isdbs3>();
+ frontendSettings.isdbs3({
+ .frequency = static_cast<uint32_t>(isdbs3.frequency),
+ .streamId = static_cast<uint16_t>(isdbs3.streamId),
+ .streamIdType = static_cast<FrontendIsdbsStreamIdType>(isdbs3.streamIdType),
+ .modulation = static_cast<FrontendIsdbs3Modulation>(isdbs3.modulation),
+ .coderate = static_cast<FrontendIsdbs3Coderate>(isdbs3.codeRate),
+ .symbolRate = static_cast<uint32_t>(isdbs3.symbolRate),
+ .rolloff = static_cast<FrontendIsdbs3Rolloff>(isdbs3.rolloff),
+ });
+ break;
+ }
+ case TunerFrontendUnionSettings::isdbt: {
+ auto isdbt = settings.get<TunerFrontendUnionSettings::isdbt>();
+ frontendSettings.isdbt({
+ .frequency = static_cast<uint32_t>(isdbt.frequency),
+ .modulation = static_cast<FrontendIsdbtModulation>(isdbt.modulation),
+ .bandwidth = static_cast<FrontendIsdbtBandwidth>(isdbt.bandwidth),
+ .mode = static_cast<FrontendIsdbtMode>(isdbt.mode),
+ .coderate = static_cast<FrontendIsdbtCoderate>(isdbt.codeRate),
+ .guardInterval = static_cast<FrontendIsdbtGuardInterval>(isdbt.guardInterval),
+ .serviceAreaId = static_cast<uint32_t>(isdbt.serviceAreaId),
+ });
+ break;
+ }
+ default:
+ break;
+ }
+
+ return frontendSettings;
+}
+
+FrontendSettingsExt1_1 TunerFrontend::getHidlFrontendSettingsExt(
+ const TunerFrontendSettings& aidlSettings) {
+ FrontendSettingsExt1_1 frontendSettingsExt{
+ .endFrequency = static_cast<uint32_t>(aidlSettings.endFrequency),
+ .inversion = static_cast<FrontendSpectralInversion>(aidlSettings.inversion),
+ };
+
+ auto settings = aidlSettings.settings;
+ switch (settings.getTag()) {
+ case TunerFrontendUnionSettings::analog: {
+ auto analog = settings.get<TunerFrontendUnionSettings::analog>();
+ if (analog.isExtended) {
+ frontendSettingsExt.settingExt.analog({
+ .aftFlag = static_cast<FrontendAnalogAftFlag>(analog.aftFlag),
+ });
+ } else {
+ frontendSettingsExt.settingExt.noinit();
+ }
+ break;
+ }
+ case TunerFrontendUnionSettings::cable: {
+ auto dvbc = settings.get<TunerFrontendUnionSettings::cable>();
+ if (dvbc.isExtended) {
+ frontendSettingsExt.settingExt.dvbc({
+ .interleaveMode = static_cast<FrontendCableTimeInterleaveMode>(
+ dvbc.interleaveMode),
+ .bandwidth = static_cast<FrontendDvbcBandwidth>(
+ dvbc.bandwidth),
+ });
+ } else {
+ frontendSettingsExt.settingExt.noinit();
+ }
+ break;
+ }
+ case TunerFrontendUnionSettings::dvbs: {
+ auto dvbs = settings.get<TunerFrontendUnionSettings::dvbs>();
+ if (dvbs.isExtended) {
+ frontendSettingsExt.settingExt.dvbs({
+ .scanType = static_cast<FrontendDvbsScanType>(dvbs.scanType),
+ .isDiseqcRxMessage = dvbs.isDiseqcRxMessage,
+ });
+ } else {
+ frontendSettingsExt.settingExt.noinit();
+ }
+ break;
+ }
+ case TunerFrontendUnionSettings::dvbt: {
+ auto dvbt = settings.get<TunerFrontendUnionSettings::dvbt>();
+ if (dvbt.isExtended) {
+ frontendSettingsExt.settingExt.dvbt({
+ .constellation =
+ static_cast<hardware::tv::tuner::V1_1::FrontendDvbtConstellation>(
+ dvbt.constellation),
+ .transmissionMode =
+ static_cast<hardware::tv::tuner::V1_1::FrontendDvbtTransmissionMode>(
+ dvbt.transmissionMode),
+ });
+ } else {
+ frontendSettingsExt.settingExt.noinit();
+ }
+ break;
+ }
+ case TunerFrontendUnionSettings::dtmb: {
+ auto dtmb = settings.get<TunerFrontendUnionSettings::dtmb>();
+ frontendSettingsExt.settingExt.dtmb({
+ .frequency = static_cast<uint32_t>(dtmb.frequency),
+ .transmissionMode = static_cast<FrontendDtmbTransmissionMode>(
+ dtmb.transmissionMode),
+ .bandwidth = static_cast<FrontendDtmbBandwidth>(dtmb.bandwidth),
+ .modulation = static_cast<FrontendDtmbModulation>(dtmb.modulation),
+ .codeRate = static_cast<FrontendDtmbCodeRate>(dtmb.codeRate),
+ .guardInterval = static_cast<FrontendDtmbGuardInterval>(dtmb.guardInterval),
+ .interleaveMode = static_cast<FrontendDtmbTimeInterleaveMode>(dtmb.interleaveMode),
+ });
+ break;
+ }
+ default:
+ frontendSettingsExt.settingExt.noinit();
+ break;
+ }
+
+ return frontendSettingsExt;
+}
+} // namespace android
diff --git a/services/tuner/TunerFrontend.h b/services/tuner/TunerFrontend.h
new file mode 100644
index 0000000..22fd509
--- /dev/null
+++ b/services/tuner/TunerFrontend.h
@@ -0,0 +1,111 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TUNERFRONTEND_H
+#define ANDROID_MEDIA_TUNERFRONTEND_H
+
+#include <aidl/android/media/tv/tuner/BnTunerFrontend.h>
+#include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include <android/hardware/tv/tuner/1.1/IFrontend.h>
+#include <android/hardware/tv/tuner/1.1/IFrontendCallback.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <utils/Log.h>
+
+using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::media::tv::tuner::BnTunerFrontend;
+using ::aidl::android::media::tv::tuner::ITunerFrontendCallback;
+using ::aidl::android::media::tv::tuner::ITunerLnb;
+using ::aidl::android::media::tv::tuner::TunerFrontendAtsc3Settings;
+using ::aidl::android::media::tv::tuner::TunerFrontendDvbsCodeRate;
+using ::aidl::android::media::tv::tuner::TunerFrontendScanMessage;
+using ::aidl::android::media::tv::tuner::TunerFrontendSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendStatus;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3PlpSettings;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbsCodeRate;
+using ::android::hardware::tv::tuner::V1_0::FrontendEventType;
+using ::android::hardware::tv::tuner::V1_0::FrontendId;
+using ::android::hardware::tv::tuner::V1_0::FrontendScanMessage;
+using ::android::hardware::tv::tuner::V1_0::FrontendScanMessageType;
+using ::android::hardware::tv::tuner::V1_0::FrontendSettings;
+using ::android::hardware::tv::tuner::V1_0::FrontendStatus;
+using ::android::hardware::tv::tuner::V1_0::IFrontend;
+using ::android::hardware::tv::tuner::V1_1::IFrontendCallback;
+using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageExt1_1;
+using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageTypeExt1_1;
+using ::android::hardware::tv::tuner::V1_1::FrontendSettingsExt1_1;
+using ::android::hardware::tv::tuner::V1_1::FrontendStatusExt1_1;
+
+using namespace std;
+
+namespace android {
+
+class TunerFrontend : public BnTunerFrontend {
+
+public:
+ TunerFrontend(sp<IFrontend> frontend, int id);
+ virtual ~TunerFrontend();
+ Status setCallback(
+ const shared_ptr<ITunerFrontendCallback>& tunerFrontendCallback) override;
+ Status tune(const TunerFrontendSettings& settings) override;
+ Status stopTune() override;
+ Status scan(const TunerFrontendSettings& settings, int frontendScanType) override;
+ Status stopScan() override;
+ Status setLnb(const shared_ptr<ITunerLnb>& lnb) override;
+ Status setLna(bool bEnable) override;
+ Status linkCiCamToFrontend(int ciCamId, int32_t* _aidl_return) override;
+ Status unlinkCiCamToFrontend(int ciCamId) override;
+ Status close() override;
+ Status getStatus(const vector<int32_t>& statusTypes,
+ vector<TunerFrontendStatus>* _aidl_return) override;
+ Status getStatusExtended_1_1(const vector<int32_t>& statusTypes,
+ vector<TunerFrontendStatus>* _aidl_return) override;
+ Status getFrontendId(int* _aidl_return) override;
+
+ struct FrontendCallback : public IFrontendCallback {
+ FrontendCallback(const shared_ptr<ITunerFrontendCallback> tunerFrontendCallback)
+ : mTunerFrontendCallback(tunerFrontendCallback) {};
+
+ virtual Return<void> onEvent(FrontendEventType frontendEventType);
+ virtual Return<void> onScanMessage(
+ FrontendScanMessageType type, const FrontendScanMessage& message);
+ virtual Return<void> onScanMessageExt1_1(
+ FrontendScanMessageTypeExt1_1 type, const FrontendScanMessageExt1_1& message);
+
+ shared_ptr<ITunerFrontendCallback> mTunerFrontendCallback;
+ };
+
+private:
+ hidl_vec<FrontendAtsc3PlpSettings> getAtsc3PlpSettings(
+ const TunerFrontendAtsc3Settings& settings);
+ FrontendDvbsCodeRate getDvbsCodeRate(const TunerFrontendDvbsCodeRate& codeRate);
+ FrontendSettings getHidlFrontendSettings(const TunerFrontendSettings& aidlSettings);
+ FrontendSettingsExt1_1 getHidlFrontendSettingsExt(const TunerFrontendSettings& aidlSettings);
+ void getAidlFrontendStatus(
+ vector<FrontendStatus>& hidlStatus, vector<TunerFrontendStatus>& aidlStatus);
+ void getAidlFrontendStatusExt(
+ vector<FrontendStatusExt1_1>& hidlStatus, vector<TunerFrontendStatus>& aidlStatus);
+
+ int mId;
+ sp<IFrontend> mFrontend;
+ sp<::android::hardware::tv::tuner::V1_1::IFrontend> mFrontend_1_1;
+};
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_TUNERFRONTEND_H
diff --git a/services/tuner/TunerLnb.cpp b/services/tuner/TunerLnb.cpp
new file mode 100644
index 0000000..77248d4
--- /dev/null
+++ b/services/tuner/TunerLnb.cpp
@@ -0,0 +1,140 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TunerLnb"
+
+#include "TunerLnb.h"
+
+using ::android::hardware::tv::tuner::V1_0::LnbPosition;
+using ::android::hardware::tv::tuner::V1_0::LnbTone;
+using ::android::hardware::tv::tuner::V1_0::LnbVoltage;
+using ::android::hardware::tv::tuner::V1_0::Result;
+
+namespace android {
+
+TunerLnb::TunerLnb(sp<ILnb> lnb, int id) {
+ mLnb = lnb;
+ mId = id;
+}
+
+TunerLnb::~TunerLnb() {
+ mLnb = NULL;
+ mId = -1;
+}
+
+Status TunerLnb::setCallback(
+ const shared_ptr<ITunerLnbCallback>& tunerLnbCallback) {
+ if (mLnb == NULL) {
+ ALOGE("ILnb is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ if (tunerLnbCallback == NULL) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_ARGUMENT));
+ }
+
+ sp<ILnbCallback> lnbCallback = new LnbCallback(tunerLnbCallback);
+ Result status = mLnb->setCallback(lnbCallback);
+ if (status != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+ }
+ return Status::ok();
+}
+
+Status TunerLnb::setVoltage(int voltage) {
+ if (mLnb == NULL) {
+ ALOGE("ILnb is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result status = mLnb->setVoltage(static_cast<LnbVoltage>(voltage));
+ if (status != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+ }
+ return Status::ok();
+}
+
+Status TunerLnb::setTone(int tone) {
+ if (mLnb == NULL) {
+ ALOGE("ILnb is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result status = mLnb->setTone(static_cast<LnbTone>(tone));
+ if (status != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+ }
+ return Status::ok();
+}
+
+Status TunerLnb::setSatellitePosition(int position) {
+ if (mLnb == NULL) {
+ ALOGE("ILnb is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result status = mLnb->setSatellitePosition(static_cast<LnbPosition>(position));
+ if (status != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+ }
+ return Status::ok();
+}
+
+Status TunerLnb::sendDiseqcMessage(const vector<uint8_t>& diseqcMessage) {
+ if (mLnb == NULL) {
+ ALOGE("ILnb is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result status = mLnb->sendDiseqcMessage(diseqcMessage);
+ if (status != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+ }
+ return Status::ok();
+}
+
+Status TunerLnb::close() {
+ if (mLnb == NULL) {
+ ALOGE("ILnb is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res = mLnb->close();
+ mLnb = NULL;
+
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+
+/////////////// ILnbCallback ///////////////////////
+
+Return<void> TunerLnb::LnbCallback::onEvent(const LnbEventType lnbEventType) {
+ if (mTunerLnbCallback != NULL) {
+ mTunerLnbCallback->onEvent((int)lnbEventType);
+ }
+ return Void();
+}
+
+Return<void> TunerLnb::LnbCallback::onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) {
+ if (mTunerLnbCallback != NULL && diseqcMessage != NULL) {
+ vector<uint8_t> msg(begin(diseqcMessage), end(diseqcMessage));
+ mTunerLnbCallback->onDiseqcMessage(msg);
+ }
+ return Void();
+}
+} // namespace android
diff --git a/services/tuner/TunerLnb.h b/services/tuner/TunerLnb.h
new file mode 100644
index 0000000..500d072
--- /dev/null
+++ b/services/tuner/TunerLnb.h
@@ -0,0 +1,71 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TUNERFLNB_H
+#define ANDROID_MEDIA_TUNERFLNB_H
+
+#include <aidl/android/media/tv/tuner/BnTunerLnb.h>
+#include <android/hardware/tv/tuner/1.0/ILnb.h>
+#include <android/hardware/tv/tuner/1.0/ILnbCallback.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <utils/Log.h>
+
+using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::media::tv::tuner::BnTunerLnb;
+using ::aidl::android::media::tv::tuner::ITunerLnbCallback;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::tv::tuner::V1_0::ILnb;
+using ::android::hardware::tv::tuner::V1_0::ILnbCallback;
+using ::android::hardware::tv::tuner::V1_0::LnbEventType;
+
+using namespace std;
+
+namespace android {
+
+class TunerLnb : public BnTunerLnb {
+
+public:
+ TunerLnb(sp<ILnb> lnb, int id);
+ virtual ~TunerLnb();
+ Status setCallback(const shared_ptr<ITunerLnbCallback>& tunerLnbCallback) override;
+ Status setVoltage(int voltage) override;
+ Status setTone(int tone) override;
+ Status setSatellitePosition(int position) override;
+ Status sendDiseqcMessage(const vector<uint8_t>& diseqcMessage) override;
+ Status close() override;
+
+ int getId() { return mId; }
+
+ struct LnbCallback : public ILnbCallback {
+ LnbCallback(const shared_ptr<ITunerLnbCallback> tunerLnbCallback)
+ : mTunerLnbCallback(tunerLnbCallback) {};
+
+ virtual Return<void> onEvent(const LnbEventType lnbEventType);
+ virtual Return<void> onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage);
+
+ shared_ptr<ITunerLnbCallback> mTunerLnbCallback;
+ };
+
+private:
+ int mId;
+ sp<ILnb> mLnb;
+};
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_TUNERFLNB_H
diff --git a/services/tuner/TunerService.cpp b/services/tuner/TunerService.cpp
new file mode 100644
index 0000000..77e1c40
--- /dev/null
+++ b/services/tuner/TunerService.cpp
@@ -0,0 +1,541 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TunerService"
+
+#include <android/binder_manager.h>
+#include <android/content/pm/IPackageManagerNative.h>
+#include <binder/IServiceManager.h>
+#include <utils/Log.h>
+#include "TunerService.h"
+#include "TunerFrontend.h"
+#include "TunerLnb.h"
+#include "TunerDemux.h"
+#include "TunerDescrambler.h"
+
+using ::aidl::android::media::tv::tuner::TunerFrontendAnalogCapabilities;
+using ::aidl::android::media::tv::tuner::TunerFrontendAtsc3Capabilities;
+using ::aidl::android::media::tv::tuner::TunerFrontendAtscCapabilities;
+using ::aidl::android::media::tv::tuner::TunerFrontendCableCapabilities;
+using ::aidl::android::media::tv::tuner::TunerFrontendCapabilities;
+using ::aidl::android::media::tv::tuner::TunerFrontendDvbsCapabilities;
+using ::aidl::android::media::tv::tuner::TunerFrontendDvbtCapabilities;
+using ::aidl::android::media::tv::tuner::TunerFrontendIsdbs3Capabilities;
+using ::aidl::android::media::tv::tuner::TunerFrontendIsdbsCapabilities;
+using ::aidl::android::media::tv::tuner::TunerFrontendIsdbtCapabilities;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterAvSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
+using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
+using ::android::hardware::tv::tuner::V1_0::FrontendId;
+using ::android::hardware::tv::tuner::V1_0::FrontendType;
+using ::android::hardware::tv::tuner::V1_0::IFrontend;
+using ::android::hardware::tv::tuner::V1_0::ILnb;
+using ::android::hardware::tv::tuner::V1_0::LnbId;
+using ::android::hardware::tv::tuner::V1_0::Result;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbCapabilities;
+
+namespace android {
+
+TunerService::TunerService() {
+ sp<IServiceManager> serviceMgr = defaultServiceManager();
+ sp<content::pm::IPackageManagerNative> packageMgr;
+ if (serviceMgr.get() == nullptr) {
+ ALOGE("%s: Cannot find service manager", __func__);
+ return;
+ } else {
+ sp<IBinder> binder = serviceMgr->waitForService(String16("package_native"));
+ packageMgr = interface_cast<content::pm::IPackageManagerNative>(binder);
+ }
+
+ bool hasFeature = false;
+ if (packageMgr != nullptr) {
+ binder::Status status = packageMgr->hasSystemFeature(FEATURE_TUNER, 0, &hasFeature);
+ if (!status.isOk()) {
+ ALOGE("%s: hasSystemFeature failed: %s",
+ __func__, status.exceptionMessage().c_str());
+ return;
+ }
+ if (!hasFeature) {
+ ALOGD("Current device does not support tuner feaure.");
+ return;
+ }
+ } else {
+ ALOGD("%s: Cannot find package manager.", __func__);
+ return;
+ }
+
+ ::ndk::SpAIBinder binder(AServiceManager_waitForService("tv_tuner_resource_mgr"));
+ mTunerResourceManager = ITunerResourceManager::fromBinder(binder);
+ updateTunerResources();
+}
+
+TunerService::~TunerService() {}
+
+binder_status_t TunerService::instantiate() {
+ shared_ptr<TunerService> service =
+ ::ndk::SharedRefBase::make<TunerService>();
+ return AServiceManager_addService(service->asBinder().get(), getServiceName());
+}
+
+bool TunerService::hasITuner() {
+ ALOGD("hasITuner");
+ if (mTuner != nullptr) {
+ return true;
+ }
+ mTuner = ITuner::getService();
+ if (mTuner == nullptr) {
+ ALOGE("Failed to get ITuner service");
+ return false;
+ }
+ mTunerVersion = TUNER_HAL_VERSION_1_0;
+ mTuner_1_1 = ::android::hardware::tv::tuner::V1_1::ITuner::castFrom(mTuner);
+ if (mTuner_1_1 != nullptr) {
+ mTunerVersion = TUNER_HAL_VERSION_1_1;
+ } else {
+ ALOGE("Failed to get ITuner_1_1 service");
+ }
+ return true;
+}
+
+bool TunerService::hasITuner_1_1() {
+ ALOGD("hasITuner_1_1");
+ hasITuner();
+ return (mTunerVersion == TUNER_HAL_VERSION_1_1);
+}
+
+Status TunerService::openDemux(
+ int /* demuxHandle */, std::shared_ptr<ITunerDemux>* _aidl_return) {
+ ALOGD("openDemux");
+ if (!hasITuner()) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::NOT_INITIALIZED));
+ }
+ Result res;
+ uint32_t id;
+ sp<IDemux> demuxSp = nullptr;
+ shared_ptr<ITunerDemux> tunerDemux = nullptr;
+ mTuner->openDemux([&](Result r, uint32_t demuxId, const sp<IDemux>& demux) {
+ demuxSp = demux;
+ id = demuxId;
+ res = r;
+ ALOGD("open demux, id = %d", demuxId);
+ });
+ if (res == Result::SUCCESS) {
+ tunerDemux = ::ndk::SharedRefBase::make<TunerDemux>(demuxSp, id);
+ *_aidl_return = tunerDemux->ref<ITunerDemux>();
+ return Status::ok();
+ }
+
+ ALOGW("open demux failed, res = %d", res);
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+}
+
+Status TunerService::getDemuxCaps(TunerDemuxCapabilities* _aidl_return) {
+ ALOGD("getDemuxCaps");
+ if (!hasITuner()) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::NOT_INITIALIZED));
+ }
+ Result res;
+ DemuxCapabilities caps;
+ mTuner->getDemuxCaps([&](Result r, const DemuxCapabilities& demuxCaps) {
+ caps = demuxCaps;
+ res = r;
+ });
+ if (res == Result::SUCCESS) {
+ *_aidl_return = getAidlDemuxCaps(caps);
+ return Status::ok();
+ }
+
+ ALOGW("Get demux caps failed, res = %d", res);
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+}
+
+Status TunerService::getFrontendIds(vector<int32_t>* ids) {
+ if (!hasITuner()) {
+ return Status::fromServiceSpecificError(
+ static_cast<int32_t>(Result::NOT_INITIALIZED));
+ }
+ hidl_vec<FrontendId> feIds;
+ Result res = getHidlFrontendIds(feIds);
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ ids->resize(feIds.size());
+ copy(feIds.begin(), feIds.end(), ids->begin());
+
+ return Status::ok();
+}
+
+Status TunerService::getFrontendInfo(int32_t id, TunerFrontendInfo* _aidl_return) {
+ if (!hasITuner()) {
+ ALOGE("ITuner service is not init.");
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ FrontendInfo info;
+ Result res = getHidlFrontendInfo(id, info);
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+
+ TunerFrontendInfo tunerInfo = convertToAidlFrontendInfo(info);
+ *_aidl_return = tunerInfo;
+ return Status::ok();
+}
+
+Status TunerService::getFrontendDtmbCapabilities(
+ int32_t id, TunerFrontendDtmbCapabilities* _aidl_return) {
+ if (!hasITuner_1_1()) {
+ ALOGE("ITuner_1_1 service is not init.");
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res;
+ FrontendDtmbCapabilities dtmbCaps;
+ mTuner_1_1->getFrontendDtmbCapabilities(id,
+ [&](Result r, const FrontendDtmbCapabilities& caps) {
+ dtmbCaps = caps;
+ res = r;
+ });
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+
+ TunerFrontendDtmbCapabilities aidlDtmbCaps{
+ .transmissionModeCap = (int)dtmbCaps.transmissionModeCap,
+ .bandwidthCap = (int)dtmbCaps.bandwidthCap,
+ .modulationCap = (int)dtmbCaps.modulationCap,
+ .codeRateCap = (int)dtmbCaps.codeRateCap,
+ .guardIntervalCap = (int)dtmbCaps.guardIntervalCap,
+ .interleaveModeCap = (int)dtmbCaps.interleaveModeCap,
+ };
+
+ *_aidl_return = aidlDtmbCaps;
+ return Status::ok();
+}
+
+Status TunerService::openFrontend(
+ int32_t frontendHandle, shared_ptr<ITunerFrontend>* _aidl_return) {
+ if (!hasITuner()) {
+ ALOGE("ITuner service is not init.");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result status;
+ sp<IFrontend> frontend;
+ int id = getResourceIdFromHandle(frontendHandle, FRONTEND);
+ mTuner->openFrontendById(id, [&](Result result, const sp<IFrontend>& fe) {
+ frontend = fe;
+ status = result;
+ });
+ if (status != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+ }
+ *_aidl_return = ::ndk::SharedRefBase::make<TunerFrontend>(frontend, id);
+ return Status::ok();
+}
+
+Status TunerService::openLnb(int lnbHandle, shared_ptr<ITunerLnb>* _aidl_return) {
+ if (!hasITuner()) {
+ ALOGD("get ITuner failed");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result status;
+ sp<ILnb> lnb;
+ int id = getResourceIdFromHandle(lnbHandle, LNB);
+ mTuner->openLnbById(id, [&](Result result, const sp<ILnb>& lnbSp){
+ lnb = lnbSp;
+ status = result;
+ });
+ if (status != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+ }
+
+ *_aidl_return = ::ndk::SharedRefBase::make<TunerLnb>(lnb, id);
+ return Status::ok();
+}
+
+Status TunerService::openLnbByName(const string& lnbName, shared_ptr<ITunerLnb>* _aidl_return) {
+ if (!hasITuner()) {
+ ALOGE("get ITuner failed");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ int lnbId;
+ Result status;
+ sp<ILnb> lnb;
+ mTuner->openLnbByName(lnbName, [&](Result r, LnbId id, const sp<ILnb>& lnbSp) {
+ status = r;
+ lnb = lnbSp;
+ lnbId = (int)id;
+ });
+ if (status != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+ }
+
+ *_aidl_return = ::ndk::SharedRefBase::make<TunerLnb>(lnb, lnbId);
+ return Status::ok();
+}
+
+Status TunerService::openDescrambler(int32_t /*descramblerHandle*/,
+ std::shared_ptr<ITunerDescrambler>* _aidl_return) {
+ if (!hasITuner()) {
+ ALOGD("get ITuner failed");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result status;
+ sp<IDescrambler> descrambler;
+ //int id = getResourceIdFromHandle(descramblerHandle, DESCRAMBLER);
+ mTuner->openDescrambler([&](Result r, const sp<IDescrambler>& descramblerSp) {
+ status = r;
+ descrambler = descramblerSp;
+ });
+ if (status != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+ }
+
+ *_aidl_return = ::ndk::SharedRefBase::make<TunerDescrambler>(descrambler);
+ return Status::ok();
+}
+
+void TunerService::updateTunerResources() {
+ if (!hasITuner() || mTunerResourceManager == NULL) {
+ ALOGE("Failed to updateTunerResources");
+ return;
+ }
+
+ updateFrontendResources();
+ updateLnbResources();
+ // TODO: update Demux, Descrambler.
+}
+
+Status TunerService::getTunerHalVersion(int* _aidl_return) {
+ hasITuner();
+ *_aidl_return = mTunerVersion;
+ return Status::ok();
+}
+
+void TunerService::updateFrontendResources() {
+ hidl_vec<FrontendId> ids;
+ Result res = getHidlFrontendIds(ids);
+ if (res != Result::SUCCESS) {
+ return;
+ }
+ vector<TunerFrontendInfo> infos;
+ for (int i = 0; i < ids.size(); i++) {
+ FrontendInfo frontendInfo;
+ Result res = getHidlFrontendInfo((int)ids[i], frontendInfo);
+ if (res != Result::SUCCESS) {
+ continue;
+ }
+ TunerFrontendInfo tunerFrontendInfo{
+ .handle = getResourceHandleFromId((int)ids[i], FRONTEND),
+ .type = static_cast<int>(frontendInfo.type),
+ .exclusiveGroupId = static_cast<int>(frontendInfo.exclusiveGroupId),
+ };
+ infos.push_back(tunerFrontendInfo);
+ }
+ mTunerResourceManager->setFrontendInfoList(infos);
+}
+
+void TunerService::updateLnbResources() {
+ vector<int> handles = getLnbHandles();
+ if (handles.size() == 0) {
+ return;
+ }
+ mTunerResourceManager->setLnbInfoList(handles);
+}
+
+vector<int> TunerService::getLnbHandles() {
+ vector<int> lnbHandles;
+ if (mTuner != NULL) {
+ Result res;
+ vector<LnbId> lnbIds;
+ mTuner->getLnbIds([&](Result r, const hardware::hidl_vec<LnbId>& ids) {
+ lnbIds = ids;
+ res = r;
+ });
+ if (res != Result::SUCCESS || lnbIds.size() == 0) {
+ } else {
+ for (int i = 0; i < lnbIds.size(); i++) {
+ lnbHandles.push_back(getResourceHandleFromId((int)lnbIds[i], LNB));
+ }
+ }
+ }
+
+ return lnbHandles;
+}
+
+Result TunerService::getHidlFrontendIds(hidl_vec<FrontendId>& ids) {
+ if (mTuner == NULL) {
+ return Result::NOT_INITIALIZED;
+ }
+ Result res;
+ mTuner->getFrontendIds([&](Result r, const hidl_vec<FrontendId>& frontendIds) {
+ ids = frontendIds;
+ res = r;
+ });
+ return res;
+}
+
+Result TunerService::getHidlFrontendInfo(int id, FrontendInfo& info) {
+ if (mTuner == NULL) {
+ return Result::NOT_INITIALIZED;
+ }
+ Result res;
+ mTuner->getFrontendInfo(id, [&](Result r, const FrontendInfo& feInfo) {
+ info = feInfo;
+ res = r;
+ });
+ return res;
+}
+
+TunerDemuxCapabilities TunerService::getAidlDemuxCaps(DemuxCapabilities caps) {
+ TunerDemuxCapabilities aidlCaps{
+ .numDemux = (int)caps.numDemux,
+ .numRecord = (int)caps.numRecord,
+ .numPlayback = (int)caps.numPlayback,
+ .numTsFilter = (int)caps.numTsFilter,
+ .numSectionFilter = (int)caps.numSectionFilter,
+ .numAudioFilter = (int)caps.numAudioFilter,
+ .numVideoFilter = (int)caps.numVideoFilter,
+ .numPesFilter = (int)caps.numPesFilter,
+ .numPcrFilter = (int)caps.numPcrFilter,
+ .numBytesInSectionFilter = (int)caps.numBytesInSectionFilter,
+ .filterCaps = (int)caps.filterCaps,
+ .bTimeFilter = caps.bTimeFilter,
+ };
+ aidlCaps.linkCaps.resize(caps.linkCaps.size());
+ copy(caps.linkCaps.begin(), caps.linkCaps.end(), aidlCaps.linkCaps.begin());
+ return aidlCaps;
+}
+
+TunerFrontendInfo TunerService::convertToAidlFrontendInfo(FrontendInfo halInfo) {
+ TunerFrontendInfo info{
+ .type = (int)halInfo.type,
+ .minFrequency = (int)halInfo.minFrequency,
+ .maxFrequency = (int)halInfo.maxFrequency,
+ .minSymbolRate = (int)halInfo.minSymbolRate,
+ .maxSymbolRate = (int)halInfo.maxSymbolRate,
+ .acquireRange = (int)halInfo.acquireRange,
+ .exclusiveGroupId = (int)halInfo.exclusiveGroupId,
+ };
+ for (int i = 0; i < halInfo.statusCaps.size(); i++) {
+ info.statusCaps.push_back((int)halInfo.statusCaps[i]);
+ }
+
+ TunerFrontendCapabilities caps;
+ switch (halInfo.type) {
+ case FrontendType::ANALOG: {
+ TunerFrontendAnalogCapabilities analogCaps{
+ .typeCap = (int)halInfo.frontendCaps.analogCaps().typeCap,
+ .sifStandardCap = (int)halInfo.frontendCaps.analogCaps().sifStandardCap,
+ };
+ caps.set<TunerFrontendCapabilities::analogCaps>(analogCaps);
+ break;
+ }
+ case FrontendType::ATSC: {
+ TunerFrontendAtscCapabilities atscCaps{
+ .modulationCap = (int)halInfo.frontendCaps.atscCaps().modulationCap,
+ };
+ caps.set<TunerFrontendCapabilities::atscCaps>(atscCaps);
+ break;
+ }
+ case FrontendType::ATSC3: {
+ TunerFrontendAtsc3Capabilities atsc3Caps{
+ .bandwidthCap = (int)halInfo.frontendCaps.atsc3Caps().bandwidthCap,
+ .modulationCap = (int)halInfo.frontendCaps.atsc3Caps().modulationCap,
+ .timeInterleaveModeCap =
+ (int)halInfo.frontendCaps.atsc3Caps().timeInterleaveModeCap,
+ .codeRateCap = (int)halInfo.frontendCaps.atsc3Caps().codeRateCap,
+ .demodOutputFormatCap = (int)halInfo.frontendCaps.atsc3Caps().demodOutputFormatCap,
+ .fecCap = (int)halInfo.frontendCaps.atsc3Caps().fecCap,
+ };
+ caps.set<TunerFrontendCapabilities::atsc3Caps>(atsc3Caps);
+ break;
+ }
+ case FrontendType::DVBC: {
+ TunerFrontendCableCapabilities cableCaps{
+ .modulationCap = (int)halInfo.frontendCaps.dvbcCaps().modulationCap,
+ .codeRateCap = (int64_t)halInfo.frontendCaps.dvbcCaps().fecCap,
+ .annexCap = (int)halInfo.frontendCaps.dvbcCaps().annexCap,
+ };
+ caps.set<TunerFrontendCapabilities::cableCaps>(cableCaps);
+ break;
+ }
+ case FrontendType::DVBS: {
+ TunerFrontendDvbsCapabilities dvbsCaps{
+ .modulationCap = (int)halInfo.frontendCaps.dvbsCaps().modulationCap,
+ .codeRateCap = (long)halInfo.frontendCaps.dvbsCaps().innerfecCap,
+ .standard = (int)halInfo.frontendCaps.dvbsCaps().standard,
+ };
+ caps.set<TunerFrontendCapabilities::dvbsCaps>(dvbsCaps);
+ break;
+ }
+ case FrontendType::DVBT: {
+ TunerFrontendDvbtCapabilities dvbtCaps{
+ .transmissionModeCap = (int)halInfo.frontendCaps.dvbtCaps().transmissionModeCap,
+ .bandwidthCap = (int)halInfo.frontendCaps.dvbtCaps().bandwidthCap,
+ .constellationCap = (int)halInfo.frontendCaps.dvbtCaps().constellationCap,
+ .codeRateCap = (int)halInfo.frontendCaps.dvbtCaps().coderateCap,
+ .hierarchyCap = (int)halInfo.frontendCaps.dvbtCaps().hierarchyCap,
+ .guardIntervalCap = (int)halInfo.frontendCaps.dvbtCaps().guardIntervalCap,
+ .isT2Supported = (bool)halInfo.frontendCaps.dvbtCaps().isT2Supported,
+ .isMisoSupported = (bool)halInfo.frontendCaps.dvbtCaps().isMisoSupported,
+ };
+ caps.set<TunerFrontendCapabilities::dvbtCaps>(dvbtCaps);
+ break;
+ }
+ case FrontendType::ISDBS: {
+ TunerFrontendIsdbsCapabilities isdbsCaps{
+ .modulationCap = (int)halInfo.frontendCaps.isdbsCaps().modulationCap,
+ .codeRateCap = (int)halInfo.frontendCaps.isdbsCaps().coderateCap,
+ };
+ caps.set<TunerFrontendCapabilities::isdbsCaps>(isdbsCaps);
+ break;
+ }
+ case FrontendType::ISDBS3: {
+ TunerFrontendIsdbs3Capabilities isdbs3Caps{
+ .modulationCap = (int)halInfo.frontendCaps.isdbs3Caps().modulationCap,
+ .codeRateCap = (int)halInfo.frontendCaps.isdbs3Caps().coderateCap,
+ };
+ caps.set<TunerFrontendCapabilities::isdbs3Caps>(isdbs3Caps);
+ break;
+ }
+ case FrontendType::ISDBT: {
+ TunerFrontendIsdbtCapabilities isdbtCaps{
+ .modeCap = (int)halInfo.frontendCaps.isdbtCaps().modeCap,
+ .bandwidthCap = (int)halInfo.frontendCaps.isdbtCaps().bandwidthCap,
+ .modulationCap = (int)halInfo.frontendCaps.isdbtCaps().modulationCap,
+ .codeRateCap = (int)halInfo.frontendCaps.isdbtCaps().coderateCap,
+ .guardIntervalCap = (int)halInfo.frontendCaps.isdbtCaps().guardIntervalCap,
+ };
+ caps.set<TunerFrontendCapabilities::isdbtCaps>(isdbtCaps);
+ break;
+ }
+ default:
+ break;
+ }
+
+ info.caps = caps;
+ return info;
+}
+} // namespace android
diff --git a/services/tuner/TunerService.h b/services/tuner/TunerService.h
new file mode 100644
index 0000000..f8e2ee6
--- /dev/null
+++ b/services/tuner/TunerService.h
@@ -0,0 +1,157 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TUNERSERVICE_H
+#define ANDROID_MEDIA_TUNERSERVICE_H
+
+#include <aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.h>
+#include <aidl/android/media/tv/tuner/BnTunerService.h>
+#include <android/hardware/tv/tuner/1.1/ITuner.h>
+#include <fmq/AidlMessageQueue.h>
+#include <fmq/EventFlag.h>
+#include <fmq/MessageQueue.h>
+
+using ::aidl::android::hardware::common::fmq::GrantorDescriptor;
+using ::aidl::android::hardware::common::fmq::MQDescriptor;
+using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
+using ::aidl::android::media::tv::tuner::BnTunerService;
+using ::aidl::android::media::tv::tuner::ITunerDemux;
+using ::aidl::android::media::tv::tuner::ITunerDescrambler;
+using ::aidl::android::media::tv::tuner::ITunerFrontend;
+using ::aidl::android::media::tv::tuner::ITunerLnb;
+using ::aidl::android::media::tv::tuner::TunerDemuxCapabilities;
+using ::aidl::android::media::tv::tuner::TunerFrontendDtmbCapabilities;
+using ::aidl::android::media::tv::tuner::TunerFrontendInfo;
+using ::aidl::android::media::tv::tunerresourcemanager::ITunerResourceManager;
+
+using ::android::hardware::details::logError;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::EventFlag;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::MQDescriptorSync;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::tv::tuner::V1_0::DemuxCapabilities;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterAvSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterStatus;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
+using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
+using ::android::hardware::tv::tuner::V1_0::FrontendId;
+using ::android::hardware::tv::tuner::V1_0::FrontendInfo;
+using ::android::hardware::tv::tuner::V1_0::IDemux;
+using ::android::hardware::tv::tuner::V1_0::IDescrambler;
+using ::android::hardware::tv::tuner::V1_0::IFilter;
+using ::android::hardware::tv::tuner::V1_0::IFilterCallback;
+using ::android::hardware::tv::tuner::V1_0::ITuner;
+using ::android::hardware::tv::tuner::V1_0::Result;
+
+using Status = ::ndk::ScopedAStatus;
+
+using namespace std;
+
+namespace android {
+
+const static int TUNER_HAL_VERSION_UNKNOWN = 0;
+const static int TUNER_HAL_VERSION_1_0 = 1 << 16;
+const static int TUNER_HAL_VERSION_1_1 = (1 << 16) | 1;
+// System Feature defined in PackageManager
+static const ::android::String16 FEATURE_TUNER(::android::String16("android.hardware.tv.tuner"));
+
+typedef enum {
+ FRONTEND,
+ LNB,
+ DEMUX,
+ DESCRAMBLER,
+} TunerResourceType;
+
+struct FilterCallback : public IFilterCallback {
+ ~FilterCallback() {}
+ Return<void> onFilterEvent(const DemuxFilterEvent&) {
+ return Void();
+ }
+ Return<void> onFilterStatus(const DemuxFilterStatus) {
+ return Void();
+ }
+};
+
+class TunerService : public BnTunerService {
+ typedef AidlMessageQueue<int8_t, SynchronizedReadWrite> AidlMessageQueue;
+ typedef MessageQueue<uint8_t, kSynchronizedReadWrite> HidlMessageQueue;
+ typedef MQDescriptor<int8_t, SynchronizedReadWrite> AidlMQDesc;
+
+public:
+ static char const *getServiceName() { return "media.tuner"; }
+ static binder_status_t instantiate();
+ TunerService();
+ virtual ~TunerService();
+
+ Status getFrontendIds(vector<int32_t>* ids) override;
+ Status getFrontendInfo(int32_t id, TunerFrontendInfo* _aidl_return) override;
+ Status getFrontendDtmbCapabilities(
+ int32_t id, TunerFrontendDtmbCapabilities* _aidl_return) override;
+ Status openFrontend(
+ int32_t frontendHandle, shared_ptr<ITunerFrontend>* _aidl_return) override;
+ Status openLnb(int lnbHandle, shared_ptr<ITunerLnb>* _aidl_return) override;
+ Status openLnbByName(const string& lnbName, shared_ptr<ITunerLnb>* _aidl_return) override;
+ Status openDemux(int32_t demuxHandle, std::shared_ptr<ITunerDemux>* _aidl_return) override;
+ Status getDemuxCaps(TunerDemuxCapabilities* _aidl_return) override;
+ Status openDescrambler(int32_t descramblerHandle,
+ std::shared_ptr<ITunerDescrambler>* _aidl_return) override;
+ Status getTunerHalVersion(int* _aidl_return) override;
+
+ // TODO: create a map between resource id and handles.
+ static int getResourceIdFromHandle(int resourceHandle, int /*type*/) {
+ return (resourceHandle & 0x00ff0000) >> 16;
+ }
+
+ int getResourceHandleFromId(int id, int resourceType) {
+ // TODO: build up randomly generated id to handle mapping
+ return (resourceType & 0x000000ff) << 24
+ | (id << 16)
+ | (mResourceRequestCount++ & 0xffff);
+ }
+
+private:
+ bool hasITuner();
+ bool hasITuner_1_1();
+ void updateTunerResources();
+
+ void updateFrontendResources();
+ void updateLnbResources();
+ Result getHidlFrontendIds(hidl_vec<FrontendId>& ids);
+ Result getHidlFrontendInfo(int id, FrontendInfo& info);
+ vector<int> getLnbHandles();
+
+ TunerDemuxCapabilities getAidlDemuxCaps(DemuxCapabilities caps);
+ TunerFrontendInfo convertToAidlFrontendInfo(FrontendInfo halInfo);
+
+ sp<ITuner> mTuner;
+ sp<::android::hardware::tv::tuner::V1_1::ITuner> mTuner_1_1;
+
+ shared_ptr<ITunerResourceManager> mTunerResourceManager;
+ int mResourceRequestCount = 0;
+
+ int mTunerVersion = TUNER_HAL_VERSION_UNKNOWN;
+};
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_TUNERSERVICE_H
diff --git a/services/tuner/TunerTimeFilter.cpp b/services/tuner/TunerTimeFilter.cpp
new file mode 100644
index 0000000..ea9da30
--- /dev/null
+++ b/services/tuner/TunerTimeFilter.cpp
@@ -0,0 +1,114 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TunerTimeFilter"
+
+#include "TunerTimeFilter.h"
+
+using ::android::hardware::tv::tuner::V1_0::Result;
+using ::android::hardware::tv::tuner::V1_1::Constant64Bit;
+
+namespace android {
+
+TunerTimeFilter::TunerTimeFilter(sp<ITimeFilter> timeFilter) {
+ mTimeFilter = timeFilter;
+}
+
+TunerTimeFilter::~TunerTimeFilter() {
+ mTimeFilter = NULL;
+}
+
+Status TunerTimeFilter::setTimeStamp(int64_t timeStamp) {
+ if (mTimeFilter == NULL) {
+ ALOGE("ITimeFilter is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result status = mTimeFilter->setTimeStamp(timeStamp);
+ if (status != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+ }
+ return Status::ok();
+}
+
+Status TunerTimeFilter::clearTimeStamp() {
+ if (mTimeFilter == NULL) {
+ ALOGE("ITimeFilter is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result status = mTimeFilter->clearTimeStamp();
+ if (status != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+ }
+ return Status::ok();
+}
+
+Status TunerTimeFilter::getSourceTime(int64_t* _aidl_return) {
+ if (mTimeFilter == NULL) {
+ *_aidl_return = (int64_t)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
+ ALOGE("ITimeFilter is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result status;
+ mTimeFilter->getSourceTime(
+ [&](Result r, uint64_t t) {
+ status = r;
+ *_aidl_return = t;
+ });
+ if (status != Result::SUCCESS) {
+ *_aidl_return = (int64_t)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+ }
+ return Status::ok();
+}
+
+Status TunerTimeFilter::getTimeStamp(int64_t* _aidl_return) {
+ if (mTimeFilter == NULL) {
+ *_aidl_return = (int64_t)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
+ ALOGE("ITimeFilter is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result status;
+ mTimeFilter->getTimeStamp(
+ [&](Result r, uint64_t t) {
+ status = r;
+ *_aidl_return = t;
+ });
+ if (status != Result::SUCCESS) {
+ *_aidl_return = (int64_t)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
+ return Status::fromServiceSpecificError(static_cast<int32_t>(status));
+ }
+ return Status::ok();
+}
+
+Status TunerTimeFilter::close() {
+ if (mTimeFilter == NULL) {
+ ALOGE("ITimeFilter is not initialized");
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ Result res = mTimeFilter->close();
+ mTimeFilter = NULL;
+
+ if (res != Result::SUCCESS) {
+ return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+ }
+ return Status::ok();
+}
+} // namespace android
diff --git a/services/tuner/TunerTimeFilter.h b/services/tuner/TunerTimeFilter.h
new file mode 100644
index 0000000..d675319
--- /dev/null
+++ b/services/tuner/TunerTimeFilter.h
@@ -0,0 +1,54 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_TUNERFTIMEFILTER_H
+#define ANDROID_MEDIA_TUNERFTIMEFILTER_H
+
+#include <aidl/android/media/tv/tuner/BnTunerTimeFilter.h>
+#include <android/hardware/tv/tuner/1.0/ITimeFilter.h>
+#include <android/hardware/tv/tuner/1.1/types.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <utils/Log.h>
+
+using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::media::tv::tuner::BnTunerTimeFilter;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::tv::tuner::V1_0::ITimeFilter;
+
+using namespace std;
+
+namespace android {
+
+class TunerTimeFilter : public BnTunerTimeFilter {
+
+public:
+ TunerTimeFilter(sp<ITimeFilter> timeFilter);
+ virtual ~TunerTimeFilter();
+ Status setTimeStamp(int64_t timeStamp) override;
+ Status clearTimeStamp() override;
+ Status getSourceTime(int64_t* _aidl_return) override;
+ Status getTimeStamp(int64_t* _aidl_return) override;
+ Status close() override;
+
+private:
+ sp<ITimeFilter> mTimeFilter;
+};
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_TUNERFTIMEFILTER_H
diff --git a/services/tuner/aidl/android/media/tv/OWNERS b/services/tuner/aidl/android/media/tv/OWNERS
new file mode 100644
index 0000000..0ceb8e8
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/OWNERS
@@ -0,0 +1,2 @@
+nchalko@google.com
+quxiangfang@google.com
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerDemux.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerDemux.aidl
new file mode 100644
index 0000000..73b00ae
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerDemux.aidl
@@ -0,0 +1,78 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.ITunerDvr;
+import android.media.tv.tuner.ITunerDvrCallback;
+import android.media.tv.tuner.ITunerFilter;
+import android.media.tv.tuner.ITunerFilterCallback;
+import android.media.tv.tuner.ITunerFrontend;
+import android.media.tv.tuner.ITunerTimeFilter;
+
+/**
+ * Tuner Demux interface handles tuner related operations.
+ *
+ * {@hide}
+ */
+interface ITunerDemux {
+
+ /**
+ * Set a frontend resource as data input of the demux
+ */
+ void setFrontendDataSource(in ITunerFrontend frontend);
+
+ /**
+ * Open a new filter in the demux
+ */
+ ITunerFilter openFilter(
+ in int mainType, in int subtype, in int bufferSize, in ITunerFilterCallback cb);
+
+ /**
+ * Open time filter of the demux.
+ */
+ ITunerTimeFilter openTimeFilter();
+
+ /**
+ * Get hardware sync ID for audio and video.
+ */
+ int getAvSyncHwId(ITunerFilter tunerFilter);
+
+ /**
+ * Get current time stamp to use for A/V sync.
+ */
+ long getAvSyncTime(in int avSyncHwId);
+
+ /**
+ * Open a DVR (Digital Video Record) instance in the demux.
+ */
+ ITunerDvr openDvr(in int dvbType, in int bufferSize, in ITunerDvrCallback cb);
+
+ /**
+ * Connect Conditional Access Modules (CAM) through Common Interface (CI).
+ */
+ void connectCiCam(in int ciCamId);
+
+ /**
+ * Disconnect Conditional Access Modules (CAM).
+ */
+ void disconnectCiCam();
+
+ /**
+ * Releases the ITunerDemux instance.
+ */
+ void close();
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerDescrambler.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerDescrambler.aidl
new file mode 100644
index 0000000..7370eee
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerDescrambler.aidl
@@ -0,0 +1,53 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.ITunerDemux;
+import android.media.tv.tuner.ITunerFilter;
+import android.media.tv.tuner.TunerDemuxPid;
+
+/**
+ * Tuner Demux interface handles tuner related operations.
+ *
+ * {@hide}
+ */
+interface ITunerDescrambler {
+ /**
+ * Set a demux as source of the descrambler.
+ */
+ void setDemuxSource(in ITunerDemux tunerDemux);
+
+ /**
+ * Set a key token to link descrambler to a key slot.
+ */
+ void setKeyToken(in byte[] keyToken);
+
+ /**
+ * Add packets' PID to the descrambler for descrambling.
+ */
+ void addPid(in TunerDemuxPid pid, in ITunerFilter optionalSourceFilter);
+
+ /**
+ * Remove packets' PID from the descrambler.
+ */
+ void removePid(in TunerDemuxPid pid, in ITunerFilter optionalSourceFilter);
+
+ /**
+ * Close a new interface of ITunerDescrambler.
+ */
+ void close();
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerDvr.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerDvr.aidl
new file mode 100644
index 0000000..8f1601b
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerDvr.aidl
@@ -0,0 +1,69 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.hardware.common.fmq.MQDescriptor;
+import android.hardware.common.fmq.SynchronizedReadWrite;
+import android.media.tv.tuner.ITunerFilter;
+import android.media.tv.tuner.TunerDvrSettings;
+
+/**
+ * Tuner Dvr interface handles tuner related operations.
+ *
+ * {@hide}
+ */
+interface ITunerDvr {
+ /**
+ * Get the descriptor of the DVR's FMQ.
+ */
+ MQDescriptor<byte, SynchronizedReadWrite> getQueueDesc();
+
+ /**
+ * Configure the DVR.
+ */
+ void configure(in TunerDvrSettings settings);
+
+ /**
+ * Attach one filter to DVR interface for recording.
+ */
+ void attachFilter(in ITunerFilter filter);
+
+ /**
+ * Detach one filter from the DVR's recording.
+ */
+ void detachFilter(in ITunerFilter filter);
+
+ /**
+ * Start DVR.
+ */
+ void start();
+
+ /**
+ * Stop DVR.
+ */
+ void stop();
+
+ /**
+ * Flush DVR data.
+ */
+ void flush();
+
+ /**
+ * close the DVR instance to release resource for DVR.
+ */
+ void close();
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerDvrCallback.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerDvrCallback.aidl
new file mode 100644
index 0000000..e234fe5
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerDvrCallback.aidl
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * TunerDvrCallback interface handles tuner dvr related callbacks.
+ *
+ * {@hide}
+ */
+interface ITunerDvrCallback {
+ /**
+ * Notify the client a new status of the demux's record.
+ */
+ void onRecordStatus(in int status);
+
+ /**
+ * Notify the client a new status of the demux's playback.
+ */
+ void onPlaybackStatus(in int status);
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerFilter.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerFilter.aidl
new file mode 100644
index 0000000..10d4c3b
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerFilter.aidl
@@ -0,0 +1,100 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.hardware.common.fmq.MQDescriptor;
+import android.hardware.common.fmq.SynchronizedReadWrite;
+import android.hardware.common.NativeHandle;
+import android.media.tv.tuner.TunerFilterConfiguration;
+import android.media.tv.tuner.TunerFilterSharedHandleInfo;
+
+/**
+ * Tuner Filter interface handles tuner related operations.
+ *
+ * {@hide}
+ */
+interface ITunerFilter {
+ /**
+ * Get the filter Id.
+ */
+ int getId();
+
+ /**
+ * Get the 64-bit filter Id.
+ */
+ long getId64Bit();
+
+ /**
+ * Get the descriptor of the Filter's FMQ.
+ */
+ MQDescriptor<byte, SynchronizedReadWrite> getQueueDesc();
+
+ /**
+ * Configure the filter.
+ */
+ void configure(in TunerFilterConfiguration config);
+
+ /**
+ * Configure the monitor event of the Filter.
+ */
+ void configureMonitorEvent(in int monitorEventType);
+
+ /**
+ * Configure the context id of the IP Filter.
+ */
+ void configureIpFilterContextId(in int cid);
+
+ /**
+ * Configure the stream type of the media Filter.
+ */
+ void configureAvStreamType(in int avStreamType);
+
+ /**
+ * Get the a/v shared memory handle
+ */
+ TunerFilterSharedHandleInfo getAvSharedHandleInfo();
+
+ /**
+ * Release the handle reported by the HAL for AV memory.
+ */
+ void releaseAvHandle(in NativeHandle handle, in long avDataId);
+
+ /**
+ * Set the filter's data source.
+ */
+ void setDataSource(ITunerFilter filter);
+
+ /**
+ * Start the filter.
+ */
+ void start();
+
+ /**
+ * Stop the filter.
+ */
+ void stop();
+
+ /**
+ * Flush the filter.
+ */
+ void flush();
+
+ /**
+ * Close the filter.
+ */
+ void close();
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerFilterCallback.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerFilterCallback.aidl
new file mode 100644
index 0000000..e7a52a7
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerFilterCallback.aidl
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerFilterEvent;
+
+/**
+ * TunerFilterCallback interface handles tuner filter related callbacks.
+ *
+ * {@hide}
+ */
+interface ITunerFilterCallback {
+ /**
+ * Notify the client a new status of a filter.
+ */
+ void onFilterStatus(int status);
+
+ /**
+ * Notify the client that a new filter event happened.
+ */
+ void onFilterEvent(in TunerFilterEvent[] filterEvent);
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerFrontend.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerFrontend.aidl
new file mode 100644
index 0000000..ef0255a
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerFrontend.aidl
@@ -0,0 +1,107 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.ITunerFrontendCallback;
+import android.media.tv.tuner.ITunerLnb;
+import android.media.tv.tuner.TunerFrontendSettings;
+import android.media.tv.tuner.TunerFrontendStatus;
+
+/**
+ * Tuner Frontend interface handles frontend related operations.
+ *
+ * {@hide}
+ */
+interface ITunerFrontend {
+ /**
+ * Set the frontend callback.
+ *
+ * @param tunerFrontendCallback the callback to receive frontend related info.
+ */
+ void setCallback(in ITunerFrontendCallback tunerFrontendCallback);
+
+ /**
+ * Tunes the frontend to using the settings given.
+ *
+ * @param settings the settings to tune with.
+ */
+ void tune(in TunerFrontendSettings settings);
+
+ /**
+ * Stop the previous tuning.
+ */
+ void stopTune();
+
+ /**
+ * Scan the frontend to use the settings given.
+ *
+ * @param settings the settings to scan with.
+ * @param frontendScanType scan with given type.
+ */
+ void scan(in TunerFrontendSettings settings, in int frontendScanType);
+
+ /**
+ * Stop the previous scanning.
+ */
+ void stopScan();
+
+ /**
+ * Sets Low-Noise Block downconverter (LNB) for satellite frontend.
+ *
+ * @param tuner lnb interface.
+ */
+ void setLnb(in ITunerLnb lnb);
+
+ /**
+ * Enable or Disable Low Noise Amplifier (LNA).
+ *
+ * @param bEnable enable Lna or not.
+ */
+ void setLna(in boolean bEnable);
+
+ /**
+ * Link Frontend to the cicam with given id.
+ *
+ * @return lts id
+ */
+ int linkCiCamToFrontend(in int ciCamId);
+
+ /**
+ * Unink Frontend to the cicam with given id.
+ */
+ void unlinkCiCamToFrontend(in int ciCamId);
+
+ /**
+ * Releases the ITunerFrontend instance.
+ */
+ void close();
+
+ /**
+ * Gets the statuses of the frontend.
+ */
+ TunerFrontendStatus[] getStatus(in int[] statusTypes);
+
+ /**
+ * Gets the 1.1 extended statuses of the frontend.
+ */
+ TunerFrontendStatus[] getStatusExtended_1_1(in int[] statusTypes);
+
+ /**
+ * Gets the id of the frontend.
+ */
+ int getFrontendId();
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerFrontendCallback.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerFrontendCallback.aidl
new file mode 100644
index 0000000..c92f5ee
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerFrontendCallback.aidl
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerFrontendScanMessage;
+
+/**
+ * TunerFrontendCallback interface handles tuner frontend related callbacks.
+ *
+ * {@hide}
+ */
+interface ITunerFrontendCallback {
+ /**
+ * Notify the client that a new event happened on the frontend.
+ */
+ void onEvent(in int frontendEventType);
+
+ /**
+ * notify the client of scan messages.
+ */
+ void onScanMessage(in int messageType, in TunerFrontendScanMessage message);
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerLnb.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerLnb.aidl
new file mode 100644
index 0000000..d62145e
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerLnb.aidl
@@ -0,0 +1,56 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.ITunerLnbCallback;
+
+/**
+ * Tuner Lnb interface handles Lnb related operations.
+ *
+ * {@hide}
+ */
+interface ITunerLnb {
+ /**
+ * Set the lnb callback.
+ */
+ void setCallback(in ITunerLnbCallback tunerLnbCallback);
+
+ /**
+ * Set the lnb's power voltage.
+ */
+ void setVoltage(in int voltage);
+
+ /**
+ * Set the lnb's tone mode.
+ */
+ void setTone(in int tone);
+
+ /**
+ * Select the lnb's position.
+ */
+ void setSatellitePosition(in int position);
+
+ /**
+ * Sends DiSEqC (Digital Satellite Equipment Control) message.
+ */
+ void sendDiseqcMessage(in byte[] diseqcMessage);
+
+ /**
+ * Releases the LNB instance.
+ */
+ void close();
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerLnbCallback.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerLnbCallback.aidl
new file mode 100644
index 0000000..117352f
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerLnbCallback.aidl
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * TuneLnbCallback interface handles tuner lnb related callbacks.
+ *
+ * {@hide}
+ */
+interface ITunerLnbCallback {
+ /**
+ * Notify the client that a new event happened on the Lnb.
+ */
+ void onEvent(in int lnbEventType);
+
+ /**
+ * notify the client of new DiSEqC message.
+ */
+ void onDiseqcMessage(in byte[] diseqcMessage);
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerService.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerService.aidl
new file mode 100644
index 0000000..755b152
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerService.aidl
@@ -0,0 +1,105 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.hardware.common.fmq.MQDescriptor;
+import android.hardware.common.fmq.SynchronizedReadWrite;
+import android.hardware.common.fmq.UnsynchronizedWrite;
+import android.media.tv.tuner.ITunerDemux;
+import android.media.tv.tuner.ITunerDescrambler;
+import android.media.tv.tuner.ITunerFrontend;
+import android.media.tv.tuner.ITunerLnb;
+import android.media.tv.tuner.TunerDemuxCapabilities;
+import android.media.tv.tuner.TunerFrontendDtmbCapabilities;
+import android.media.tv.tuner.TunerFrontendInfo;
+
+/**
+ * TunerService interface handles tuner related operations.
+ *
+ * {@hide}
+ */
+//@VintfStability
+interface ITunerService {
+
+ /**
+ * Gets frontend IDs.
+ */
+ void getFrontendIds(out int[] ids);
+
+ /**
+ * Retrieve the frontend's information.
+ *
+ * @param frontendHandle the handle of the frontend granted by TRM.
+ * @return the information of the frontend.
+ */
+ TunerFrontendInfo getFrontendInfo(in int frontendHandle);
+
+ /**
+ * Get Dtmb Frontend Capabilities.
+ */
+ TunerFrontendDtmbCapabilities getFrontendDtmbCapabilities(in int id);
+
+ /**
+ * Open a Tuner Frontend interface.
+ *
+ * @param frontendHandle the handle of the frontend granted by TRM.
+ * @return the aidl interface of the frontend.
+ */
+ ITunerFrontend openFrontend(in int frontendHandle);
+
+ /**
+ * Open a new interface of ITunerLnb given a lnbHandle.
+ *
+ * @param lnbHandle the handle of the LNB granted by TRM.
+ * @return a newly created ITunerLnb interface.
+ */
+ ITunerLnb openLnb(in int lnbHandle);
+
+ /**
+ * Open a new interface of ITunerLnb given a LNB name.
+ *
+ * @param lnbName the name for an external LNB to be opened.
+ * @return a newly created ITunerLnb interface.
+ */
+ ITunerLnb openLnbByName(in String lnbName);
+
+ /**
+ * Create a new instance of Demux.
+ */
+ ITunerDemux openDemux(in int demuxHandle);
+
+ /**
+ * Retrieve the Tuner Demux capabilities.
+ *
+ * @return the demux’s capabilities.
+ */
+ TunerDemuxCapabilities getDemuxCaps();
+
+ /* Open a new interface of ITunerDescrambler given a descramblerHandle.
+ *
+ * @param descramblerHandle the handle of the descrambler granted by TRM.
+ * @return a newly created ITunerDescrambler interface.
+ */
+ ITunerDescrambler openDescrambler(in int descramblerHandle);
+
+ /**
+ * Get an integer that carries the Tuner HIDL version. The high 16 bits are the
+ * major version number while the low 16 bits are the minor version. Default
+ * value is unknown version 0.
+ */
+ int getTunerHalVersion();
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerTimeFilter.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerTimeFilter.aidl
new file mode 100644
index 0000000..f84b9bf
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerTimeFilter.aidl
@@ -0,0 +1,49 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Tuner Time Filter interface handles time filter related operations.
+ *
+ * {@hide}
+ */
+interface ITunerTimeFilter {
+ /**
+ * Set time stamp for time based filter.
+ */
+ void setTimeStamp(in long timeStamp);
+
+ /**
+ * Clear the time stamp in the time filter.
+ */
+ void clearTimeStamp();
+
+ /**
+ * Get the time from the beginning of current data source.
+ */
+ long getSourceTime();
+
+ /**
+ * Get the current time in the time filter.
+ */
+ long getTimeStamp();
+
+ /**
+ * Close the Time Filter instance.
+ */
+ void close();
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerAudioExtraMetaData.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerAudioExtraMetaData.aidl
new file mode 100644
index 0000000..df3374a
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerAudioExtraMetaData.aidl
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Extra Meta Data from AD (Audio Descriptor) according to ETSI TS 101 154 V2.1.1.
+ *
+ * {@hide}
+ */
+parcelable TunerAudioExtraMetaData {
+ byte adFade;
+
+ byte adPan;
+
+ byte versionTextTag;
+
+ byte adGainCenter;
+
+ byte adGainFront;
+
+ byte adGainSurround;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerDemuxCapabilities.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerDemuxCapabilities.aidl
new file mode 100644
index 0000000..71ab151
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerDemuxCapabilities.aidl
@@ -0,0 +1,50 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Tuner Demux capabilities interface.
+ *
+ * {@hide}
+ */
+parcelable TunerDemuxCapabilities {
+ int numDemux;
+
+ int numRecord;
+
+ int numPlayback;
+
+ int numTsFilter;
+
+ int numSectionFilter;
+
+ int numAudioFilter;
+
+ int numVideoFilter;
+
+ int numPesFilter;
+
+ int numPcrFilter;
+
+ int numBytesInSectionFilter;
+
+ int filterCaps;
+
+ int[] linkCaps;
+
+ boolean bTimeFilter;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerDemuxIpAddress.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerDemuxIpAddress.aidl
new file mode 100644
index 0000000..b65f404
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerDemuxIpAddress.aidl
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Demux ip address configure.
+ *
+ * {@hide}
+ */
+parcelable TunerDemuxIpAddress {
+ boolean isIpV6;
+
+ byte[] addr;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerDemuxIpAddressSettings.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerDemuxIpAddressSettings.aidl
new file mode 100644
index 0000000..b244388
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerDemuxIpAddressSettings.aidl
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerDemuxIpAddress;
+
+/**
+ * Filter Settings for an Ip filter.
+ *
+ * {@hide}
+ */
+parcelable TunerDemuxIpAddressSettings {
+ TunerDemuxIpAddress srcIpAddress;
+
+ TunerDemuxIpAddress dstIpAddress;
+
+ char srcPort;
+
+ char dstPort;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerDemuxPid.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerDemuxPid.aidl
new file mode 100644
index 0000000..8b238b6
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerDemuxPid.aidl
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Demux PID interface.
+ *
+ * {@hide}
+ */
+union TunerDemuxPid {
+ char tPid;
+
+ char mmtpPid;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerDvrSettings.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerDvrSettings.aidl
new file mode 100644
index 0000000..4ec4d75
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerDvrSettings.aidl
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Dvr Settings interface.
+ *
+ * {@hide}
+ */
+parcelable TunerDvrSettings {
+ int statusMask;
+
+ int lowThreshold;
+
+ int highThreshold;
+
+ int dataFormat;
+
+ int packetSize;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterAlpConfiguration.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterAlpConfiguration.aidl
new file mode 100644
index 0000000..4c9e3af
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterAlpConfiguration.aidl
@@ -0,0 +1,32 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerFilterSettings;
+
+/**
+ * Filter Settings for an ALP filter.
+ *
+ * {@hide}
+ */
+parcelable TunerFilterAlpConfiguration {
+ byte packetType;
+
+ byte lengthType;
+
+ TunerFilterSettings filterSettings;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterAvSettings.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterAvSettings.aidl
new file mode 100644
index 0000000..6bf88f0
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterAvSettings.aidl
@@ -0,0 +1,29 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Filter Settings for a Video and Audio.
+ *
+ * {@hide}
+ */
+parcelable TunerFilterAvSettings {
+ /**
+ * true if the filter output goes to decoder directly in pass through mode.
+ */
+ boolean isPassthrough;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterConfiguration.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterConfiguration.aidl
new file mode 100644
index 0000000..808cfd1
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterConfiguration.aidl
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerFilterAlpConfiguration;
+import android.media.tv.tuner.TunerFilterIpConfiguration;
+import android.media.tv.tuner.TunerFilterMmtpConfiguration;
+import android.media.tv.tuner.TunerFilterTlvConfiguration;
+import android.media.tv.tuner.TunerFilterTsConfiguration;
+
+/**
+ * Filter configuration.
+ *
+ * {@hide}
+ */
+union TunerFilterConfiguration {
+ TunerFilterTsConfiguration ts;
+
+ TunerFilterMmtpConfiguration mmtp;
+
+ TunerFilterIpConfiguration ip;
+
+ TunerFilterTlvConfiguration tlv;
+
+ TunerFilterAlpConfiguration alp;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterDownloadEvent.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterDownloadEvent.aidl
new file mode 100644
index 0000000..b971dd3
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterDownloadEvent.aidl
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Filter Event for Download data.
+ *
+ * {@hide}
+ */
+parcelable TunerFilterDownloadEvent {
+ int itemId;
+
+ /**
+ * MPU sequence number of filtered data (only for MMTP)
+ */
+ int mpuSequenceNumber;
+
+ int itemFragmentIndex;
+
+ int lastItemFragmentIndex;
+
+ /**
+ * Data size in bytes of filtered data
+ */
+ char dataLength;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterDownloadSettings.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterDownloadSettings.aidl
new file mode 100644
index 0000000..417a5fe
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterDownloadSettings.aidl
@@ -0,0 +1,26 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Filter Settings for downloading.
+ *
+ * {@hide}
+ */
+parcelable TunerFilterDownloadSettings {
+ int downloadId;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterEvent.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterEvent.aidl
new file mode 100644
index 0000000..1305510
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterEvent.aidl
@@ -0,0 +1,54 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerFilterDownloadEvent;
+import android.media.tv.tuner.TunerFilterIpPayloadEvent;
+import android.media.tv.tuner.TunerFilterMediaEvent;
+import android.media.tv.tuner.TunerFilterMmtpRecordEvent;
+import android.media.tv.tuner.TunerFilterMonitorEvent;
+import android.media.tv.tuner.TunerFilterPesEvent;
+import android.media.tv.tuner.TunerFilterSectionEvent;
+import android.media.tv.tuner.TunerFilterTemiEvent;
+import android.media.tv.tuner.TunerFilterTsRecordEvent;
+
+/**
+ * Filter events.
+ *
+ * {@hide}
+ */
+union TunerFilterEvent {
+ TunerFilterMediaEvent media;
+
+ TunerFilterSectionEvent section;
+
+ TunerFilterPesEvent pes;
+
+ TunerFilterTsRecordEvent tsRecord;
+
+ TunerFilterMmtpRecordEvent mmtpRecord;
+
+ TunerFilterDownloadEvent download;
+
+ TunerFilterIpPayloadEvent ipPayload;
+
+ TunerFilterTemiEvent temi;
+
+ TunerFilterMonitorEvent monitor;
+
+ int startId;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterIpConfiguration.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterIpConfiguration.aidl
new file mode 100644
index 0000000..8b4d889
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterIpConfiguration.aidl
@@ -0,0 +1,31 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerDemuxIpAddressSettings;
+import android.media.tv.tuner.TunerFilterSettings;
+
+/**
+ * Filter Settings for a ip filter.
+ *
+ * {@hide}
+ */
+parcelable TunerFilterIpConfiguration {
+ TunerDemuxIpAddressSettings ipAddr;
+
+ TunerFilterSettings filterSettings;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterIpPayloadEvent.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterIpPayloadEvent.aidl
new file mode 100644
index 0000000..d5bda93
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterIpPayloadEvent.aidl
@@ -0,0 +1,29 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Filter Event for IP payload data.
+ *
+ * {@hide}
+ */
+parcelable TunerFilterIpPayloadEvent {
+ /**
+ * Data size in bytes of ip data
+ */
+ char dataLength;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterMediaEvent.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterMediaEvent.aidl
new file mode 100644
index 0000000..c3dbce9
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterMediaEvent.aidl
@@ -0,0 +1,84 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.hardware.common.NativeHandle;
+import android.media.tv.tuner.TunerAudioExtraMetaData;
+
+/**
+ * Filter Event for Audio or Video Filter.
+ *
+ * {@hide}
+ */
+parcelable TunerFilterMediaEvent {
+ char streamId;
+
+ /**
+ * true if PTS is present in PES header.
+ */
+ boolean isPtsPresent;
+
+ /**
+ * Presentation Time Stamp for audio or video frame. It based on 90KHz has
+ * the same format as PTS (Presentation Time Stamp).
+ */
+ long pts;
+
+ /**
+ * Data size in bytes of audio or video frame
+ */
+ int dataLength;
+
+ /**
+ * The offset in the memory block which is shared among multiple
+ * MediaEvents.
+ */
+ int offset;
+
+ /**
+ * A handle associated to the memory where audio or video data stays.
+ */
+ NativeHandle avMemory;
+
+ /**
+ * True if the avMemory is in secure area, and isn't mappable.
+ */
+ boolean isSecureMemory;
+
+ /**
+ * An Id is used by HAL to provide additional information for AV data.
+ * For secure audio, it's the audio handle used by Audio Track.
+ */
+ long avDataId;
+
+ /**
+ * MPU sequence number of filtered data (only for MMTP)
+ */
+ int mpuSequenceNumber;
+
+ boolean isPesPrivateData;
+
+ /**
+ * If TunerAudioExtraMetaData field is valid or not
+ */
+ boolean isAudioExtraMetaData;
+
+ /**
+ * Only valid when isAudioExtraMetaData is true
+ */
+ TunerAudioExtraMetaData audio;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterMmtpConfiguration.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterMmtpConfiguration.aidl
new file mode 100644
index 0000000..162ca8e
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterMmtpConfiguration.aidl
@@ -0,0 +1,30 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerFilterSettings;
+
+/**
+ * Filter Settings for an mmtp filter.
+ *
+ * {@hide}
+ */
+parcelable TunerFilterMmtpConfiguration {
+ char mmtpPid;
+
+ TunerFilterSettings filterSettings;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterMmtpRecordEvent.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterMmtpRecordEvent.aidl
new file mode 100644
index 0000000..b8871cf
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterMmtpRecordEvent.aidl
@@ -0,0 +1,57 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Filter Event for an MMTP Record Filter.
+ *
+ * {@hide}
+ */
+parcelable TunerFilterMmtpRecordEvent {
+ int scHevcIndexMask;
+
+ /**
+ * Byte number from beginning of the filter's output
+ */
+ long byteNumber;
+
+ /**
+ * If the current event contains extended information or not
+ */
+ boolean isExtended;
+
+ /**
+ * The Presentation Time Stamp(PTS) for the audio or video frame. It is based on 90KHz
+ * and has the same format as the PTS in ISO/IEC 13818-1.
+ */
+ long pts;
+
+ /**
+ * MPU sequence number of the filtered data. This is only used for MMTP.
+ */
+ int mpuSequenceNumber;
+
+ /**
+ * Specifies the address of the first macroblock in the slice defined in ITU-T Rec. H.264.
+ */
+ int firstMbInSlice;
+
+ /**
+ * TS index mask.
+ */
+ int tsIndexMask;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterMonitorEvent.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterMonitorEvent.aidl
new file mode 100644
index 0000000..31ab5e6
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterMonitorEvent.aidl
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Filter monitor events.
+ *
+ * {@hide}
+ */
+union TunerFilterMonitorEvent {
+ /**
+ * New scrambling status.
+ */
+ int scramblingStatus;
+
+ /**
+ * New cid for the IP filter.
+ */
+ int cid;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterPesDataSettings.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterPesDataSettings.aidl
new file mode 100644
index 0000000..312f314
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterPesDataSettings.aidl
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Filter Settings for Pes Data.
+ *
+ * {@hide}
+ */
+parcelable TunerFilterPesDataSettings {
+ char streamId;
+
+ boolean isRaw;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterPesEvent.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterPesEvent.aidl
new file mode 100644
index 0000000..dc1ecc6
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterPesEvent.aidl
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Filter Event for PES Filter.
+ *
+ * {@hide}
+ */
+parcelable TunerFilterPesEvent {
+ char streamId;
+
+ /**
+ * Data size in bytes of PES data
+ */
+ char dataLength;
+
+ /**
+ * MPU sequence number of filtered data
+ */
+ int mpuSequenceNumber;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterRecordSettings.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterRecordSettings.aidl
new file mode 100644
index 0000000..29be624
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterRecordSettings.aidl
@@ -0,0 +1,32 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerFilterScIndexMask;
+
+/**
+ * Filter Settings for recording.
+ *
+ * {@hide}
+ */
+parcelable TunerFilterRecordSettings {
+ int tsIndexMask;
+
+ int scIndexType;
+
+ TunerFilterScIndexMask scIndexMask;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterScIndexMask.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterScIndexMask.aidl
new file mode 100644
index 0000000..ed37fce
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterScIndexMask.aidl
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Filter SC Index Mask
+ *
+ * {@hide}
+ */
+union TunerFilterScIndexMask {
+ int sc;
+
+ int scHevc;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterSectionBits.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterSectionBits.aidl
new file mode 100644
index 0000000..dd4f842
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterSectionBits.aidl
@@ -0,0 +1,30 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Bits settings of a section Filter.
+ *
+ * {@hide}
+ */
+parcelable TunerFilterSectionBits {
+ byte[] filter;
+
+ byte[] mask;
+
+ byte[] mode;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterSectionCondition.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterSectionCondition.aidl
new file mode 100644
index 0000000..00aabe4
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterSectionCondition.aidl
@@ -0,0 +1,31 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerFilterSectionBits;
+import android.media.tv.tuner.TunerFilterSectionTableInfo;
+
+/**
+ * Section filter condition settings.
+ *
+ * {@hide}
+ */
+union TunerFilterSectionCondition {
+ TunerFilterSectionBits sectionBits;
+
+ TunerFilterSectionTableInfo tableInfo;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterSectionEvent.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterSectionEvent.aidl
new file mode 100644
index 0000000..5f20926
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterSectionEvent.aidl
@@ -0,0 +1,44 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Filter Event for Section Filter.
+ *
+ * {@hide}
+ */
+parcelable TunerFilterSectionEvent {
+ /**
+ * Table ID of filtered data
+ */
+ char tableId;
+
+ /**
+ * Version number of filtered data
+ */
+ char version;
+
+ /**
+ * Section number of filtered data
+ */
+ char sectionNum;
+
+ /**
+ * Data size in bytes of filtered data
+ */
+ char dataLength;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterSectionSettings.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterSectionSettings.aidl
new file mode 100644
index 0000000..22129b6
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterSectionSettings.aidl
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerFilterSectionCondition;
+
+/**
+ * Filter Settings for a section filter.
+ *
+ * {@hide}
+ */
+parcelable TunerFilterSectionSettings {
+ TunerFilterSectionCondition condition;
+
+ boolean isCheckCrc;
+
+ boolean isRepeat;
+
+ boolean isRaw;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterSectionTableInfo.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterSectionTableInfo.aidl
new file mode 100644
index 0000000..cc78c9d
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterSectionTableInfo.aidl
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Table info settings of a section Filter.
+ *
+ * {@hide}
+ */
+parcelable TunerFilterSectionTableInfo {
+ char tableId;
+
+ char version;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterSettings.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterSettings.aidl
new file mode 100644
index 0000000..eb7eaa5
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterSettings.aidl
@@ -0,0 +1,44 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerFilterAvSettings;
+import android.media.tv.tuner.TunerFilterDownloadSettings;
+import android.media.tv.tuner.TunerFilterPesDataSettings;
+import android.media.tv.tuner.TunerFilterRecordSettings;
+import android.media.tv.tuner.TunerFilterSectionSettings;
+
+/**
+ * Filter Settings.
+ *
+ * {@hide}
+ */
+union TunerFilterSettings {
+ boolean nothing;
+
+ TunerFilterAvSettings av;
+
+ TunerFilterSectionSettings section;
+
+ TunerFilterPesDataSettings pesData;
+
+ TunerFilterRecordSettings record;
+
+ TunerFilterDownloadSettings download;
+
+ boolean isPassthrough;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterSharedHandleInfo.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterSharedHandleInfo.aidl
new file mode 100644
index 0000000..122dfc3
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterSharedHandleInfo.aidl
@@ -0,0 +1,29 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.hardware.common.NativeHandle;
+
+/**
+ * Filter Shared Handle Information.
+ *
+ * {@hide}
+ */
+parcelable TunerFilterSharedHandleInfo {
+ NativeHandle handle;
+ long size;
+}
\ No newline at end of file
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterTemiEvent.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterTemiEvent.aidl
new file mode 100644
index 0000000..4c4e993
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterTemiEvent.aidl
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Filter Event for Timed External Media Information (TEMI) data.
+ *
+ * {@hide}
+ */
+parcelable TunerFilterTemiEvent {
+ /**
+ * Presentation Time Stamp for audio or video frame. It based on 90KHz has
+ * the same format as PTS (Presentation Time Stamp) in ISO/IEC 13818-1.
+ */
+ long pts;
+
+ /**
+ * TEMI Descriptor Tag
+ */
+ byte descrTag;
+
+ /**
+ * TEMI Descriptor
+ */
+ byte[] descrData;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterTlvConfiguration.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterTlvConfiguration.aidl
new file mode 100644
index 0000000..0b237b4
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterTlvConfiguration.aidl
@@ -0,0 +1,32 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerFilterSettings;
+
+/**
+ * Filter Settings for a tlv filter.
+ *
+ * {@hide}
+ */
+parcelable TunerFilterTlvConfiguration {
+ byte packetType;
+
+ boolean isCompressedIpPacket;
+
+ TunerFilterSettings filterSettings;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterTsConfiguration.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterTsConfiguration.aidl
new file mode 100644
index 0000000..2e386e6
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterTsConfiguration.aidl
@@ -0,0 +1,30 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerFilterSettings;
+
+/**
+ * Filter Settings for a TS filter.
+ *
+ * {@hide}
+ */
+parcelable TunerFilterTsConfiguration {
+ char tpid;
+
+ TunerFilterSettings filterSettings;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterTsRecordEvent.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterTsRecordEvent.aidl
new file mode 100644
index 0000000..c52a749
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterTsRecordEvent.aidl
@@ -0,0 +1,56 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerFilterScIndexMask;
+
+/**
+ * Filter Event for TS Record Filter.
+ *
+ * {@hide}
+ */
+parcelable TunerFilterTsRecordEvent {
+ char pid;
+
+ int tsIndexMask;
+
+ /**
+ * Indexes of record output
+ */
+ TunerFilterScIndexMask scIndexMask;
+
+ /**
+ * Byte number from beginning of the filter's output
+ */
+ long byteNumber;
+
+ /**
+ * If the current event contains extended information or not
+ */
+ boolean isExtended;
+
+ /**
+ * The Presentation Time Stamp(PTS) for the audio or video frame. It is based on 90KHz
+ * and has the same format as the PTS in ISO/IEC 13818-1.
+ */
+ long pts;
+
+ /**
+ * Specifies the address of the first macroblock in the slice defined in ITU-T Rec. H.264.
+ */
+ int firstMbInSlice;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendAnalogCapabilities.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendAnalogCapabilities.aidl
new file mode 100644
index 0000000..74bf04e
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendAnalogCapabilities.aidl
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Analog Frontend Capabilities interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendAnalogCapabilities {
+ /**
+ * Signal Type capability
+ */
+ int typeCap;
+
+ /**
+ * Standard Interchange Format (SIF) capability
+ */
+ int sifStandardCap;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendAnalogSettings.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendAnalogSettings.aidl
new file mode 100644
index 0000000..40cd8c9
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendAnalogSettings.aidl
@@ -0,0 +1,43 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Analog Frontend Settings interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendAnalogSettings {
+ /**
+ * Signal frequency in Hertz
+ */
+ int frequency;
+
+ int signalType;
+
+ /**
+ * Standard Interchange Format (SIF) setting
+ */
+ int sifStandard;
+
+ /**
+ * Fields after isExtended are only valid when isExtended is true
+ */
+ boolean isExtended;
+
+ int aftFlag;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendAtsc3Capabilities.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendAtsc3Capabilities.aidl
new file mode 100644
index 0000000..6c9be77
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendAtsc3Capabilities.aidl
@@ -0,0 +1,54 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * ATSC3 Frontend Capabilities interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendAtsc3Capabilities {
+ /**
+ * Bandwidth capability
+ */
+ int bandwidthCap;
+
+ /**
+ * Modulation capability
+ */
+ int modulationCap;
+
+ /**
+ * TimeInterleaveMode capability
+ */
+ int timeInterleaveModeCap;
+
+ /**
+ * CodeRate capability
+ */
+ int codeRateCap;
+
+ /**
+ * FEC capability
+ */
+ int fecCap;
+
+ /**
+ * Demodulator Output Format capability
+ */
+ int demodOutputFormatCap;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendAtsc3PlpSettings.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendAtsc3PlpSettings.aidl
new file mode 100644
index 0000000..b29e1f7
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendAtsc3PlpSettings.aidl
@@ -0,0 +1,37 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Atsc3 Frontend Physical Layer Pipe Settings interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendAtsc3PlpSettings {
+ int plpId;
+
+ int modulation;
+
+ int interleaveMode;
+
+ int codeRate;
+
+ /**
+ * Forward Error Correction Type.
+ */
+ int fec;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendAtsc3Settings.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendAtsc3Settings.aidl
new file mode 100644
index 0000000..32fb8c7
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendAtsc3Settings.aidl
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerFrontendAtsc3PlpSettings;
+
+/**
+ * Atsc3 Frontend Settings interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendAtsc3Settings {
+ /**
+ * Signal frequency in Hertz
+ */
+ int frequency;
+
+ /**
+ * Bandwidth of tuning band.
+ */
+ int bandwidth;
+
+ int demodOutputFormat;
+
+ TunerFrontendAtsc3PlpSettings[] plpSettings;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendAtscCapabilities.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendAtscCapabilities.aidl
new file mode 100644
index 0000000..2b6c2fc
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendAtscCapabilities.aidl
@@ -0,0 +1,29 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * ATSC Frontend Capabilities interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendAtscCapabilities {
+ /**
+ * Modulation capability
+ */
+ int modulationCap;
+}
\ No newline at end of file
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendAtscSettings.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendAtscSettings.aidl
new file mode 100644
index 0000000..c7a8c07
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendAtscSettings.aidl
@@ -0,0 +1,31 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Atsc Frontend Settings interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendAtscSettings {
+ /**
+ * Signal frequency in Hertz
+ */
+ int frequency;
+
+ int modulation;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendCableCapabilities.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendCableCapabilities.aidl
new file mode 100644
index 0000000..b880c60
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendCableCapabilities.aidl
@@ -0,0 +1,39 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Cable(DVBC) Frontend Capabilities interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendCableCapabilities {
+ /**
+ * Modulation capability
+ */
+ int modulationCap;
+
+ /**
+ * Code Rate capability
+ */
+ long codeRateCap; // inner FEC will converge to codeRate
+
+ /**
+ * Annex capability
+ */
+ int annexCap;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendCableSettings.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendCableSettings.aidl
new file mode 100644
index 0000000..b9bcf29
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendCableSettings.aidl
@@ -0,0 +1,63 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Cable Frontend Settings interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendCableSettings {
+ /**
+ * Signal frequency in Hertz
+ */
+ int frequency;
+
+ int modulation;
+
+ /**
+ * Inner Forward Error Correction type as specified in ETSI EN 300 468 V1.15.1
+ * and ETSI EN 302 307-2 V1.1.1.
+ */
+ long innerFec;
+
+ /**
+ * Symbols per second
+ */
+ int symbolRate;
+
+ /**
+ * Outer Forward Error Correction (FEC) Type.
+ */
+ int outerFec;
+
+ int annex;
+
+ /**
+ * Spectral Inversion Type.
+ */
+ int spectralInversion;
+
+ /**
+ * Fields after isExtended are only valid when isExtended is true
+ */
+ boolean isExtended;
+
+ int interleaveMode;
+
+ int bandwidth;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendCapabilities.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendCapabilities.aidl
new file mode 100644
index 0000000..19f31f1
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendCapabilities.aidl
@@ -0,0 +1,85 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerFrontendAnalogCapabilities;
+import android.media.tv.tuner.TunerFrontendAtscCapabilities;
+import android.media.tv.tuner.TunerFrontendAtsc3Capabilities;
+import android.media.tv.tuner.TunerFrontendCableCapabilities;
+import android.media.tv.tuner.TunerFrontendDvbsCapabilities;
+import android.media.tv.tuner.TunerFrontendDvbtCapabilities;
+import android.media.tv.tuner.TunerFrontendIsdbsCapabilities;
+import android.media.tv.tuner.TunerFrontendIsdbs3Capabilities;
+import android.media.tv.tuner.TunerFrontendIsdbtCapabilities;
+
+/**
+ * Frontend Capabilities interface.
+ *
+ * Use a group of vectors as the workaround for Union structure that is not fully supported
+ * in AIDL currently.
+ *
+ * Client may use FrontendInfo.type as the discriminar to check the corresponding vector. If
+ * the vector is not null, it contains valid value.
+ *
+ * {@hide}
+ */
+union TunerFrontendCapabilities {
+ /**
+ * Analog Frontend Capabilities
+ */
+ TunerFrontendAnalogCapabilities analogCaps;
+
+ /**
+ * ATSC Frontend Capabilities
+ */
+ TunerFrontendAtscCapabilities atscCaps;
+
+ /**
+ * ATSC3 Frontend Capabilities
+ */
+ TunerFrontendAtsc3Capabilities atsc3Caps;
+
+ /**
+ * Cable Frontend Capabilities
+ */
+ TunerFrontendCableCapabilities cableCaps;
+
+ /**
+ * DVBS Frontend Capabilities
+ */
+ TunerFrontendDvbsCapabilities dvbsCaps;
+
+ /**
+ * DVBT Frontend Capabilities
+ */
+ TunerFrontendDvbtCapabilities dvbtCaps;
+
+ /**
+ * ISDB-S Frontend Capabilities
+ */
+ TunerFrontendIsdbsCapabilities isdbsCaps;
+
+ /**
+ * ISDB-S3 Frontend Capabilities
+ */
+ TunerFrontendIsdbs3Capabilities isdbs3Caps;
+
+ /**
+ * ISDB-T Frontend Capabilities
+ */
+ TunerFrontendIsdbtCapabilities isdbtCaps;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendDtmbCapabilities.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendDtmbCapabilities.aidl
new file mode 100644
index 0000000..e8e4933
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendDtmbCapabilities.aidl
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * DTMB Frontend Capabilities interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendDtmbCapabilities {
+ int transmissionModeCap;
+
+ int bandwidthCap;
+
+ int modulationCap;
+
+ int codeRateCap;
+
+ int guardIntervalCap;
+
+ int interleaveModeCap;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendDtmbSettings.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendDtmbSettings.aidl
new file mode 100644
index 0000000..45e7ff9
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendDtmbSettings.aidl
@@ -0,0 +1,38 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * DTMB Frontend Settings interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendDtmbSettings {
+ int frequency;
+
+ int transmissionMode;
+
+ int bandwidth;
+
+ int modulation;
+
+ int codeRate;
+
+ int guardInterval;
+
+ int interleaveMode;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendDvbsCapabilities.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendDvbsCapabilities.aidl
new file mode 100644
index 0000000..5e4322c
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendDvbsCapabilities.aidl
@@ -0,0 +1,39 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * DVBS Frontend Capabilities interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendDvbsCapabilities {
+ /**
+ * Modulation capability
+ */
+ int modulationCap;
+
+ /**
+ * Code Rate capability
+ */
+ long codeRateCap; // inner FEC will converge to codeRate
+
+ /**
+ * Sub standards capability
+ */
+ int standard;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendDvbsCodeRate.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendDvbsCodeRate.aidl
new file mode 100644
index 0000000..59b7de3
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendDvbsCodeRate.aidl
@@ -0,0 +1,42 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Dvbs Frontend CodeRate interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendDvbsCodeRate {
+ /**
+ * Inner Forward Error Correction type as specified in ETSI EN 300 468 V1.15.1
+ * and ETSI EN 302 307-2 V1.1.1.
+ */
+ long fec;
+
+ boolean isLinear;
+
+ /**
+ * true if enable short frame
+ */
+ boolean isShortFrames;
+
+ /**
+ * bits number in 1000 symbol. 0 if use the default.
+ */
+ int bitsPer1000Symbol;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendDvbsSettings.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendDvbsSettings.aidl
new file mode 100644
index 0000000..ec3e4b9
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendDvbsSettings.aidl
@@ -0,0 +1,65 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerFrontendDvbsCodeRate;
+
+/**
+ * Dvbs Frontend Settings interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendDvbsSettings {
+ /**
+ * Signal frequency in Hertz
+ */
+ int frequency;
+
+ int modulation;
+
+ TunerFrontendDvbsCodeRate codeRate;
+
+ int symbolRate;
+
+ /**
+ * Roll off type.
+ */
+ int rolloff;
+
+ /**
+ * Pilot mode.
+ */
+ int pilot;
+
+ int inputStreamId;
+
+ int standard;
+
+ /**
+ * Vcm mode.
+ */
+ int vcm;
+
+ /**
+ * Fields after isExtended are only valid when isExtended is true
+ */
+ boolean isExtended;
+
+ int scanType;
+
+ boolean isDiseqcRxMessage;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendDvbtCapabilities.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendDvbtCapabilities.aidl
new file mode 100644
index 0000000..73f16dd
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendDvbtCapabilities.aidl
@@ -0,0 +1,64 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * DVBT Frontend Capabilities interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendDvbtCapabilities {
+ /**
+ * Transmission Mode capability
+ */
+ int transmissionModeCap;
+
+ /**
+ * Bandwidth capability
+ */
+ int bandwidthCap;
+
+ /**
+ * Constellation capability
+ */
+ int constellationCap;
+
+ /**
+ * Code Rate capability
+ */
+ int codeRateCap;
+
+ /**
+ * Hierarchy Type capability
+ */
+ int hierarchyCap;
+
+ /**
+ * Guard Interval capability
+ */
+ int guardIntervalCap;
+
+ /**
+ * T2 Support capability
+ */
+ boolean isT2Supported;
+
+ /**
+ * Miso Support capability
+ */
+ boolean isMisoSupported;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendDvbtSettings.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendDvbtSettings.aidl
new file mode 100644
index 0000000..14c942a
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendDvbtSettings.aidl
@@ -0,0 +1,75 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Dvbt Frontend Settings interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendDvbtSettings {
+ /**
+ * Signal frequency in Hertz
+ */
+ int frequency;
+
+ int transmissionMode;
+
+ int bandwidth;
+
+ int constellation;
+
+ int hierarchy;
+
+ /**
+ * Code Rate for High Priority level
+ */
+ int hpCodeRate;
+
+ /**
+ * Code Rate for Low Priority level
+ */
+ int lpCodeRate;
+
+ int guardInterval;
+
+ boolean isHighPriority;
+
+ int standard;
+
+ boolean isMiso;
+
+ /**
+ * Physical Layer Pipe (PLP) mode
+ */
+ int plpMode;
+
+ /**
+ * Physical Layer Pipe (PLP) Id
+ */
+ int plpId;
+
+ /**
+ * Physical Layer Pipe (PLP) Group Id
+ */
+ int plpGroupId;
+
+ /**
+ * Fields after isExtended are only valid when isExtended is true
+ */
+ boolean isExtended;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendInfo.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendInfo.aidl
new file mode 100644
index 0000000..4bccd56
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendInfo.aidl
@@ -0,0 +1,80 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerFrontendCapabilities;
+
+/**
+ * FrontendInfo interface that carries tuner frontend information.
+ *
+ * <p>This is used to update the TunerResourceManager and pass Frontend
+ * information from HAL to the client side.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendInfo {
+ /**
+ * Frontend Handle
+ */
+ int handle;
+
+ /**
+ * Frontend Type
+ */
+ int type;
+
+ /**
+ * Minimum Frequency in Hertz
+ */
+ int minFrequency;
+
+ /**
+ * Maximum Frequency in Hertz
+ */
+ int maxFrequency;
+
+ /**
+ * Minimum symbols per second
+ */
+ int minSymbolRate;
+
+ /**
+ * Maximum symbols per second
+ */
+ int maxSymbolRate;
+
+ /**
+ * Range in Hertz
+ */
+ int acquireRange;
+
+ /**
+ * Frontends are assigned with the same exclusiveGroupId if they can't
+ * function at same time. For instance, they share same hardware module.
+ */
+ int exclusiveGroupId;
+
+ /**
+ * A list of supported status types which client can inquiry
+ */
+ int[] statusCaps;
+
+ /**
+ * Frontend Capabilities
+ */
+ TunerFrontendCapabilities caps;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendIsdbs3Capabilities.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendIsdbs3Capabilities.aidl
new file mode 100644
index 0000000..84dd67a
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendIsdbs3Capabilities.aidl
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * ISDB-S3 Frontend Capabilities interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendIsdbs3Capabilities {
+ /**
+ * Modulation capability
+ */
+ int modulationCap;
+
+ /**
+ * Code Rate capability
+ */
+ int codeRateCap;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendIsdbs3Settings.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendIsdbs3Settings.aidl
new file mode 100644
index 0000000..9a11fd5
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendIsdbs3Settings.aidl
@@ -0,0 +1,44 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Isdbs3 Frontend Settings interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendIsdbs3Settings {
+ /**
+ * Signal frequency in Hertz
+ */
+ int frequency;
+
+ char streamId;
+
+ int streamIdType;
+
+ int modulation;
+
+ int codeRate;
+
+ /**
+ * Symbols per second
+ */
+ int symbolRate;
+
+ int rolloff;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendIsdbsCapabilities.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendIsdbsCapabilities.aidl
new file mode 100644
index 0000000..15dfdf7
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendIsdbsCapabilities.aidl
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * ISDB-S Frontend Capabilities interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendIsdbsCapabilities {
+ /**
+ * Modulation capability
+ */
+ int modulationCap;
+
+ /**
+ * Code Rate capability
+ */
+ int codeRateCap;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendIsdbsSettings.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendIsdbsSettings.aidl
new file mode 100644
index 0000000..dff9f4a
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendIsdbsSettings.aidl
@@ -0,0 +1,44 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Isdbs Frontend Settings interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendIsdbsSettings {
+ /**
+ * Signal frequency in Hertz
+ */
+ int frequency;
+
+ char streamId;
+
+ int streamIdType;
+
+ int modulation;
+
+ int codeRate;
+
+ /**
+ * Symbols per second
+ */
+ int symbolRate;
+
+ int rolloff;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendIsdbtCapabilities.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendIsdbtCapabilities.aidl
new file mode 100644
index 0000000..c9295d8
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendIsdbtCapabilities.aidl
@@ -0,0 +1,49 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * ISDB-T Frontend Capabilities interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendIsdbtCapabilities {
+ /**
+ * ISDB-T Mode capability
+ */
+ int modeCap;
+
+ /**
+ * Bandwidth capability
+ */
+ int bandwidthCap;
+
+ /**
+ * Modulation capability
+ */
+ int modulationCap;
+
+ /**
+ * Code Rate capability
+ */
+ int codeRateCap;
+
+ /**
+ * Guard Interval capability
+ */
+ int guardIntervalCap;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendIsdbtSettings.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendIsdbtSettings.aidl
new file mode 100644
index 0000000..191f3a6
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendIsdbtSettings.aidl
@@ -0,0 +1,41 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Isdbt Frontend Settings interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendIsdbtSettings {
+ /**
+ * Signal frequency in Hertz
+ */
+ int frequency;
+
+ int modulation;
+
+ int bandwidth;
+
+ int mode;
+
+ int codeRate;
+
+ int guardInterval;
+
+ int serviceAreaId;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendScanAtsc3PlpInfo.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendScanAtsc3PlpInfo.aidl
new file mode 100644
index 0000000..1b8fcbb
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendScanAtsc3PlpInfo.aidl
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Atsc3 Frontend Physical Layer Pipe Info.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendScanAtsc3PlpInfo {
+ byte plpId;
+
+ boolean llsFlag;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendScanMessage.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendScanMessage.aidl
new file mode 100644
index 0000000..9921ca1
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendScanMessage.aidl
@@ -0,0 +1,56 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerFrontendScanAtsc3PlpInfo;
+
+/**
+ * Tuner Frontend Scan Message interface.
+ *
+ * {@hide}
+ */
+union TunerFrontendScanMessage {
+ boolean isLocked;
+
+ boolean isEnd;
+
+ byte progressPercent;
+
+ int[] frequencies;
+
+ int[] symbolRates;
+
+ int hierarchy;
+
+ int analogType;
+
+ byte[] plpIds;
+
+ byte[] groupIds;
+
+ char[] inputStreamIds;
+
+ int std;
+
+ TunerFrontendScanAtsc3PlpInfo[] atsc3PlpInfos;
+
+ int modulation;
+
+ int annex;
+
+ boolean isHighPriority;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendSettings.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendSettings.aidl
new file mode 100644
index 0000000..70a5f3e
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendSettings.aidl
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerFrontendUnionSettings;
+
+/**
+ * Frontend Settings interface.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendSettings {
+ TunerFrontendUnionSettings settings;
+
+ boolean isExtended;
+
+ int endFrequency;
+
+ int inversion;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendStatus.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendStatus.aidl
new file mode 100644
index 0000000..2b3c01b
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendStatus.aidl
@@ -0,0 +1,187 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerFrontendStatusAtsc3PlpInfo;
+
+/**
+ * Tuner Frontend Status interface.
+ *
+ * {@hide}
+ */
+union TunerFrontendStatus {
+ /**
+ * Lock status for Demod in True/False.
+ */
+ boolean isDemodLocked;
+
+ /**
+ * SNR value measured by 0.001 dB.
+ */
+ int snr;
+
+ /**
+ * The number of error bits per 1 billion bits.
+ */
+ int ber;
+
+ /**
+ * The number of error packages per 1 billion packages.
+ */
+ int per;
+
+ /**
+ * The number of error bits per 1 billion bits before FEC.
+ */
+ int preBer;
+
+ /**
+ * Signal Quality in percent.
+ */
+ int signalQuality;
+
+ /**
+ * Signal Strength measured by 0.001 dBm.
+ */
+ int signalStrength;
+
+ /**
+ * Symbols per second
+ */
+ int symbolRate;
+
+ long innerFec;
+
+ /**
+ * Check frontend type to decide the hidl type value
+ */
+ int modulation;
+
+ int inversion;
+
+ int lnbVoltage;
+
+ byte plpId;
+
+ boolean isEWBS;
+
+ /**
+ * AGC value is normalized from 0 to 255.
+ */
+ byte agc;
+
+ boolean isLnaOn;
+
+ boolean[] isLayerError;
+
+ /**
+ * MER value measured by 0.001 dB
+ */
+ int mer;
+
+ /**
+ * Frequency difference in Hertz.
+ */
+ int freqOffset;
+
+ int hierarchy;
+
+ boolean isRfLocked;
+
+ /**
+ * A list of PLP status for tuned PLPs for ATSC3 frontend.
+ */
+ TunerFrontendStatusAtsc3PlpInfo[] plpInfo;
+
+ // 1.1 Extension Starting
+
+ /**
+ * Extended modulation status. Check frontend type to decide the hidl type value.
+ */
+ int[] modulations;
+
+ /**
+ * Extended bit error ratio status.
+ */
+ int[] bers;
+
+ /**
+ * Extended code rate status.
+ */
+ long[] codeRates;
+
+ /**
+ * Extended bandwidth status. Check frontend type to decide the hidl type value.
+ */
+ int bandwidth;
+
+ /**
+ * Extended guard interval status. Check frontend type to decide the hidl type value.
+ */
+ int interval;
+
+ /**
+ * Extended transmission mode status. Check frontend type to decide the hidl type value.
+ */
+ int transmissionMode;
+
+ /**
+ * Uncorrectable Error Counts of the frontend's Physical Layer Pipe (PLP)
+ * since the last tune operation.
+ */
+ int uec;
+
+ /**
+ * The current DVB-T2 system id status.
+ */
+ char systemId;
+
+ /**
+ * Frontend Interleaving Modes. Check frontend type to decide the hidl type value.
+ */
+ int[] interleaving;
+
+ /**
+ * Segments in ISDB-T Specification of all the channels.
+ */
+ byte[] isdbtSegment;
+
+ /**
+ * Transport Stream Data Rate in BPS of the current channel.
+ */
+ int[] tsDataRate;
+
+ /**
+ * Roll Off Type status of the frontend. Check frontend type to decide the hidl type value.
+ */
+ int rollOff;
+
+ /**
+ * If the frontend currently supports MISO or not.
+ */
+ boolean isMiso;
+
+ /**
+ * If the frontend code rate is linear or not.
+ */
+ boolean isLinear;
+
+ /**
+ * If short frames are enabled or not.
+ */
+ boolean isShortFrames;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendStatusAtsc3PlpInfo.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendStatusAtsc3PlpInfo.aidl
new file mode 100644
index 0000000..4116c34
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendStatusAtsc3PlpInfo.aidl
@@ -0,0 +1,39 @@
+/**
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Atsc3 Frontend Physical Layer Pipe Info in Frontend status.
+ *
+ * {@hide}
+ */
+parcelable TunerFrontendStatusAtsc3PlpInfo {
+ /**
+ * PLP Id value.
+ */
+ byte plpId;
+
+ /**
+ * Demod Lock/Unlock status of this particular PLP.
+ */
+ boolean isLocked;
+
+ /**
+ * Uncorrectable Error Counts (UEC) of this particular PLP since last tune operation.
+ */
+ int uec;
+}
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFrontendUnionSettings.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendUnionSettings.aidl
new file mode 100644
index 0000000..c362c2a
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFrontendUnionSettings.aidl
@@ -0,0 +1,55 @@
+/**
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerFrontendAnalogSettings;
+import android.media.tv.tuner.TunerFrontendAtscSettings;
+import android.media.tv.tuner.TunerFrontendAtsc3Settings;
+import android.media.tv.tuner.TunerFrontendCableSettings;
+import android.media.tv.tuner.TunerFrontendDtmbSettings;
+import android.media.tv.tuner.TunerFrontendDvbsSettings;
+import android.media.tv.tuner.TunerFrontendDvbtSettings;
+import android.media.tv.tuner.TunerFrontendIsdbsSettings;
+import android.media.tv.tuner.TunerFrontendIsdbs3Settings;
+import android.media.tv.tuner.TunerFrontendIsdbtSettings;
+
+/**
+ * Frontend Settings Union interface.
+ *
+ * {@hide}
+ */
+union TunerFrontendUnionSettings {
+ TunerFrontendAnalogSettings analog;
+
+ TunerFrontendAtscSettings atsc;
+
+ TunerFrontendAtsc3Settings atsc3;
+
+ TunerFrontendCableSettings cable;
+
+ TunerFrontendDvbsSettings dvbs;
+
+ TunerFrontendDvbtSettings dvbt;
+
+ TunerFrontendIsdbsSettings isdbs;
+
+ TunerFrontendIsdbs3Settings isdbs3;
+
+ TunerFrontendIsdbtSettings isdbt;
+
+ TunerFrontendDtmbSettings dtmb;
+}
diff --git a/services/tuner/main_tunerservice.cpp b/services/tuner/main_tunerservice.cpp
new file mode 100644
index 0000000..586a0e2
--- /dev/null
+++ b/services/tuner/main_tunerservice.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/Log.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "TunerService.h"
+
+using namespace android;
+
+int main(int argc __unused, char** argv) {
+ ALOGD("Tuner service starting");
+
+ strcpy(argv[0], "media.tuner");
+ sp<ProcessState> proc(ProcessState::self());
+ sp<IServiceManager> sm = defaultServiceManager();
+ ALOGD("ServiceManager: %p", sm.get());
+
+ binder_status_t status = TunerService::instantiate();
+ if (status != STATUS_OK) {
+ ALOGD("Failed to add tuner service as AIDL interface");
+ return -1;
+ }
+
+ ProcessState::self()->startThreadPool();
+ IPCThreadState::self()->joinThreadPool();
+}
diff --git a/services/tuner/mediatuner.rc b/services/tuner/mediatuner.rc
new file mode 100644
index 0000000..fd30618
--- /dev/null
+++ b/services/tuner/mediatuner.rc
@@ -0,0 +1,5 @@
+service media.tuner /system/bin/mediatuner
+ class main
+ group media
+ ioprio rt 4
+ task_profiles ProcessCapacityHigh HighPerformance
diff --git a/tools/mainline_hook.sh b/tools/mainline_hook.sh
deleted file mode 100755
index 58afb49..0000000
--- a/tools/mainline_hook.sh
+++ /dev/null
@@ -1,84 +0,0 @@
-#!/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/mainline_hook_partial.sh b/tools/mainline_hook_partial.sh
index 3dc6163..bd82315 100755
--- a/tools/mainline_hook_partial.sh
+++ b/tools/mainline_hook_partial.sh
@@ -35,7 +35,8 @@
## tunables:
##
-DEV_BRANCH=rvc-dev
+DEV_BRANCH=master
+MAINLINE_BRANCH=sc-mainline-prod
filelist_file=MainlineFiles.cfg
###
@@ -50,8 +51,9 @@
## get the active branch:
## * <localbranch> <shainfo> [goog/master] Fix to handle missing checks on error returned
## strip this down to "master"
+## * b157501573_advisory 25521834a6 [goog/sc-dev] Merge "PlayerBase: add audio session ID" into sc-dev
##
-current=`git branch -vv | grep -P "^\*[^\[]+\[goog/"|sed -e 's/^.*\[//' | sed -e 's/:.*$//'| sed -e 's/^goog\///'`
+current=`git branch -vv | grep -P "^\*[^\[]+\[goog/"|sed -e 's/^.*\[//' | sed -e 's/\].*$//'|sed -e 's/:.*$//'| sed -e 's/^goog\///'`
if [ "${current}" = "" ] ; then
current=unknown
fi
@@ -142,12 +144,36 @@
exit 0
fi
+#
+# exit 0 is "all good, no output passed along to user"
+# exit 77 is "a warning, pass along the output to the user"
+# exit 1 will be a failure.
+#
result=0
+
+# simple reminder that it should also land in mainline branch
+#
+if [ "${current}" != "${MAINLINE_BRANCH}" ] ; then
+ # simple reminder to ensure it hits mainline
+ result=77
+ cat - <<EOF
+You are uploading repo ${RED}${REPO_PATH}${NORMAL} to branch ${RED}${current}${NORMAL}.
+The mainline branch for ${RED}${REPO_PATH}${NORMAL} is branch ${RED}${MAINLINE_BRANCH}${NORMAL}.
+
+Ensure an appropriate cherry pick or equivalent lands in branch ${RED}${MAINLINE_BRANCH}${NORMAL}.
+Security bulletin timing or unreleased functionality may drive when this can be landed.
+EOF
+fi
+
+# watch for the mixed some mainline / some not CL
+# we usually want to reject such mixed CLs
+#
+
if [ ! -z "${mainline_no}" ] ; then
# mixed bag, suggest (not insist) that developer split them.
result=1
cat - <<EOF
-This CL contains files contains both mainline and non-mainline files. Consider separating
+This change contains both mainline and non-mainline files. Please separate
them into separate CLs. It may also be appropriate to update the list of mainline
files in ${RED}${REPO_ROOT}/${filelist_file}${NORMAL}.
@@ -162,33 +188,6 @@
echo ${mainline_no} | sed -e 's/ /
/g'
echo -e ${NORMAL}
-fi
-
-if [ "${current}" != "${DEV_BRANCH}" ] ; then
- # Change is not in the desired mainline dev branch
- result=1
-
- #echo -e "${RED}"
- cat - <<EOF
-
-You are uploading repo ${RED}${REPO_PATH}${NORMAL} to branch ${RED}${current}${NORMAL}.
-The source of truth for ${RED}${REPO_PATH}${NORMAL} is branch ${RED}${DEV_BRANCH}${NORMAL}.
-
-Please upload this change to branch ${RED}${DEV_BRANCH}${NORMAL} unless one or more of
-the following apply:
-- this is a security bug prohibited from disclosure before the next dessert release.
- (moderate security bugs fall into this category).
-- this is new functionality prohibitied from disclosure before the next dessert release.
-EOF
- #echo -e "${NORMAL}"
-
-fi
-
-## since stdout is buffered in a way that complicates the below, we're just going
-## to tell the user what they can do to get around this check instead of asking them
-## as part of this run of the command.
-
-if [ ${result} != 0 ] ; then
cat - <<EOF
If you are sure you want to proceed uploading to branch ${RED}${current}${NORMAL},
@@ -196,5 +195,11 @@
EOF
fi
+
+# result will be:
+# 0: all good, no output passed to user
+# 77: warnings (but we pass), output passed along to user
+# else: failure, output passed along to the user
+
exit ${result}
diff --git a/tools/mainline_hook_project.sh b/tools/mainline_hook_project.sh
index 8d35470..cb5fc44 100755
--- a/tools/mainline_hook_project.sh
+++ b/tools/mainline_hook_project.sh
@@ -16,7 +16,8 @@
# tunables
-DEV_BRANCH=rvc-dev
+DEV_BRANCH=master
+MAINLINE_BRANCH=sc-mainline-prod
###
RED=$(tput setaf 1)
@@ -25,41 +26,27 @@
## check the active branch:
## * b131183694 d198c6a [goog/master] Fix to handle missing checks on error returned
##
-current=`git branch -vv | grep -P "^\*[^\[]+\[goog/"|sed -e 's/^.*\[//' | sed -e 's/:.*$//'| sed -e 's/^goog\///'`
+current=`git branch -vv | grep -P "^\*[^\[]+\[goog/"|sed -e 's/^.*\[//' | sed -e 's/\].*$//'|sed -e 's/:.*$//'| sed -e 's/^goog\///'`
if [ "${current}" = "" ] ; then
current=unknown
fi
-if [ "${current}" = "${DEV_BRANCH}" ] ; then
- # Change appears to be in mainline dev branch
- exit 0
+# simple reminder that it should also land in mainline branch
+#
+if [ "${current}" != "${MAINLINE_BRANCH}" ] ; then
+ # simple reminder to ensure it hits mainline
+ cat - <<EOF
+You are uploading repo ${RED}${REPO_PATH}${NORMAL} to branch ${RED}${current}${NORMAL}.
+The mainline branch for ${RED}${REPO_PATH}${NORMAL} is branch ${RED}${MAINLINE_BRANCH}${NORMAL}.
+
+Ensure an appropriate cherry pick or equivalent lands in branch ${RED}${MAINLINE_BRANCH}${NORMAL}.
+Security bulletin timing or unreleased functionality may determine when that can be landed.
+
+EOF
fi
-## warn the user that about not being on the typical/desired branch.
-
-cat - <<EOF
-
-You are uploading repo ${RED}${REPO_PATH}${NORMAL} to branch ${RED}${current}${NORMAL}.
-The source of truth for ${RED}${REPO_PATH}${NORMAL} is branch ${RED}${DEV_BRANCH}${NORMAL}.
-
-Please upload this change to branch ${RED}${DEV_BRANCH}${NORMAL} unless one or more of
-the following apply:
-- this is a security bug prohibited from disclosure before the next dessert release.
- (moderate security bugs fall into this category).
-- this is new functionality prohibitied from disclosure before the next dessert release.
-EOF
-
-
-##
-## TODO: prompt the user y/n to continue right now instead of re-invoking with no-verify
-## this has to get around how repo buffers stdout from this script such that the output
-## is not flushed before we try to read the input.
-##
-
-cat - <<EOF
-If you are sure you want to proceed uploading to branch ${RED}${current}${NORMAL},
-re-run your repo upload command with the '--no-verify' option
-
-EOF
-exit 1
+# exit 0 is "all good, no output passed along to user"
+# exit 77 is "all ok, but output is passed along to the user"
+#
+exit 77