Merge "Camera: Don't hold locks during shared output dequeue" into pi-dev
diff --git a/media/libaudiohal/4.0/Android.bp b/media/libaudiohal/4.0/Android.bp
new file mode 100644
index 0000000..3d104ab
--- /dev/null
+++ b/media/libaudiohal/4.0/Android.bp
@@ -0,0 +1,57 @@
+cc_library_shared {
+ name: "libaudiohal@4.0",
+
+ srcs: [
+ "DeviceHalLocal.cpp",
+ "DevicesFactoryHalHybrid.cpp",
+ "DevicesFactoryHalLocal.cpp",
+ "StreamHalLocal.cpp",
+
+ "ConversionHelperHidl.cpp",
+ "DeviceHalHidl.cpp",
+ "DevicesFactoryHalHidl.cpp",
+ "EffectBufferHalHidl.cpp",
+ "EffectHalHidl.cpp",
+ "EffectsFactoryHalHidl.cpp",
+ "StreamHalHidl.cpp",
+ ],
+
+ export_include_dirs: ["include"],
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libaudiohal_deathhandler",
+ "libaudioutils",
+ "libcutils",
+ "liblog",
+ "libutils",
+ "libhardware",
+ "libbase",
+ "libfmq",
+ "libhwbinder",
+ "libhidlbase",
+ "libhidlmemory",
+ "libhidltransport",
+ "android.hardware.audio@4.0",
+ "android.hardware.audio.common-util",
+ "android.hardware.audio.common@4.0",
+ "android.hardware.audio.common@4.0-util",
+ "android.hardware.audio.effect@4.0",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "libmedia_helper",
+ "libmediautils",
+ ],
+ header_libs: [
+ "android.hardware.audio.common.util@all-versions",
+ "libaudiohal_headers"
+ ],
+
+ export_shared_lib_headers: [
+ "libfmq",
+ ],
+}
diff --git a/media/libaudiohal/4.0/ConversionHelperHidl.cpp b/media/libaudiohal/4.0/ConversionHelperHidl.cpp
new file mode 100644
index 0000000..a3cc28f
--- /dev/null
+++ b/media/libaudiohal/4.0/ConversionHelperHidl.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+#include <string.h>
+
+#define LOG_TAG "HalHidl"
+#include <media/AudioParameter.h>
+#include <utils/Log.h>
+
+#include "ConversionHelperHidl.h"
+
+using ::android::hardware::audio::V4_0::Result;
+
+namespace android {
+namespace V4_0 {
+
+// static
+status_t ConversionHelperHidl::keysFromHal(const String8& keys, hidl_vec<hidl_string> *hidlKeys) {
+ AudioParameter halKeys(keys);
+ if (halKeys.size() == 0) return BAD_VALUE;
+ hidlKeys->resize(halKeys.size());
+ //FIXME: keyStreamSupportedChannels and keyStreamSupportedSamplingRates come with a
+ // "keyFormat=<value>" pair. We need to transform it into a single key string so that it is
+ // carried over to the legacy HAL via HIDL.
+ String8 value;
+ bool keepFormatValue = halKeys.size() == 2 &&
+ (halKeys.get(String8(AudioParameter::keyStreamSupportedChannels), value) == NO_ERROR ||
+ halKeys.get(String8(AudioParameter::keyStreamSupportedSamplingRates), value) == NO_ERROR);
+
+ for (size_t i = 0; i < halKeys.size(); ++i) {
+ String8 key;
+ status_t status = halKeys.getAt(i, key);
+ if (status != OK) return status;
+ if (keepFormatValue && key == AudioParameter::keyFormat) {
+ AudioParameter formatParam;
+ halKeys.getAt(i, key, value);
+ formatParam.add(key, value);
+ key = formatParam.toString();
+ }
+ (*hidlKeys)[i] = key.string();
+ }
+ return OK;
+}
+
+// static
+status_t ConversionHelperHidl::parametersFromHal(
+ const String8& kvPairs, hidl_vec<ParameterValue> *hidlParams) {
+ AudioParameter params(kvPairs);
+ if (params.size() == 0) return BAD_VALUE;
+ hidlParams->resize(params.size());
+ for (size_t i = 0; i < params.size(); ++i) {
+ String8 key, value;
+ status_t status = params.getAt(i, key, value);
+ if (status != OK) return status;
+ (*hidlParams)[i].key = key.string();
+ (*hidlParams)[i].value = value.string();
+ }
+ return OK;
+}
+
+// static
+void ConversionHelperHidl::parametersToHal(
+ const hidl_vec<ParameterValue>& parameters, String8 *values) {
+ AudioParameter params;
+ for (size_t i = 0; i < parameters.size(); ++i) {
+ params.add(String8(parameters[i].key.c_str()), String8(parameters[i].value.c_str()));
+ }
+ values->setTo(params.toString());
+}
+
+ConversionHelperHidl::ConversionHelperHidl(const char* className)
+ : mClassName(className) {
+}
+
+// static
+status_t ConversionHelperHidl::analyzeResult(const Result& result) {
+ switch (result) {
+ case Result::OK: return OK;
+ case Result::INVALID_ARGUMENTS: return BAD_VALUE;
+ case Result::INVALID_STATE: return NOT_ENOUGH_DATA;
+ case Result::NOT_INITIALIZED: return NO_INIT;
+ case Result::NOT_SUPPORTED: return INVALID_OPERATION;
+ default: return NO_INIT;
+ }
+}
+
+void ConversionHelperHidl::emitError(const char* funcName, const char* description) {
+ ALOGE("%s %p %s: %s (from rpc)", mClassName, this, funcName, description);
+}
+
+} // namespace V4_0
+} // namespace android
diff --git a/media/libaudiohal/4.0/ConversionHelperHidl.h b/media/libaudiohal/4.0/ConversionHelperHidl.h
new file mode 100644
index 0000000..ddc8569
--- /dev/null
+++ b/media/libaudiohal/4.0/ConversionHelperHidl.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_4_0_H
+#define ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_4_0_H
+
+#include <android/hardware/audio/4.0/types.h>
+#include <hidl/HidlSupport.h>
+#include <utils/String8.h>
+
+using ::android::hardware::audio::V4_0::ParameterValue;
+using ::android::hardware::Return;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+
+namespace android {
+namespace V4_0 {
+
+class ConversionHelperHidl {
+ protected:
+ static status_t keysFromHal(const String8& keys, hidl_vec<hidl_string> *hidlKeys);
+ static status_t parametersFromHal(const String8& kvPairs, hidl_vec<ParameterValue> *hidlParams);
+ static void parametersToHal(const hidl_vec<ParameterValue>& parameters, String8 *values);
+
+ ConversionHelperHidl(const char* className);
+
+ template<typename R, typename T>
+ status_t processReturn(const char* funcName, const Return<R>& ret, T *retval) {
+ if (ret.isOk()) {
+ // This way it also works for enum class to unscoped enum conversion.
+ *retval = static_cast<T>(static_cast<R>(ret));
+ return OK;
+ }
+ return processReturn(funcName, ret);
+ }
+
+ template<typename T>
+ status_t processReturn(const char* funcName, const Return<T>& ret) {
+ if (!ret.isOk()) {
+ emitError(funcName, ret.description().c_str());
+ }
+ return ret.isOk() ? OK : FAILED_TRANSACTION;
+ }
+
+ status_t processReturn(const char* funcName, const Return<hardware::audio::V4_0::Result>& ret) {
+ if (!ret.isOk()) {
+ emitError(funcName, ret.description().c_str());
+ }
+ return ret.isOk() ? analyzeResult(ret) : FAILED_TRANSACTION;
+ }
+
+ template<typename T>
+ status_t processReturn(
+ const char* funcName, const Return<T>& ret, hardware::audio::V4_0::Result retval) {
+ if (!ret.isOk()) {
+ emitError(funcName, ret.description().c_str());
+ }
+ return ret.isOk() ? analyzeResult(retval) : FAILED_TRANSACTION;
+ }
+
+ private:
+ const char* mClassName;
+
+ static status_t analyzeResult(const hardware::audio::V4_0::Result& result);
+
+ void emitError(const char* funcName, const char* description);
+};
+
+} // namespace V4_0
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_4_0_H
diff --git a/media/libaudiohal/4.0/DeviceHalHidl.cpp b/media/libaudiohal/4.0/DeviceHalHidl.cpp
new file mode 100644
index 0000000..8da1051
--- /dev/null
+++ b/media/libaudiohal/4.0/DeviceHalHidl.cpp
@@ -0,0 +1,372 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+
+#define LOG_TAG "DeviceHalHidl"
+//#define LOG_NDEBUG 0
+
+#include <android/hardware/audio/4.0/IPrimaryDevice.h>
+#include <cutils/native_handle.h>
+#include <hwbinder/IPCThreadState.h>
+#include <utils/Log.h>
+
+#include <common/all-versions/VersionUtils.h>
+
+#include "DeviceHalHidl.h"
+#include "HidlUtils.h"
+#include "StreamHalHidl.h"
+#include "VersionUtils.h"
+
+using ::android::hardware::audio::common::V4_0::AudioConfig;
+using ::android::hardware::audio::common::V4_0::AudioDevice;
+using ::android::hardware::audio::common::V4_0::AudioInputFlag;
+using ::android::hardware::audio::common::V4_0::AudioOutputFlag;
+using ::android::hardware::audio::common::V4_0::AudioPatchHandle;
+using ::android::hardware::audio::common::V4_0::AudioPort;
+using ::android::hardware::audio::common::V4_0::AudioPortConfig;
+using ::android::hardware::audio::common::V4_0::AudioMode;
+using ::android::hardware::audio::common::V4_0::AudioSource;
+using ::android::hardware::audio::common::V4_0::HidlUtils;
+using ::android::hardware::audio::common::utils::mkEnumConverter;
+using ::android::hardware::audio::V4_0::DeviceAddress;
+using ::android::hardware::audio::V4_0::IPrimaryDevice;
+using ::android::hardware::audio::V4_0::ParameterValue;
+using ::android::hardware::audio::V4_0::Result;
+using ::android::hardware::audio::V4_0::SinkMetadata;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+
+namespace android {
+namespace V4_0 {
+
+namespace {
+
+status_t deviceAddressFromHal(
+ audio_devices_t device, const char* halAddress, DeviceAddress* address) {
+ address->device = AudioDevice(device);
+
+ if (address == nullptr || strnlen(halAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0) {
+ return OK;
+ }
+ const bool isInput = (device & AUDIO_DEVICE_BIT_IN) != 0;
+ if (isInput) device &= ~AUDIO_DEVICE_BIT_IN;
+ if ((!isInput && (device & AUDIO_DEVICE_OUT_ALL_A2DP) != 0)
+ || (isInput && (device & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) != 0)) {
+ int status = sscanf(halAddress,
+ "%hhX:%hhX:%hhX:%hhX:%hhX:%hhX",
+ &address->address.mac[0], &address->address.mac[1], &address->address.mac[2],
+ &address->address.mac[3], &address->address.mac[4], &address->address.mac[5]);
+ return status == 6 ? OK : BAD_VALUE;
+ } else if ((!isInput && (device & AUDIO_DEVICE_OUT_IP) != 0)
+ || (isInput && (device & AUDIO_DEVICE_IN_IP) != 0)) {
+ int status = sscanf(halAddress,
+ "%hhu.%hhu.%hhu.%hhu",
+ &address->address.ipv4[0], &address->address.ipv4[1],
+ &address->address.ipv4[2], &address->address.ipv4[3]);
+ return status == 4 ? OK : BAD_VALUE;
+ } else if ((!isInput && (device & AUDIO_DEVICE_OUT_ALL_USB)) != 0
+ || (isInput && (device & AUDIO_DEVICE_IN_ALL_USB)) != 0) {
+ int status = sscanf(halAddress,
+ "card=%d;device=%d",
+ &address->address.alsa.card, &address->address.alsa.device);
+ return status == 2 ? OK : BAD_VALUE;
+ } else if ((!isInput && (device & AUDIO_DEVICE_OUT_BUS) != 0)
+ || (isInput && (device & AUDIO_DEVICE_IN_BUS) != 0)) {
+ if (halAddress != NULL) {
+ address->busAddress = halAddress;
+ return OK;
+ }
+ return BAD_VALUE;
+ } else if ((!isInput && (device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX)) != 0
+ || (isInput && (device & AUDIO_DEVICE_IN_REMOTE_SUBMIX) != 0)) {
+ if (halAddress != NULL) {
+ address->rSubmixAddress = halAddress;
+ return OK;
+ }
+ return BAD_VALUE;
+ }
+ return OK;
+}
+
+} // namespace
+
+DeviceHalHidl::DeviceHalHidl(const sp<IDevice>& device)
+ : ConversionHelperHidl("Device"), mDevice(device),
+ mPrimaryDevice(IPrimaryDevice::castFrom(device)) {
+}
+
+DeviceHalHidl::~DeviceHalHidl() {
+ if (mDevice != 0) {
+ mDevice.clear();
+ hardware::IPCThreadState::self()->flushCommands();
+ }
+}
+
+status_t DeviceHalHidl::getSupportedDevices(uint32_t*) {
+ // Obsolete.
+ return INVALID_OPERATION;
+}
+
+status_t DeviceHalHidl::initCheck() {
+ if (mDevice == 0) return NO_INIT;
+ return processReturn("initCheck", mDevice->initCheck());
+}
+
+status_t DeviceHalHidl::setVoiceVolume(float volume) {
+ if (mDevice == 0) return NO_INIT;
+ if (mPrimaryDevice == 0) return INVALID_OPERATION;
+ return processReturn("setVoiceVolume", mPrimaryDevice->setVoiceVolume(volume));
+}
+
+status_t DeviceHalHidl::setMasterVolume(float volume) {
+ if (mDevice == 0) return NO_INIT;
+ if (mPrimaryDevice == 0) return INVALID_OPERATION;
+ return processReturn("setMasterVolume", mPrimaryDevice->setMasterVolume(volume));
+}
+
+status_t DeviceHalHidl::getMasterVolume(float *volume) {
+ if (mDevice == 0) return NO_INIT;
+ if (mPrimaryDevice == 0) return INVALID_OPERATION;
+ Result retval;
+ Return<void> ret = mPrimaryDevice->getMasterVolume(
+ [&](Result r, float v) {
+ retval = r;
+ if (retval == Result::OK) {
+ *volume = v;
+ }
+ });
+ return processReturn("getMasterVolume", ret, retval);
+}
+
+status_t DeviceHalHidl::setMode(audio_mode_t mode) {
+ if (mDevice == 0) return NO_INIT;
+ if (mPrimaryDevice == 0) return INVALID_OPERATION;
+ return processReturn("setMode", mPrimaryDevice->setMode(AudioMode(mode)));
+}
+
+status_t DeviceHalHidl::setMicMute(bool state) {
+ if (mDevice == 0) return NO_INIT;
+ return processReturn("setMicMute", mDevice->setMicMute(state));
+}
+
+status_t DeviceHalHidl::getMicMute(bool *state) {
+ if (mDevice == 0) return NO_INIT;
+ Result retval;
+ Return<void> ret = mDevice->getMicMute(
+ [&](Result r, bool mute) {
+ retval = r;
+ if (retval == Result::OK) {
+ *state = mute;
+ }
+ });
+ return processReturn("getMicMute", ret, retval);
+}
+
+status_t DeviceHalHidl::setMasterMute(bool state) {
+ if (mDevice == 0) return NO_INIT;
+ return processReturn("setMasterMute", mDevice->setMasterMute(state));
+}
+
+status_t DeviceHalHidl::getMasterMute(bool *state) {
+ if (mDevice == 0) return NO_INIT;
+ Result retval;
+ Return<void> ret = mDevice->getMasterMute(
+ [&](Result r, bool mute) {
+ retval = r;
+ if (retval == Result::OK) {
+ *state = mute;
+ }
+ });
+ return processReturn("getMasterMute", ret, retval);
+}
+
+status_t DeviceHalHidl::setParameters(const String8& kvPairs) {
+ if (mDevice == 0) return NO_INIT;
+ hidl_vec<ParameterValue> hidlParams;
+ status_t status = parametersFromHal(kvPairs, &hidlParams);
+ if (status != OK) return status;
+ // TODO: change the API so that context and kvPairs are separated
+ return processReturn("setParameters",
+ utils::setParameters(mDevice, {} /* context */, hidlParams));
+}
+
+status_t DeviceHalHidl::getParameters(const String8& keys, String8 *values) {
+ values->clear();
+ if (mDevice == 0) return NO_INIT;
+ hidl_vec<hidl_string> hidlKeys;
+ status_t status = keysFromHal(keys, &hidlKeys);
+ if (status != OK) return status;
+ Result retval;
+ Return<void> ret = utils::getParameters(mDevice,
+ {} /* context */,
+ hidlKeys,
+ [&](Result r, const hidl_vec<ParameterValue>& parameters) {
+ retval = r;
+ if (retval == Result::OK) {
+ parametersToHal(parameters, values);
+ }
+ });
+ return processReturn("getParameters", ret, retval);
+}
+
+status_t DeviceHalHidl::getInputBufferSize(
+ const struct audio_config *config, size_t *size) {
+ if (mDevice == 0) return NO_INIT;
+ AudioConfig hidlConfig;
+ HidlUtils::audioConfigFromHal(*config, &hidlConfig);
+ Result retval;
+ Return<void> ret = mDevice->getInputBufferSize(
+ hidlConfig,
+ [&](Result r, uint64_t bufferSize) {
+ retval = r;
+ if (retval == Result::OK) {
+ *size = static_cast<size_t>(bufferSize);
+ }
+ });
+ return processReturn("getInputBufferSize", ret, retval);
+}
+
+status_t DeviceHalHidl::openOutputStream(
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ audio_output_flags_t flags,
+ struct audio_config *config,
+ const char *address,
+ sp<StreamOutHalInterface> *outStream) {
+ if (mDevice == 0) return NO_INIT;
+ DeviceAddress hidlDevice;
+ status_t status = deviceAddressFromHal(devices, address, &hidlDevice);
+ if (status != OK) return status;
+ AudioConfig hidlConfig;
+ HidlUtils::audioConfigFromHal(*config, &hidlConfig);
+ Result retval = Result::NOT_INITIALIZED;
+ Return<void> ret = mDevice->openOutputStream(
+ handle,
+ hidlDevice,
+ hidlConfig,
+ mkEnumConverter<AudioOutputFlag>(flags),
+ {} /* metadata */,
+ [&](Result r, const sp<IStreamOut>& result, const AudioConfig& suggestedConfig) {
+ retval = r;
+ if (retval == Result::OK) {
+ *outStream = new StreamOutHalHidl(result);
+ }
+ HidlUtils::audioConfigToHal(suggestedConfig, config);
+ });
+ return processReturn("openOutputStream", ret, retval);
+}
+
+status_t DeviceHalHidl::openInputStream(
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ struct audio_config *config,
+ audio_input_flags_t flags,
+ const char *address,
+ audio_source_t source,
+ sp<StreamInHalInterface> *inStream) {
+ if (mDevice == 0) return NO_INIT;
+ DeviceAddress hidlDevice;
+ status_t status = deviceAddressFromHal(devices, address, &hidlDevice);
+ if (status != OK) return status;
+ AudioConfig hidlConfig;
+ HidlUtils::audioConfigFromHal(*config, &hidlConfig);
+ Result retval = Result::NOT_INITIALIZED;
+ // TODO: correctly propagate the tracks sources and volume
+ // for now, only send the main source at 1dbfs
+ SinkMetadata metadata = {{{AudioSource(source), 1}}};
+ Return<void> ret = mDevice->openInputStream(
+ handle,
+ hidlDevice,
+ hidlConfig,
+ flags,
+ metadata,
+ [&](Result r, const sp<IStreamIn>& result, const AudioConfig& suggestedConfig) {
+ retval = r;
+ if (retval == Result::OK) {
+ *inStream = new StreamInHalHidl(result);
+ }
+ HidlUtils::audioConfigToHal(suggestedConfig, config);
+ });
+ return processReturn("openInputStream", ret, retval);
+}
+
+status_t DeviceHalHidl::supportsAudioPatches(bool *supportsPatches) {
+ if (mDevice == 0) return NO_INIT;
+ return processReturn("supportsAudioPatches", mDevice->supportsAudioPatches(), supportsPatches);
+}
+
+status_t DeviceHalHidl::createAudioPatch(
+ unsigned int num_sources,
+ const struct audio_port_config *sources,
+ unsigned int num_sinks,
+ const struct audio_port_config *sinks,
+ audio_patch_handle_t *patch) {
+ if (mDevice == 0) return NO_INIT;
+ hidl_vec<AudioPortConfig> hidlSources, hidlSinks;
+ HidlUtils::audioPortConfigsFromHal(num_sources, sources, &hidlSources);
+ HidlUtils::audioPortConfigsFromHal(num_sinks, sinks, &hidlSinks);
+ Result retval;
+ Return<void> ret = mDevice->createAudioPatch(
+ hidlSources, hidlSinks,
+ [&](Result r, AudioPatchHandle hidlPatch) {
+ retval = r;
+ if (retval == Result::OK) {
+ *patch = static_cast<audio_patch_handle_t>(hidlPatch);
+ }
+ });
+ return processReturn("createAudioPatch", ret, retval);
+}
+
+status_t DeviceHalHidl::releaseAudioPatch(audio_patch_handle_t patch) {
+ if (mDevice == 0) return NO_INIT;
+ return processReturn("releaseAudioPatch", mDevice->releaseAudioPatch(patch));
+}
+
+status_t DeviceHalHidl::getAudioPort(struct audio_port *port) {
+ if (mDevice == 0) return NO_INIT;
+ AudioPort hidlPort;
+ HidlUtils::audioPortFromHal(*port, &hidlPort);
+ Result retval;
+ Return<void> ret = mDevice->getAudioPort(
+ hidlPort,
+ [&](Result r, const AudioPort& p) {
+ retval = r;
+ if (retval == Result::OK) {
+ HidlUtils::audioPortToHal(p, port);
+ }
+ });
+ return processReturn("getAudioPort", ret, retval);
+}
+
+status_t DeviceHalHidl::setAudioPortConfig(const struct audio_port_config *config) {
+ if (mDevice == 0) return NO_INIT;
+ AudioPortConfig hidlConfig;
+ HidlUtils::audioPortConfigFromHal(*config, &hidlConfig);
+ return processReturn("setAudioPortConfig", mDevice->setAudioPortConfig(hidlConfig));
+}
+
+status_t DeviceHalHidl::dump(int fd) {
+ if (mDevice == 0) return NO_INIT;
+ native_handle_t* hidlHandle = native_handle_create(1, 0);
+ hidlHandle->data[0] = fd;
+ Return<void> ret = mDevice->debug(hidlHandle, {} /* options */);
+ native_handle_delete(hidlHandle);
+ return processReturn("dump", ret);
+}
+
+} // namespace V4_0
+} // namespace android
diff --git a/media/libaudiohal/4.0/DeviceHalHidl.h b/media/libaudiohal/4.0/DeviceHalHidl.h
new file mode 100644
index 0000000..f460add
--- /dev/null
+++ b/media/libaudiohal/4.0/DeviceHalHidl.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_DEVICE_HAL_HIDL_4_0_H
+#define ANDROID_HARDWARE_DEVICE_HAL_HIDL_4_0_H
+
+#include <android/hardware/audio/4.0/IDevice.h>
+#include <android/hardware/audio/4.0/IPrimaryDevice.h>
+#include <media/audiohal/DeviceHalInterface.h>
+
+#include "ConversionHelperHidl.h"
+
+using ::android::hardware::audio::V4_0::IDevice;
+using ::android::hardware::audio::V4_0::IPrimaryDevice;
+using ::android::hardware::Return;
+
+namespace android {
+namespace V4_0 {
+
+class DeviceHalHidl : public DeviceHalInterface, public ConversionHelperHidl
+{
+ public:
+ // Sets the value of 'devices' to a bitmask of 1 or more values of audio_devices_t.
+ virtual status_t getSupportedDevices(uint32_t *devices);
+
+ // Check to see if the audio hardware interface has been initialized.
+ virtual status_t initCheck();
+
+ // Set the audio volume of a voice call. Range is between 0.0 and 1.0.
+ virtual status_t setVoiceVolume(float volume);
+
+ // Set the audio volume for all audio activities other than voice call.
+ virtual status_t setMasterVolume(float volume);
+
+ // Get the current master volume value for the HAL.
+ virtual status_t getMasterVolume(float *volume);
+
+ // Called when the audio mode changes.
+ virtual status_t setMode(audio_mode_t mode);
+
+ // Muting control.
+ virtual status_t setMicMute(bool state);
+ virtual status_t getMicMute(bool *state);
+ virtual status_t setMasterMute(bool state);
+ virtual status_t getMasterMute(bool *state);
+
+ // Set global audio parameters.
+ virtual status_t setParameters(const String8& kvPairs);
+
+ // Get global audio parameters.
+ virtual status_t getParameters(const String8& keys, String8 *values);
+
+ // Returns audio input buffer size according to parameters passed.
+ virtual status_t getInputBufferSize(const struct audio_config *config,
+ size_t *size);
+
+ // Creates and opens the audio hardware output stream. The stream is closed
+ // by releasing all references to the returned object.
+ virtual status_t openOutputStream(
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ audio_output_flags_t flags,
+ struct audio_config *config,
+ const char *address,
+ sp<StreamOutHalInterface> *outStream);
+
+ // Creates and opens the audio hardware input stream. The stream is closed
+ // by releasing all references to the returned object.
+ virtual status_t openInputStream(
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ struct audio_config *config,
+ audio_input_flags_t flags,
+ const char *address,
+ audio_source_t source,
+ sp<StreamInHalInterface> *inStream);
+
+ // Returns whether createAudioPatch and releaseAudioPatch operations are supported.
+ virtual status_t supportsAudioPatches(bool *supportsPatches);
+
+ // Creates an audio patch between several source and sink ports.
+ virtual status_t createAudioPatch(
+ unsigned int num_sources,
+ const struct audio_port_config *sources,
+ unsigned int num_sinks,
+ const struct audio_port_config *sinks,
+ audio_patch_handle_t *patch);
+
+ // Releases an audio patch.
+ virtual status_t releaseAudioPatch(audio_patch_handle_t patch);
+
+ // Fills the list of supported attributes for a given audio port.
+ virtual status_t getAudioPort(struct audio_port *port);
+
+ // Set audio port configuration.
+ virtual status_t setAudioPortConfig(const struct audio_port_config *config);
+
+ virtual status_t dump(int fd);
+
+ private:
+ friend class DevicesFactoryHalHidl;
+ sp<IDevice> mDevice;
+ sp<IPrimaryDevice> mPrimaryDevice; // Null if it's not a primary device.
+
+ // Can not be constructed directly by clients.
+ explicit DeviceHalHidl(const sp<IDevice>& device);
+
+ // The destructor automatically closes the device.
+ virtual ~DeviceHalHidl();
+};
+
+} // namespace V4_0
+} // namespace android
+
+#endif // ANDROID_HARDWARE_DEVICE_HAL_HIDL_4_0_H
diff --git a/media/libaudiohal/4.0/DeviceHalLocal.cpp b/media/libaudiohal/4.0/DeviceHalLocal.cpp
new file mode 100644
index 0000000..e64eee1
--- /dev/null
+++ b/media/libaudiohal/4.0/DeviceHalLocal.cpp
@@ -0,0 +1,201 @@
+/*
+ * 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 "DeviceHalLocal"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+
+#include "DeviceHalLocal.h"
+#include "StreamHalLocal.h"
+
+namespace android {
+namespace V4_0 {
+
+DeviceHalLocal::DeviceHalLocal(audio_hw_device_t *dev)
+ : mDev(dev) {
+}
+
+DeviceHalLocal::~DeviceHalLocal() {
+ int status = audio_hw_device_close(mDev);
+ ALOGW_IF(status, "Error closing audio hw device %p: %s", mDev, strerror(-status));
+ mDev = 0;
+}
+
+status_t DeviceHalLocal::getSupportedDevices(uint32_t *devices) {
+ if (mDev->get_supported_devices == NULL) return INVALID_OPERATION;
+ *devices = mDev->get_supported_devices(mDev);
+ return OK;
+}
+
+status_t DeviceHalLocal::initCheck() {
+ return mDev->init_check(mDev);
+}
+
+status_t DeviceHalLocal::setVoiceVolume(float volume) {
+ return mDev->set_voice_volume(mDev, volume);
+}
+
+status_t DeviceHalLocal::setMasterVolume(float volume) {
+ if (mDev->set_master_volume == NULL) return INVALID_OPERATION;
+ return mDev->set_master_volume(mDev, volume);
+}
+
+status_t DeviceHalLocal::getMasterVolume(float *volume) {
+ if (mDev->get_master_volume == NULL) return INVALID_OPERATION;
+ return mDev->get_master_volume(mDev, volume);
+}
+
+status_t DeviceHalLocal::setMode(audio_mode_t mode) {
+ return mDev->set_mode(mDev, mode);
+}
+
+status_t DeviceHalLocal::setMicMute(bool state) {
+ return mDev->set_mic_mute(mDev, state);
+}
+
+status_t DeviceHalLocal::getMicMute(bool *state) {
+ return mDev->get_mic_mute(mDev, state);
+}
+
+status_t DeviceHalLocal::setMasterMute(bool state) {
+ if (mDev->set_master_mute == NULL) return INVALID_OPERATION;
+ return mDev->set_master_mute(mDev, state);
+}
+
+status_t DeviceHalLocal::getMasterMute(bool *state) {
+ if (mDev->get_master_mute == NULL) return INVALID_OPERATION;
+ return mDev->get_master_mute(mDev, state);
+}
+
+status_t DeviceHalLocal::setParameters(const String8& kvPairs) {
+ return mDev->set_parameters(mDev, kvPairs.string());
+}
+
+status_t DeviceHalLocal::getParameters(const String8& keys, String8 *values) {
+ char *halValues = mDev->get_parameters(mDev, keys.string());
+ if (halValues != NULL) {
+ values->setTo(halValues);
+ free(halValues);
+ } else {
+ values->clear();
+ }
+ return OK;
+}
+
+status_t DeviceHalLocal::getInputBufferSize(
+ const struct audio_config *config, size_t *size) {
+ *size = mDev->get_input_buffer_size(mDev, config);
+ return OK;
+}
+
+status_t DeviceHalLocal::openOutputStream(
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ audio_output_flags_t flags,
+ struct audio_config *config,
+ const char *address,
+ sp<StreamOutHalInterface> *outStream) {
+ audio_stream_out_t *halStream;
+ ALOGV("open_output_stream handle: %d devices: %x flags: %#x"
+ "srate: %d format %#x channels %x address %s",
+ handle, devices, flags,
+ config->sample_rate, config->format, config->channel_mask,
+ address);
+ int openResut = mDev->open_output_stream(
+ mDev, handle, devices, flags, config, &halStream, address);
+ if (openResut == OK) {
+ *outStream = new StreamOutHalLocal(halStream, this);
+ }
+ ALOGV("open_output_stream status %d stream %p", openResut, halStream);
+ return openResut;
+}
+
+status_t DeviceHalLocal::openInputStream(
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ struct audio_config *config,
+ audio_input_flags_t flags,
+ const char *address,
+ audio_source_t source,
+ sp<StreamInHalInterface> *inStream) {
+ audio_stream_in_t *halStream;
+ ALOGV("open_input_stream handle: %d devices: %x flags: %#x "
+ "srate: %d format %#x channels %x address %s source %d",
+ handle, devices, flags,
+ config->sample_rate, config->format, config->channel_mask,
+ address, source);
+ int openResult = mDev->open_input_stream(
+ mDev, handle, devices, config, &halStream, flags, address, source);
+ if (openResult == OK) {
+ *inStream = new StreamInHalLocal(halStream, this);
+ }
+ ALOGV("open_input_stream status %d stream %p", openResult, inStream);
+ return openResult;
+}
+
+status_t DeviceHalLocal::supportsAudioPatches(bool *supportsPatches) {
+ *supportsPatches = version() >= AUDIO_DEVICE_API_VERSION_3_0;
+ return OK;
+}
+
+status_t DeviceHalLocal::createAudioPatch(
+ unsigned int num_sources,
+ const struct audio_port_config *sources,
+ unsigned int num_sinks,
+ const struct audio_port_config *sinks,
+ audio_patch_handle_t *patch) {
+ if (version() >= AUDIO_DEVICE_API_VERSION_3_0) {
+ return mDev->create_audio_patch(
+ mDev, num_sources, sources, num_sinks, sinks, patch);
+ } else {
+ return INVALID_OPERATION;
+ }
+}
+
+status_t DeviceHalLocal::releaseAudioPatch(audio_patch_handle_t patch) {
+ if (version() >= AUDIO_DEVICE_API_VERSION_3_0) {
+ return mDev->release_audio_patch(mDev, patch);
+ } else {
+ return INVALID_OPERATION;
+ }
+}
+
+status_t DeviceHalLocal::getAudioPort(struct audio_port *port) {
+ return mDev->get_audio_port(mDev, port);
+}
+
+status_t DeviceHalLocal::setAudioPortConfig(const struct audio_port_config *config) {
+ if (version() >= AUDIO_DEVICE_API_VERSION_3_0)
+ return mDev->set_audio_port_config(mDev, config);
+ else
+ return INVALID_OPERATION;
+}
+
+status_t DeviceHalLocal::dump(int fd) {
+ return mDev->dump(mDev, fd);
+}
+
+void DeviceHalLocal::closeOutputStream(struct audio_stream_out *stream_out) {
+ mDev->close_output_stream(mDev, stream_out);
+}
+
+void DeviceHalLocal::closeInputStream(struct audio_stream_in *stream_in) {
+ mDev->close_input_stream(mDev, stream_in);
+}
+
+} // namespace V4_0
+} // namespace android
diff --git a/media/libaudiohal/4.0/DeviceHalLocal.h b/media/libaudiohal/4.0/DeviceHalLocal.h
new file mode 100644
index 0000000..daafdc7
--- /dev/null
+++ b/media/libaudiohal/4.0/DeviceHalLocal.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_DEVICE_HAL_LOCAL_4_0_H
+#define ANDROID_HARDWARE_DEVICE_HAL_LOCAL_4_0_H
+
+#include <hardware/audio.h>
+#include <media/audiohal/DeviceHalInterface.h>
+
+namespace android {
+namespace V4_0 {
+
+class DeviceHalLocal : public DeviceHalInterface
+{
+ public:
+ // Sets the value of 'devices' to a bitmask of 1 or more values of audio_devices_t.
+ virtual status_t getSupportedDevices(uint32_t *devices);
+
+ // Check to see if the audio hardware interface has been initialized.
+ virtual status_t initCheck();
+
+ // Set the audio volume of a voice call. Range is between 0.0 and 1.0.
+ virtual status_t setVoiceVolume(float volume);
+
+ // Set the audio volume for all audio activities other than voice call.
+ virtual status_t setMasterVolume(float volume);
+
+ // Get the current master volume value for the HAL.
+ virtual status_t getMasterVolume(float *volume);
+
+ // Called when the audio mode changes.
+ virtual status_t setMode(audio_mode_t mode);
+
+ // Muting control.
+ virtual status_t setMicMute(bool state);
+ virtual status_t getMicMute(bool *state);
+ virtual status_t setMasterMute(bool state);
+ virtual status_t getMasterMute(bool *state);
+
+ // Set global audio parameters.
+ virtual status_t setParameters(const String8& kvPairs);
+
+ // Get global audio parameters.
+ virtual status_t getParameters(const String8& keys, String8 *values);
+
+ // Returns audio input buffer size according to parameters passed.
+ virtual status_t getInputBufferSize(const struct audio_config *config,
+ size_t *size);
+
+ // Creates and opens the audio hardware output stream. The stream is closed
+ // by releasing all references to the returned object.
+ virtual status_t openOutputStream(
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ audio_output_flags_t flags,
+ struct audio_config *config,
+ const char *address,
+ sp<StreamOutHalInterface> *outStream);
+
+ // Creates and opens the audio hardware input stream. The stream is closed
+ // by releasing all references to the returned object.
+ virtual status_t openInputStream(
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ struct audio_config *config,
+ audio_input_flags_t flags,
+ const char *address,
+ audio_source_t source,
+ sp<StreamInHalInterface> *inStream);
+
+ // Returns whether createAudioPatch and releaseAudioPatch operations are supported.
+ virtual status_t supportsAudioPatches(bool *supportsPatches);
+
+ // Creates an audio patch between several source and sink ports.
+ virtual status_t createAudioPatch(
+ unsigned int num_sources,
+ const struct audio_port_config *sources,
+ unsigned int num_sinks,
+ const struct audio_port_config *sinks,
+ audio_patch_handle_t *patch);
+
+ // Releases an audio patch.
+ virtual status_t releaseAudioPatch(audio_patch_handle_t patch);
+
+ // Fills the list of supported attributes for a given audio port.
+ virtual status_t getAudioPort(struct audio_port *port);
+
+ // Set audio port configuration.
+ virtual status_t setAudioPortConfig(const struct audio_port_config *config);
+
+ virtual status_t dump(int fd);
+
+ void closeOutputStream(struct audio_stream_out *stream_out);
+ void closeInputStream(struct audio_stream_in *stream_in);
+
+ private:
+ audio_hw_device_t *mDev;
+
+ friend class DevicesFactoryHalLocal;
+
+ // Can not be constructed directly by clients.
+ explicit DeviceHalLocal(audio_hw_device_t *dev);
+
+ // The destructor automatically closes the device.
+ virtual ~DeviceHalLocal();
+
+ uint32_t version() const { return mDev->common.version; }
+};
+
+} // namespace V4_0
+} // namespace android
+
+#endif // ANDROID_HARDWARE_DEVICE_HAL_LOCAL_4_0_H
diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp b/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp
new file mode 100644
index 0000000..c83194e
--- /dev/null
+++ b/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+#include <string.h>
+
+#define LOG_TAG "DevicesFactoryHalHidl"
+//#define LOG_NDEBUG 0
+
+#include <android/hardware/audio/4.0/IDevice.h>
+#include <media/audiohal/hidl/HalDeathHandler.h>
+#include <utils/Log.h>
+
+#include "ConversionHelperHidl.h"
+#include "DeviceHalHidl.h"
+#include "DevicesFactoryHalHidl.h"
+
+using ::android::hardware::audio::V4_0::IDevice;
+using ::android::hardware::audio::V4_0::Result;
+using ::android::hardware::Return;
+
+namespace android {
+namespace V4_0 {
+
+DevicesFactoryHalHidl::DevicesFactoryHalHidl() {
+ mDevicesFactory = IDevicesFactory::getService();
+ if (mDevicesFactory != 0) {
+ // It is assumed that DevicesFactory is owned by AudioFlinger
+ // and thus have the same lifespan.
+ mDevicesFactory->linkToDeath(HalDeathHandler::getInstance(), 0 /*cookie*/);
+ } else {
+ ALOGE("Failed to obtain IDevicesFactory service, terminating process.");
+ exit(1);
+ }
+ // The MSD factory is optional
+ mDevicesFactoryMsd = IDevicesFactory::getService(AUDIO_HAL_SERVICE_NAME_MSD);
+ // TODO: Register death handler, and add 'restart' directive to audioserver.rc
+}
+
+DevicesFactoryHalHidl::~DevicesFactoryHalHidl() {
+}
+
+status_t DevicesFactoryHalHidl::openDevice(const char *name, sp<DeviceHalInterface> *device) {
+ if (mDevicesFactory == 0) return NO_INIT;
+ Result retval = Result::NOT_INITIALIZED;
+ Return<void> ret = mDevicesFactory->openDevice(
+ name,
+ [&](Result r, const sp<IDevice>& result) {
+ retval = r;
+ if (retval == Result::OK) {
+ *device = new DeviceHalHidl(result);
+ }
+ });
+ if (ret.isOk()) {
+ if (retval == Result::OK) return OK;
+ else if (retval == Result::INVALID_ARGUMENTS) return BAD_VALUE;
+ else return NO_INIT;
+ }
+ return FAILED_TRANSACTION;
+}
+
+} // namespace V4_0
+} // namespace android
diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHidl.h b/media/libaudiohal/4.0/DevicesFactoryHalHidl.h
new file mode 100644
index 0000000..114889b
--- /dev/null
+++ b/media/libaudiohal/4.0/DevicesFactoryHalHidl.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_4_0_H
+#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_4_0_H
+
+#include <android/hardware/audio/4.0/IDevicesFactory.h>
+#include <media/audiohal/DevicesFactoryHalInterface.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include "DeviceHalHidl.h"
+
+using ::android::hardware::audio::V4_0::IDevicesFactory;
+
+namespace android {
+namespace V4_0 {
+
+class DevicesFactoryHalHidl : public DevicesFactoryHalInterface
+{
+ public:
+ // Opens a device with the specified name. To close the device, it is
+ // necessary to release references to the returned object.
+ virtual status_t openDevice(const char *name, sp<DeviceHalInterface> *device);
+
+ private:
+ friend class DevicesFactoryHalHybrid;
+
+ sp<IDevicesFactory> mDevicesFactory;
+ sp<IDevicesFactory> mDevicesFactoryMsd;
+
+ // Can not be constructed directly by clients.
+ DevicesFactoryHalHidl();
+
+ virtual ~DevicesFactoryHalHidl();
+};
+
+} // namespace V4_0
+} // namespace android
+
+#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_4_0_H
diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHybrid.cpp b/media/libaudiohal/4.0/DevicesFactoryHalHybrid.cpp
new file mode 100644
index 0000000..c43509c
--- /dev/null
+++ b/media/libaudiohal/4.0/DevicesFactoryHalHybrid.cpp
@@ -0,0 +1,43 @@
+/*
+ * 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 "DevicesFactoryHalHybrid"
+//#define LOG_NDEBUG 0
+
+#include <libaudiohal/4.0/DevicesFactoryHalHybrid.h>
+#include "DevicesFactoryHalLocal.h"
+#include "DevicesFactoryHalHidl.h"
+
+namespace android {
+namespace V4_0 {
+
+DevicesFactoryHalHybrid::DevicesFactoryHalHybrid()
+ : mLocalFactory(new DevicesFactoryHalLocal()),
+ mHidlFactory(new DevicesFactoryHalHidl()) {
+}
+
+DevicesFactoryHalHybrid::~DevicesFactoryHalHybrid() {
+}
+
+status_t DevicesFactoryHalHybrid::openDevice(const char *name, sp<DeviceHalInterface> *device) {
+ if (mHidlFactory != 0 && strcmp(AUDIO_HARDWARE_MODULE_ID_A2DP, name) != 0) {
+ return mHidlFactory->openDevice(name, device);
+ }
+ return mLocalFactory->openDevice(name, device);
+}
+
+} // namespace V4_0
+} // namespace android
diff --git a/media/libaudiohal/4.0/DevicesFactoryHalLocal.cpp b/media/libaudiohal/4.0/DevicesFactoryHalLocal.cpp
new file mode 100644
index 0000000..e54edd4
--- /dev/null
+++ b/media/libaudiohal/4.0/DevicesFactoryHalLocal.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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 "DevicesFactoryHalLocal"
+//#define LOG_NDEBUG 0
+
+#include <string.h>
+
+#include <hardware/audio.h>
+#include <utils/Log.h>
+
+#include "DeviceHalLocal.h"
+#include "DevicesFactoryHalLocal.h"
+
+namespace android {
+namespace V4_0 {
+
+static status_t load_audio_interface(const char *if_name, audio_hw_device_t **dev)
+{
+ const hw_module_t *mod;
+ int rc;
+
+ rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod);
+ if (rc) {
+ ALOGE("%s couldn't load audio hw module %s.%s (%s)", __func__,
+ AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc));
+ goto out;
+ }
+ rc = audio_hw_device_open(mod, dev);
+ if (rc) {
+ ALOGE("%s couldn't open audio hw device in %s.%s (%s)", __func__,
+ AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc));
+ goto out;
+ }
+ if ((*dev)->common.version < AUDIO_DEVICE_API_VERSION_MIN) {
+ ALOGE("%s wrong audio hw device version %04x", __func__, (*dev)->common.version);
+ rc = BAD_VALUE;
+ audio_hw_device_close(*dev);
+ goto out;
+ }
+ return OK;
+
+out:
+ *dev = NULL;
+ return rc;
+}
+
+status_t DevicesFactoryHalLocal::openDevice(const char *name, sp<DeviceHalInterface> *device) {
+ audio_hw_device_t *dev;
+ status_t rc = load_audio_interface(name, &dev);
+ if (rc == OK) {
+ *device = new DeviceHalLocal(dev);
+ }
+ return rc;
+}
+
+} // namespace V4_0
+} // namespace android
diff --git a/media/libaudiohal/4.0/DevicesFactoryHalLocal.h b/media/libaudiohal/4.0/DevicesFactoryHalLocal.h
new file mode 100644
index 0000000..bc1c521
--- /dev/null
+++ b/media/libaudiohal/4.0/DevicesFactoryHalLocal.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_DEVICES_FACTORY_HAL_LOCAL_4_0_H
+#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_LOCAL_4_0_H
+
+#include <media/audiohal/DevicesFactoryHalInterface.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include "DeviceHalLocal.h"
+
+namespace android {
+namespace V4_0 {
+
+class DevicesFactoryHalLocal : public DevicesFactoryHalInterface
+{
+ public:
+ // Opens a device with the specified name. To close the device, it is
+ // necessary to release references to the returned object.
+ virtual status_t openDevice(const char *name, sp<DeviceHalInterface> *device);
+
+ private:
+ friend class DevicesFactoryHalHybrid;
+
+ // Can not be constructed directly by clients.
+ DevicesFactoryHalLocal() {}
+
+ virtual ~DevicesFactoryHalLocal() {}
+};
+
+} // namespace V4_0
+} // namespace android
+
+#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_LOCAL_4_0_H
diff --git a/media/libaudiohal/4.0/EffectBufferHalHidl.cpp b/media/libaudiohal/4.0/EffectBufferHalHidl.cpp
new file mode 100644
index 0000000..957c89f
--- /dev/null
+++ b/media/libaudiohal/4.0/EffectBufferHalHidl.cpp
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+#include <atomic>
+
+#define LOG_TAG "EffectBufferHalHidl"
+//#define LOG_NDEBUG 0
+
+#include <android/hidl/allocator/1.0/IAllocator.h>
+#include <hidlmemory/mapping.h>
+#include <utils/Log.h>
+
+#include "ConversionHelperHidl.h"
+#include "EffectBufferHalHidl.h"
+
+using ::android::hardware::Return;
+using ::android::hidl::allocator::V1_0::IAllocator;
+
+namespace android {
+namespace V4_0 {
+
+// static
+uint64_t EffectBufferHalHidl::makeUniqueId() {
+ static std::atomic<uint64_t> counter{1};
+ return counter++;
+}
+
+status_t EffectBufferHalHidl::allocate(
+ size_t size, sp<EffectBufferHalInterface>* buffer) {
+ return mirror(nullptr, size, buffer);
+}
+
+status_t EffectBufferHalHidl::mirror(
+ void* external, size_t size, sp<EffectBufferHalInterface>* buffer) {
+ sp<EffectBufferHalInterface> tempBuffer = new EffectBufferHalHidl(size);
+ status_t result = static_cast<EffectBufferHalHidl*>(tempBuffer.get())->init();
+ if (result == OK) {
+ tempBuffer->setExternalData(external);
+ *buffer = tempBuffer;
+ }
+ return result;
+}
+
+EffectBufferHalHidl::EffectBufferHalHidl(size_t size)
+ : mBufferSize(size), mFrameCountChanged(false),
+ mExternalData(nullptr), mAudioBuffer{0, {nullptr}} {
+ mHidlBuffer.id = makeUniqueId();
+ mHidlBuffer.frameCount = 0;
+}
+
+EffectBufferHalHidl::~EffectBufferHalHidl() {
+}
+
+status_t EffectBufferHalHidl::init() {
+ sp<IAllocator> ashmem = IAllocator::getService("ashmem");
+ if (ashmem == 0) {
+ ALOGE("Failed to retrieve ashmem allocator service");
+ return NO_INIT;
+ }
+ status_t retval = NO_MEMORY;
+ Return<void> result = ashmem->allocate(
+ mBufferSize,
+ [&](bool success, const hidl_memory& memory) {
+ if (success) {
+ mHidlBuffer.data = memory;
+ retval = OK;
+ }
+ });
+ if (result.isOk() && retval == OK) {
+ mMemory = hardware::mapMemory(mHidlBuffer.data);
+ if (mMemory != 0) {
+ mMemory->update();
+ mAudioBuffer.raw = static_cast<void*>(mMemory->getPointer());
+ memset(mAudioBuffer.raw, 0, mMemory->getSize());
+ mMemory->commit();
+ } else {
+ ALOGE("Failed to map allocated ashmem");
+ retval = NO_MEMORY;
+ }
+ } else {
+ ALOGE("Failed to allocate %d bytes from ashmem", (int)mBufferSize);
+ }
+ return result.isOk() ? retval : FAILED_TRANSACTION;
+}
+
+audio_buffer_t* EffectBufferHalHidl::audioBuffer() {
+ return &mAudioBuffer;
+}
+
+void* EffectBufferHalHidl::externalData() const {
+ return mExternalData;
+}
+
+void EffectBufferHalHidl::setFrameCount(size_t frameCount) {
+ mHidlBuffer.frameCount = frameCount;
+ mAudioBuffer.frameCount = frameCount;
+ mFrameCountChanged = true;
+}
+
+bool EffectBufferHalHidl::checkFrameCountChange() {
+ bool result = mFrameCountChanged;
+ mFrameCountChanged = false;
+ return result;
+}
+
+void EffectBufferHalHidl::setExternalData(void* external) {
+ mExternalData = external;
+}
+
+void EffectBufferHalHidl::update() {
+ update(mBufferSize);
+}
+
+void EffectBufferHalHidl::commit() {
+ commit(mBufferSize);
+}
+
+void EffectBufferHalHidl::update(size_t size) {
+ if (mExternalData == nullptr) return;
+ mMemory->update();
+ if (size > mBufferSize) size = mBufferSize;
+ memcpy(mAudioBuffer.raw, mExternalData, size);
+ mMemory->commit();
+}
+
+void EffectBufferHalHidl::commit(size_t size) {
+ if (mExternalData == nullptr) return;
+ if (size > mBufferSize) size = mBufferSize;
+ memcpy(mExternalData, mAudioBuffer.raw, size);
+}
+
+} // namespace V4_0
+} // namespace android
diff --git a/media/libaudiohal/4.0/EffectBufferHalHidl.h b/media/libaudiohal/4.0/EffectBufferHalHidl.h
new file mode 100644
index 0000000..6d578c6
--- /dev/null
+++ b/media/libaudiohal/4.0/EffectBufferHalHidl.h
@@ -0,0 +1,78 @@
+/*
+ * 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_HARDWARE_EFFECT_BUFFER_HAL_HIDL_4_0_H
+#define ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_4_0_H
+
+#include <android/hardware/audio/effect/4.0/types.h>
+#include <android/hidl/memory/1.0/IMemory.h>
+#include <hidl/HidlSupport.h>
+#include <media/audiohal/EffectBufferHalInterface.h>
+#include <system/audio_effect.h>
+
+using android::hardware::audio::effect::V4_0::AudioBuffer;
+using android::hardware::hidl_memory;
+using android::hidl::memory::V1_0::IMemory;
+
+namespace android {
+namespace V4_0 {
+
+class EffectBufferHalHidl : public EffectBufferHalInterface
+{
+ public:
+ static status_t allocate(size_t size, sp<EffectBufferHalInterface>* buffer);
+ static status_t mirror(void* external, size_t size, sp<EffectBufferHalInterface>* buffer);
+
+ virtual audio_buffer_t* audioBuffer();
+ virtual void* externalData() const;
+
+ virtual size_t getSize() const override { return mBufferSize; }
+
+ virtual void setExternalData(void* external);
+ virtual void setFrameCount(size_t frameCount);
+ virtual bool checkFrameCountChange();
+
+ virtual void update();
+ virtual void commit();
+ virtual void update(size_t size);
+ virtual void commit(size_t size);
+
+ const AudioBuffer& hidlBuffer() const { return mHidlBuffer; }
+
+ private:
+ friend class EffectBufferHalInterface;
+
+ static uint64_t makeUniqueId();
+
+ const size_t mBufferSize;
+ bool mFrameCountChanged;
+ void* mExternalData;
+ AudioBuffer mHidlBuffer;
+ sp<IMemory> mMemory;
+ audio_buffer_t mAudioBuffer;
+
+ // Can not be constructed directly by clients.
+ explicit EffectBufferHalHidl(size_t size);
+
+ virtual ~EffectBufferHalHidl();
+
+ status_t init();
+};
+
+} // namespace V4_0
+} // namespace android
+
+#endif // ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_4_0_H
diff --git a/media/libaudiohal/4.0/EffectHalHidl.cpp b/media/libaudiohal/4.0/EffectHalHidl.cpp
new file mode 100644
index 0000000..c99c4c8
--- /dev/null
+++ b/media/libaudiohal/4.0/EffectHalHidl.cpp
@@ -0,0 +1,342 @@
+/*
+ * 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 "EffectHalHidl"
+//#define LOG_NDEBUG 0
+
+#include <common/all-versions/VersionUtils.h>
+#include <hwbinder/IPCThreadState.h>
+#include <media/EffectsFactoryApi.h>
+#include <utils/Log.h>
+
+#include "ConversionHelperHidl.h"
+#include "EffectBufferHalHidl.h"
+#include "EffectHalHidl.h"
+#include "HidlUtils.h"
+
+using ::android::hardware::audio::effect::V4_0::AudioBuffer;
+using ::android::hardware::audio::effect::V4_0::EffectBufferAccess;
+using ::android::hardware::audio::effect::V4_0::EffectConfigParameters;
+using ::android::hardware::audio::effect::V4_0::MessageQueueFlagBits;
+using ::android::hardware::audio::effect::V4_0::Result;
+using ::android::hardware::audio::common::V4_0::HidlUtils;
+using ::android::hardware::audio::common::V4_0::AudioChannelMask;
+using ::android::hardware::audio::common::V4_0::AudioFormat;
+using ::android::hardware::audio::common::utils::mkEnumConverter;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::MQDescriptorSync;
+using ::android::hardware::Return;
+
+namespace android {
+namespace V4_0 {
+
+EffectHalHidl::EffectHalHidl(const sp<IEffect>& effect, uint64_t effectId)
+ : mEffect(effect), mEffectId(effectId), mBuffersChanged(true), mEfGroup(nullptr) {
+}
+
+EffectHalHidl::~EffectHalHidl() {
+ if (mEffect != 0) {
+ close();
+ mEffect.clear();
+ hardware::IPCThreadState::self()->flushCommands();
+ }
+ if (mEfGroup) {
+ EventFlag::deleteEventFlag(&mEfGroup);
+ }
+}
+
+// static
+void EffectHalHidl::effectDescriptorToHal(
+ const EffectDescriptor& descriptor, effect_descriptor_t* halDescriptor) {
+ HidlUtils::uuidToHal(descriptor.type, &halDescriptor->type);
+ HidlUtils::uuidToHal(descriptor.uuid, &halDescriptor->uuid);
+ halDescriptor->flags = static_cast<uint32_t>(descriptor.flags);
+ halDescriptor->cpuLoad = descriptor.cpuLoad;
+ halDescriptor->memoryUsage = descriptor.memoryUsage;
+ memcpy(halDescriptor->name, descriptor.name.data(), descriptor.name.size());
+ memcpy(halDescriptor->implementor,
+ descriptor.implementor.data(), descriptor.implementor.size());
+}
+
+// TODO(mnaganov): These buffer conversion functions should be shared with Effect wrapper
+// via HidlUtils. Move them there when hardware/interfaces will get un-frozen again.
+
+// static
+void EffectHalHidl::effectBufferConfigFromHal(
+ const buffer_config_t& halConfig, EffectBufferConfig* config) {
+ config->samplingRateHz = halConfig.samplingRate;
+ config->channels = mkEnumConverter<AudioChannelMask>(halConfig.channels);
+ config->format = AudioFormat(halConfig.format);
+ config->accessMode = EffectBufferAccess(halConfig.accessMode);
+ config->mask = mkEnumConverter<EffectConfigParameters>(halConfig.mask);
+}
+
+// static
+void EffectHalHidl::effectBufferConfigToHal(
+ const EffectBufferConfig& config, buffer_config_t* halConfig) {
+ halConfig->buffer.frameCount = 0;
+ halConfig->buffer.raw = NULL;
+ halConfig->samplingRate = config.samplingRateHz;
+ halConfig->channels = static_cast<uint32_t>(config.channels);
+ halConfig->bufferProvider.cookie = NULL;
+ halConfig->bufferProvider.getBuffer = NULL;
+ halConfig->bufferProvider.releaseBuffer = NULL;
+ halConfig->format = static_cast<uint8_t>(config.format);
+ halConfig->accessMode = static_cast<uint8_t>(config.accessMode);
+ halConfig->mask = static_cast<uint8_t>(config.mask);
+}
+
+// static
+void EffectHalHidl::effectConfigFromHal(const effect_config_t& halConfig, EffectConfig* config) {
+ effectBufferConfigFromHal(halConfig.inputCfg, &config->inputCfg);
+ effectBufferConfigFromHal(halConfig.outputCfg, &config->outputCfg);
+}
+
+// static
+void EffectHalHidl::effectConfigToHal(const EffectConfig& config, effect_config_t* halConfig) {
+ effectBufferConfigToHal(config.inputCfg, &halConfig->inputCfg);
+ effectBufferConfigToHal(config.outputCfg, &halConfig->outputCfg);
+}
+
+// static
+status_t EffectHalHidl::analyzeResult(const Result& result) {
+ switch (result) {
+ case Result::OK: return OK;
+ case Result::INVALID_ARGUMENTS: return BAD_VALUE;
+ case Result::INVALID_STATE: return NOT_ENOUGH_DATA;
+ case Result::NOT_INITIALIZED: return NO_INIT;
+ case Result::NOT_SUPPORTED: return INVALID_OPERATION;
+ case Result::RESULT_TOO_BIG: return NO_MEMORY;
+ default: return NO_INIT;
+ }
+}
+
+status_t EffectHalHidl::setInBuffer(const sp<EffectBufferHalInterface>& buffer) {
+ if (!mBuffersChanged) {
+ if (buffer.get() == nullptr || mInBuffer.get() == nullptr) {
+ mBuffersChanged = buffer.get() != mInBuffer.get();
+ } else {
+ mBuffersChanged = buffer->audioBuffer() != mInBuffer->audioBuffer();
+ }
+ }
+ mInBuffer = buffer;
+ return OK;
+}
+
+status_t EffectHalHidl::setOutBuffer(const sp<EffectBufferHalInterface>& buffer) {
+ if (!mBuffersChanged) {
+ if (buffer.get() == nullptr || mOutBuffer.get() == nullptr) {
+ mBuffersChanged = buffer.get() != mOutBuffer.get();
+ } else {
+ mBuffersChanged = buffer->audioBuffer() != mOutBuffer->audioBuffer();
+ }
+ }
+ mOutBuffer = buffer;
+ return OK;
+}
+
+status_t EffectHalHidl::process() {
+ return processImpl(static_cast<uint32_t>(MessageQueueFlagBits::REQUEST_PROCESS));
+}
+
+status_t EffectHalHidl::processReverse() {
+ return processImpl(static_cast<uint32_t>(MessageQueueFlagBits::REQUEST_PROCESS_REVERSE));
+}
+
+status_t EffectHalHidl::prepareForProcessing() {
+ std::unique_ptr<StatusMQ> tempStatusMQ;
+ Result retval;
+ Return<void> ret = mEffect->prepareForProcessing(
+ [&](Result r, const MQDescriptorSync<Result>& statusMQ) {
+ retval = r;
+ if (retval == Result::OK) {
+ tempStatusMQ.reset(new StatusMQ(statusMQ));
+ if (tempStatusMQ->isValid() && tempStatusMQ->getEventFlagWord()) {
+ EventFlag::createEventFlag(tempStatusMQ->getEventFlagWord(), &mEfGroup);
+ }
+ }
+ });
+ if (!ret.isOk() || retval != Result::OK) {
+ return ret.isOk() ? analyzeResult(retval) : FAILED_TRANSACTION;
+ }
+ if (!tempStatusMQ || !tempStatusMQ->isValid() || !mEfGroup) {
+ ALOGE_IF(!tempStatusMQ, "Failed to obtain status message queue for effects");
+ ALOGE_IF(tempStatusMQ && !tempStatusMQ->isValid(),
+ "Status message queue for effects is invalid");
+ ALOGE_IF(!mEfGroup, "Event flag creation for effects failed");
+ return NO_INIT;
+ }
+ mStatusMQ = std::move(tempStatusMQ);
+ return OK;
+}
+
+bool EffectHalHidl::needToResetBuffers() {
+ if (mBuffersChanged) return true;
+ bool inBufferFrameCountUpdated = mInBuffer->checkFrameCountChange();
+ bool outBufferFrameCountUpdated = mOutBuffer->checkFrameCountChange();
+ return inBufferFrameCountUpdated || outBufferFrameCountUpdated;
+}
+
+status_t EffectHalHidl::processImpl(uint32_t mqFlag) {
+ if (mEffect == 0 || mInBuffer == 0 || mOutBuffer == 0) return NO_INIT;
+ status_t status;
+ if (!mStatusMQ && (status = prepareForProcessing()) != OK) {
+ return status;
+ }
+ if (needToResetBuffers() && (status = setProcessBuffers()) != OK) {
+ return status;
+ }
+ // The data is already in the buffers, just need to flush it and wake up the server side.
+ std::atomic_thread_fence(std::memory_order_release);
+ mEfGroup->wake(mqFlag);
+ uint32_t efState = 0;
+retry:
+ status_t ret = mEfGroup->wait(
+ static_cast<uint32_t>(MessageQueueFlagBits::DONE_PROCESSING), &efState);
+ if (efState & static_cast<uint32_t>(MessageQueueFlagBits::DONE_PROCESSING)) {
+ Result retval = Result::NOT_INITIALIZED;
+ mStatusMQ->read(&retval);
+ if (retval == Result::OK || retval == Result::INVALID_STATE) {
+ // Sync back the changed contents of the buffer.
+ std::atomic_thread_fence(std::memory_order_acquire);
+ }
+ return analyzeResult(retval);
+ }
+ if (ret == -EAGAIN || ret == -EINTR) {
+ // Spurious wakeup. This normally retries no more than once.
+ goto retry;
+ }
+ return ret;
+}
+
+status_t EffectHalHidl::setProcessBuffers() {
+ Return<Result> ret = mEffect->setProcessBuffers(
+ static_cast<EffectBufferHalHidl*>(mInBuffer.get())->hidlBuffer(),
+ static_cast<EffectBufferHalHidl*>(mOutBuffer.get())->hidlBuffer());
+ if (ret.isOk() && ret == Result::OK) {
+ mBuffersChanged = false;
+ return OK;
+ }
+ return ret.isOk() ? analyzeResult(ret) : FAILED_TRANSACTION;
+}
+
+status_t EffectHalHidl::command(uint32_t cmdCode, uint32_t cmdSize, void *pCmdData,
+ uint32_t *replySize, void *pReplyData) {
+ if (mEffect == 0) return NO_INIT;
+
+ // Special cases.
+ if (cmdCode == EFFECT_CMD_SET_CONFIG || cmdCode == EFFECT_CMD_SET_CONFIG_REVERSE) {
+ return setConfigImpl(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
+ } else if (cmdCode == EFFECT_CMD_GET_CONFIG || cmdCode == EFFECT_CMD_GET_CONFIG_REVERSE) {
+ return getConfigImpl(cmdCode, replySize, pReplyData);
+ }
+
+ // Common case.
+ hidl_vec<uint8_t> hidlData;
+ if (pCmdData != nullptr && cmdSize > 0) {
+ hidlData.setToExternal(reinterpret_cast<uint8_t*>(pCmdData), cmdSize);
+ }
+ status_t status;
+ uint32_t replySizeStub = 0;
+ if (replySize == nullptr || pReplyData == nullptr) replySize = &replySizeStub;
+ Return<void> ret = mEffect->command(cmdCode, hidlData, *replySize,
+ [&](int32_t s, const hidl_vec<uint8_t>& result) {
+ status = s;
+ if (status == 0) {
+ if (*replySize > result.size()) *replySize = result.size();
+ if (pReplyData != nullptr && *replySize > 0) {
+ memcpy(pReplyData, &result[0], *replySize);
+ }
+ }
+ });
+ return ret.isOk() ? status : FAILED_TRANSACTION;
+}
+
+status_t EffectHalHidl::getDescriptor(effect_descriptor_t *pDescriptor) {
+ if (mEffect == 0) return NO_INIT;
+ Result retval = Result::NOT_INITIALIZED;
+ Return<void> ret = mEffect->getDescriptor(
+ [&](Result r, const EffectDescriptor& result) {
+ retval = r;
+ if (retval == Result::OK) {
+ effectDescriptorToHal(result, pDescriptor);
+ }
+ });
+ return ret.isOk() ? analyzeResult(retval) : FAILED_TRANSACTION;
+}
+
+status_t EffectHalHidl::close() {
+ if (mEffect == 0) return NO_INIT;
+ Return<Result> ret = mEffect->close();
+ return ret.isOk() ? analyzeResult(ret) : FAILED_TRANSACTION;
+}
+
+status_t EffectHalHidl::getConfigImpl(
+ uint32_t cmdCode, uint32_t *replySize, void *pReplyData) {
+ if (replySize == NULL || *replySize != sizeof(effect_config_t) || pReplyData == NULL) {
+ return BAD_VALUE;
+ }
+ status_t result = FAILED_TRANSACTION;
+ Return<void> ret;
+ if (cmdCode == EFFECT_CMD_GET_CONFIG) {
+ ret = mEffect->getConfig([&] (Result r, const EffectConfig &hidlConfig) {
+ result = analyzeResult(r);
+ if (r == Result::OK) {
+ effectConfigToHal(hidlConfig, static_cast<effect_config_t*>(pReplyData));
+ }
+ });
+ } else {
+ ret = mEffect->getConfigReverse([&] (Result r, const EffectConfig &hidlConfig) {
+ result = analyzeResult(r);
+ if (r == Result::OK) {
+ effectConfigToHal(hidlConfig, static_cast<effect_config_t*>(pReplyData));
+ }
+ });
+ }
+ if (!ret.isOk()) {
+ result = FAILED_TRANSACTION;
+ }
+ return result;
+}
+
+status_t EffectHalHidl::setConfigImpl(
+ uint32_t cmdCode, uint32_t cmdSize, void *pCmdData, uint32_t *replySize, void *pReplyData) {
+ if (pCmdData == NULL || cmdSize != sizeof(effect_config_t) ||
+ replySize == NULL || *replySize != sizeof(int32_t) || pReplyData == NULL) {
+ return BAD_VALUE;
+ }
+ const effect_config_t *halConfig = static_cast<effect_config_t*>(pCmdData);
+ if (halConfig->inputCfg.bufferProvider.getBuffer != NULL ||
+ halConfig->inputCfg.bufferProvider.releaseBuffer != NULL ||
+ halConfig->outputCfg.bufferProvider.getBuffer != NULL ||
+ halConfig->outputCfg.bufferProvider.releaseBuffer != NULL) {
+ ALOGE("Buffer provider callbacks are not supported");
+ }
+ EffectConfig hidlConfig;
+ effectConfigFromHal(*halConfig, &hidlConfig);
+ Return<Result> ret = cmdCode == EFFECT_CMD_SET_CONFIG ?
+ mEffect->setConfig(hidlConfig, nullptr, nullptr) :
+ mEffect->setConfigReverse(hidlConfig, nullptr, nullptr);
+ status_t result = FAILED_TRANSACTION;
+ if (ret.isOk()) {
+ result = analyzeResult(ret);
+ *static_cast<int32_t*>(pReplyData) = result;
+ }
+ return result;
+}
+
+} // namespace V4_0
+} // namespace android
diff --git a/media/libaudiohal/4.0/EffectHalHidl.h b/media/libaudiohal/4.0/EffectHalHidl.h
new file mode 100644
index 0000000..5a4dab1
--- /dev/null
+++ b/media/libaudiohal/4.0/EffectHalHidl.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_EFFECT_HAL_HIDL_4_0_H
+#define ANDROID_HARDWARE_EFFECT_HAL_HIDL_4_0_H
+
+#include <android/hardware/audio/effect/4.0/IEffect.h>
+#include <media/audiohal/EffectHalInterface.h>
+#include <fmq/EventFlag.h>
+#include <fmq/MessageQueue.h>
+#include <system/audio_effect.h>
+
+using ::android::hardware::audio::effect::V4_0::EffectBufferConfig;
+using ::android::hardware::audio::effect::V4_0::EffectConfig;
+using ::android::hardware::audio::effect::V4_0::EffectDescriptor;
+using ::android::hardware::audio::effect::V4_0::IEffect;
+using ::android::hardware::EventFlag;
+using ::android::hardware::MessageQueue;
+
+namespace android {
+namespace V4_0 {
+
+class EffectHalHidl : public EffectHalInterface
+{
+ public:
+ // Set the input buffer.
+ virtual status_t setInBuffer(const sp<EffectBufferHalInterface>& buffer);
+
+ // Set the output buffer.
+ virtual status_t setOutBuffer(const sp<EffectBufferHalInterface>& buffer);
+
+ // Effect process function.
+ virtual status_t process();
+
+ // Process reverse stream function. This function is used to pass
+ // a reference stream to the effect engine.
+ virtual status_t processReverse();
+
+ // Send a command and receive a response to/from effect engine.
+ virtual status_t command(uint32_t cmdCode, uint32_t cmdSize, void *pCmdData,
+ uint32_t *replySize, void *pReplyData);
+
+ // Returns the effect descriptor.
+ virtual status_t getDescriptor(effect_descriptor_t *pDescriptor);
+
+ // Free resources on the remote side.
+ virtual status_t close();
+
+ // Whether it's a local implementation.
+ virtual bool isLocal() const { return false; }
+
+ uint64_t effectId() const { return mEffectId; }
+
+ static void effectDescriptorToHal(
+ const EffectDescriptor& descriptor, effect_descriptor_t* halDescriptor);
+
+ private:
+ friend class EffectsFactoryHalHidl;
+ typedef MessageQueue<
+ hardware::audio::effect::V4_0::Result, hardware::kSynchronizedReadWrite> StatusMQ;
+
+ sp<IEffect> mEffect;
+ const uint64_t mEffectId;
+ sp<EffectBufferHalInterface> mInBuffer;
+ sp<EffectBufferHalInterface> mOutBuffer;
+ bool mBuffersChanged;
+ std::unique_ptr<StatusMQ> mStatusMQ;
+ EventFlag* mEfGroup;
+
+ static status_t analyzeResult(const hardware::audio::effect::V4_0::Result& result);
+ static void effectBufferConfigFromHal(
+ const buffer_config_t& halConfig, EffectBufferConfig* config);
+ static void effectBufferConfigToHal(
+ const EffectBufferConfig& config, buffer_config_t* halConfig);
+ static void effectConfigFromHal(const effect_config_t& halConfig, EffectConfig* config);
+ static void effectConfigToHal(const EffectConfig& config, effect_config_t* halConfig);
+
+ // Can not be constructed directly by clients.
+ EffectHalHidl(const sp<IEffect>& effect, uint64_t effectId);
+
+ // The destructor automatically releases the effect.
+ virtual ~EffectHalHidl();
+
+ status_t getConfigImpl(uint32_t cmdCode, uint32_t *replySize, void *pReplyData);
+ status_t prepareForProcessing();
+ bool needToResetBuffers();
+ status_t processImpl(uint32_t mqFlag);
+ status_t setConfigImpl(
+ uint32_t cmdCode, uint32_t cmdSize, void *pCmdData,
+ uint32_t *replySize, void *pReplyData);
+ status_t setProcessBuffers();
+};
+
+} // namespace V4_0
+} // namespace android
+
+#endif // ANDROID_HARDWARE_EFFECT_HAL_HIDL_4_0_H
diff --git a/media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp b/media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp
new file mode 100644
index 0000000..dfed784
--- /dev/null
+++ b/media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp
@@ -0,0 +1,152 @@
+/*
+ * 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 "EffectsFactoryHalHidl"
+//#define LOG_NDEBUG 0
+
+#include <cutils/native_handle.h>
+#include <libaudiohal/4.0/EffectsFactoryHalHidl.h>
+
+#include "ConversionHelperHidl.h"
+#include "EffectBufferHalHidl.h"
+#include "EffectHalHidl.h"
+#include "HidlUtils.h"
+
+using ::android::hardware::audio::common::V4_0::HidlUtils;
+using ::android::hardware::audio::common::V4_0::Uuid;
+using ::android::hardware::audio::effect::V4_0::IEffect;
+using ::android::hardware::audio::effect::V4_0::Result;
+using ::android::hardware::Return;
+
+namespace android {
+namespace V4_0 {
+
+EffectsFactoryHalHidl::EffectsFactoryHalHidl() : ConversionHelperHidl("EffectsFactory") {
+ mEffectsFactory = IEffectsFactory::getService();
+ if (mEffectsFactory == 0) {
+ ALOGE("Failed to obtain IEffectsFactory service, terminating process.");
+ exit(1);
+ }
+}
+
+EffectsFactoryHalHidl::~EffectsFactoryHalHidl() {
+}
+
+status_t EffectsFactoryHalHidl::queryAllDescriptors() {
+ if (mEffectsFactory == 0) return NO_INIT;
+ Result retval = Result::NOT_INITIALIZED;
+ Return<void> ret = mEffectsFactory->getAllDescriptors(
+ [&](Result r, const hidl_vec<EffectDescriptor>& result) {
+ retval = r;
+ if (retval == Result::OK) {
+ mLastDescriptors = result;
+ }
+ });
+ if (ret.isOk()) {
+ return retval == Result::OK ? OK : NO_INIT;
+ }
+ mLastDescriptors.resize(0);
+ return processReturn(__FUNCTION__, ret);
+}
+
+status_t EffectsFactoryHalHidl::queryNumberEffects(uint32_t *pNumEffects) {
+ status_t queryResult = queryAllDescriptors();
+ if (queryResult == OK) {
+ *pNumEffects = mLastDescriptors.size();
+ }
+ return queryResult;
+}
+
+status_t EffectsFactoryHalHidl::getDescriptor(
+ uint32_t index, effect_descriptor_t *pDescriptor) {
+ // TODO: We need somehow to track the changes on the server side
+ // or figure out how to convert everybody to query all the descriptors at once.
+ // TODO: check for nullptr
+ if (mLastDescriptors.size() == 0) {
+ status_t queryResult = queryAllDescriptors();
+ if (queryResult != OK) return queryResult;
+ }
+ if (index >= mLastDescriptors.size()) return NAME_NOT_FOUND;
+ EffectHalHidl::effectDescriptorToHal(mLastDescriptors[index], pDescriptor);
+ return OK;
+}
+
+status_t EffectsFactoryHalHidl::getDescriptor(
+ const effect_uuid_t *pEffectUuid, effect_descriptor_t *pDescriptor) {
+ // TODO: check for nullptr
+ if (mEffectsFactory == 0) return NO_INIT;
+ Uuid hidlUuid;
+ HidlUtils::uuidFromHal(*pEffectUuid, &hidlUuid);
+ Result retval = Result::NOT_INITIALIZED;
+ Return<void> ret = mEffectsFactory->getDescriptor(hidlUuid,
+ [&](Result r, const EffectDescriptor& result) {
+ retval = r;
+ if (retval == Result::OK) {
+ EffectHalHidl::effectDescriptorToHal(result, pDescriptor);
+ }
+ });
+ if (ret.isOk()) {
+ if (retval == Result::OK) return OK;
+ else if (retval == Result::INVALID_ARGUMENTS) return NAME_NOT_FOUND;
+ else return NO_INIT;
+ }
+ return processReturn(__FUNCTION__, ret);
+}
+
+status_t EffectsFactoryHalHidl::createEffect(
+ const effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t ioId,
+ sp<EffectHalInterface> *effect) {
+ if (mEffectsFactory == 0) return NO_INIT;
+ Uuid hidlUuid;
+ HidlUtils::uuidFromHal(*pEffectUuid, &hidlUuid);
+ Result retval = Result::NOT_INITIALIZED;
+ Return<void> ret = mEffectsFactory->createEffect(
+ hidlUuid, sessionId, ioId,
+ [&](Result r, const sp<IEffect>& result, uint64_t effectId) {
+ retval = r;
+ if (retval == Result::OK) {
+ *effect = new EffectHalHidl(result, effectId);
+ }
+ });
+ if (ret.isOk()) {
+ if (retval == Result::OK) return OK;
+ else if (retval == Result::INVALID_ARGUMENTS) return NAME_NOT_FOUND;
+ else return NO_INIT;
+ }
+ return processReturn(__FUNCTION__, ret);
+}
+
+status_t EffectsFactoryHalHidl::dumpEffects(int fd) {
+ if (mEffectsFactory == 0) return NO_INIT;
+ native_handle_t* hidlHandle = native_handle_create(1, 0);
+ hidlHandle->data[0] = fd;
+ Return<void> ret = mEffectsFactory->debug(hidlHandle, {} /* options */);
+ native_handle_delete(hidlHandle);
+ return processReturn(__FUNCTION__, ret);
+}
+
+status_t EffectsFactoryHalHidl::allocateBuffer(size_t size, sp<EffectBufferHalInterface>* buffer) {
+ return EffectBufferHalHidl::allocate(size, buffer);
+}
+
+status_t EffectsFactoryHalHidl::mirrorBuffer(void* external, size_t size,
+ sp<EffectBufferHalInterface>* buffer) {
+ return EffectBufferHalHidl::mirror(external, size, buffer);
+}
+
+
+} // namespace V4_0
+} // namespace android
diff --git a/media/libaudiohal/4.0/StreamHalHidl.cpp b/media/libaudiohal/4.0/StreamHalHidl.cpp
new file mode 100644
index 0000000..de16e98
--- /dev/null
+++ b/media/libaudiohal/4.0/StreamHalHidl.cpp
@@ -0,0 +1,758 @@
+/*
+ * 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 "StreamHalHidl"
+//#define LOG_NDEBUG 0
+
+#include <android/hardware/audio/4.0/IStreamOutCallback.h>
+#include <hwbinder/IPCThreadState.h>
+#include <mediautils/SchedulingPolicyService.h>
+#include <utils/Log.h>
+
+#include "DeviceHalHidl.h"
+#include "EffectHalHidl.h"
+#include "StreamHalHidl.h"
+#include "VersionUtils.h"
+
+using ::android::hardware::audio::common::V4_0::AudioChannelMask;
+using ::android::hardware::audio::common::V4_0::AudioFormat;
+using ::android::hardware::audio::common::V4_0::ThreadInfo;
+using ::android::hardware::audio::V4_0::AudioDrain;
+using ::android::hardware::audio::V4_0::IStreamOutCallback;
+using ::android::hardware::audio::V4_0::MessageQueueFlagBits;
+using ::android::hardware::audio::V4_0::MmapBufferInfo;
+using ::android::hardware::audio::V4_0::MmapPosition;
+using ::android::hardware::audio::V4_0::ParameterValue;
+using ::android::hardware::audio::V4_0::Result;
+using ::android::hardware::audio::V4_0::TimeSpec;
+using ::android::hardware::MQDescriptorSync;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ReadCommand = ::android::hardware::audio::V4_0::IStreamIn::ReadCommand;
+
+namespace android {
+namespace V4_0 {
+
+StreamHalHidl::StreamHalHidl(IStream *stream)
+ : ConversionHelperHidl("Stream"),
+ mStream(stream),
+ mHalThreadPriority(HAL_THREAD_PRIORITY_DEFAULT),
+ mCachedBufferSize(0){
+
+ // Instrument audio signal power logging.
+ // Note: This assumes channel mask, format, and sample rate do not change after creation.
+ if (mStream != nullptr && mStreamPowerLog.isUserDebugOrEngBuild()) {
+ // Obtain audio properties (see StreamHalHidl::getAudioProperties() below).
+ Return<void> ret = mStream->getAudioProperties(
+ [&](auto sr, auto m, auto f) {
+ mStreamPowerLog.init(sr,
+ static_cast<audio_channel_mask_t>(m),
+ static_cast<audio_format_t>(f));
+ });
+ }
+}
+
+StreamHalHidl::~StreamHalHidl() {
+ mStream = nullptr;
+}
+
+status_t StreamHalHidl::getSampleRate(uint32_t *rate) {
+ if (!mStream) return NO_INIT;
+ return processReturn("getSampleRate", mStream->getSampleRate(), rate);
+}
+
+status_t StreamHalHidl::getBufferSize(size_t *size) {
+ if (!mStream) return NO_INIT;
+ status_t status = processReturn("getBufferSize", mStream->getBufferSize(), size);
+ if (status == OK) {
+ mCachedBufferSize = *size;
+ }
+ return status;
+}
+
+status_t StreamHalHidl::getChannelMask(audio_channel_mask_t *mask) {
+ if (!mStream) return NO_INIT;
+ return processReturn("getChannelMask", mStream->getChannelMask(), mask);
+}
+
+status_t StreamHalHidl::getFormat(audio_format_t *format) {
+ if (!mStream) return NO_INIT;
+ return processReturn("getFormat", mStream->getFormat(), format);
+}
+
+status_t StreamHalHidl::getAudioProperties(
+ uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format) {
+ if (!mStream) return NO_INIT;
+ Return<void> ret = mStream->getAudioProperties(
+ [&](uint32_t sr, auto m, auto f) {
+ *sampleRate = sr;
+ *mask = static_cast<audio_channel_mask_t>(m);
+ *format = static_cast<audio_format_t>(f);
+ });
+ return processReturn("getAudioProperties", ret);
+}
+
+status_t StreamHalHidl::setParameters(const String8& kvPairs) {
+ if (!mStream) return NO_INIT;
+ hidl_vec<ParameterValue> hidlParams;
+ status_t status = parametersFromHal(kvPairs, &hidlParams);
+ if (status != OK) return status;
+ return processReturn("setParameters",
+ utils::setParameters(mStream, hidlParams, {} /* options */));
+}
+
+status_t StreamHalHidl::getParameters(const String8& keys, String8 *values) {
+ values->clear();
+ if (!mStream) return NO_INIT;
+ hidl_vec<hidl_string> hidlKeys;
+ status_t status = keysFromHal(keys, &hidlKeys);
+ if (status != OK) return status;
+ Result retval;
+ Return<void> ret = utils::getParameters(
+ mStream,
+ {} /* context */,
+ hidlKeys,
+ [&](Result r, const hidl_vec<ParameterValue>& parameters) {
+ retval = r;
+ if (retval == Result::OK) {
+ parametersToHal(parameters, values);
+ }
+ });
+ return processReturn("getParameters", ret, retval);
+}
+
+status_t StreamHalHidl::addEffect(sp<EffectHalInterface> effect) {
+ if (!mStream) return NO_INIT;
+ return processReturn("addEffect", mStream->addEffect(
+ static_cast<EffectHalHidl*>(effect.get())->effectId()));
+}
+
+status_t StreamHalHidl::removeEffect(sp<EffectHalInterface> effect) {
+ if (!mStream) return NO_INIT;
+ return processReturn("removeEffect", mStream->removeEffect(
+ static_cast<EffectHalHidl*>(effect.get())->effectId()));
+}
+
+status_t StreamHalHidl::standby() {
+ if (!mStream) return NO_INIT;
+ return processReturn("standby", mStream->standby());
+}
+
+status_t StreamHalHidl::dump(int fd) {
+ if (!mStream) return NO_INIT;
+ native_handle_t* hidlHandle = native_handle_create(1, 0);
+ hidlHandle->data[0] = fd;
+ Return<void> ret = mStream->debug(hidlHandle, {} /* options */);
+ native_handle_delete(hidlHandle);
+ mStreamPowerLog.dump(fd);
+ return processReturn("dump", ret);
+}
+
+status_t StreamHalHidl::start() {
+ if (!mStream) return NO_INIT;
+ return processReturn("start", mStream->start());
+}
+
+status_t StreamHalHidl::stop() {
+ if (!mStream) return NO_INIT;
+ return processReturn("stop", mStream->stop());
+}
+
+status_t StreamHalHidl::createMmapBuffer(int32_t minSizeFrames,
+ struct audio_mmap_buffer_info *info) {
+ Result retval;
+ Return<void> ret = mStream->createMmapBuffer(
+ minSizeFrames,
+ [&](Result r, const MmapBufferInfo& hidlInfo) {
+ retval = r;
+ if (retval == Result::OK) {
+ const native_handle *handle = hidlInfo.sharedMemory.handle();
+ if (handle->numFds > 0) {
+ info->shared_memory_fd = handle->data[0];
+ info->buffer_size_frames = hidlInfo.bufferSizeFrames;
+ info->burst_size_frames = hidlInfo.burstSizeFrames;
+ // info->shared_memory_address is not needed in HIDL context
+ info->shared_memory_address = NULL;
+ } else {
+ retval = Result::NOT_INITIALIZED;
+ }
+ }
+ });
+ return processReturn("createMmapBuffer", ret, retval);
+}
+
+status_t StreamHalHidl::getMmapPosition(struct audio_mmap_position *position) {
+ Result retval;
+ Return<void> ret = mStream->getMmapPosition(
+ [&](Result r, const MmapPosition& hidlPosition) {
+ retval = r;
+ if (retval == Result::OK) {
+ position->time_nanoseconds = hidlPosition.timeNanoseconds;
+ position->position_frames = hidlPosition.positionFrames;
+ }
+ });
+ return processReturn("getMmapPosition", ret, retval);
+}
+
+status_t StreamHalHidl::setHalThreadPriority(int priority) {
+ mHalThreadPriority = priority;
+ return OK;
+}
+
+status_t StreamHalHidl::getCachedBufferSize(size_t *size) {
+ if (mCachedBufferSize != 0) {
+ *size = mCachedBufferSize;
+ return OK;
+ }
+ return getBufferSize(size);
+}
+
+bool StreamHalHidl::requestHalThreadPriority(pid_t threadPid, pid_t threadId) {
+ if (mHalThreadPriority == HAL_THREAD_PRIORITY_DEFAULT) {
+ return true;
+ }
+ int err = requestPriority(
+ threadPid, threadId,
+ mHalThreadPriority, false /*isForApp*/, true /*asynchronous*/);
+ ALOGE_IF(err, "failed to set priority %d for pid %d tid %d; error %d",
+ mHalThreadPriority, threadPid, threadId, err);
+ // Audio will still work, but latency will be higher and sometimes unacceptable.
+ return err == 0;
+}
+
+namespace {
+
+/* Notes on callback ownership.
+
+This is how (Hw)Binder ownership model looks like. The server implementation
+is owned by Binder framework (via sp<>). Proxies are owned by clients.
+When the last proxy disappears, Binder framework releases the server impl.
+
+Thus, it is not needed to keep any references to StreamOutCallback (this is
+the server impl) -- it will live as long as HAL server holds a strong ref to
+IStreamOutCallback proxy. We clear that reference by calling 'clearCallback'
+from the destructor of StreamOutHalHidl.
+
+The callback only keeps a weak reference to the stream. The stream is owned
+by AudioFlinger.
+
+*/
+
+struct StreamOutCallback : public IStreamOutCallback {
+ StreamOutCallback(const wp<StreamOutHalHidl>& stream) : mStream(stream) {}
+
+ // IStreamOutCallback implementation
+ Return<void> onWriteReady() override {
+ sp<StreamOutHalHidl> stream = mStream.promote();
+ if (stream != 0) {
+ stream->onWriteReady();
+ }
+ return Void();
+ }
+
+ Return<void> onDrainReady() override {
+ sp<StreamOutHalHidl> stream = mStream.promote();
+ if (stream != 0) {
+ stream->onDrainReady();
+ }
+ return Void();
+ }
+
+ Return<void> onError() override {
+ sp<StreamOutHalHidl> stream = mStream.promote();
+ if (stream != 0) {
+ stream->onError();
+ }
+ return Void();
+ }
+
+ private:
+ wp<StreamOutHalHidl> mStream;
+};
+
+} // namespace
+
+StreamOutHalHidl::StreamOutHalHidl(const sp<IStreamOut>& stream)
+ : StreamHalHidl(stream.get()), mStream(stream), mWriterClient(0), mEfGroup(nullptr) {
+}
+
+StreamOutHalHidl::~StreamOutHalHidl() {
+ if (mStream != 0) {
+ if (mCallback.unsafe_get()) {
+ processReturn("clearCallback", mStream->clearCallback());
+ }
+ processReturn("close", mStream->close());
+ mStream.clear();
+ }
+ mCallback.clear();
+ hardware::IPCThreadState::self()->flushCommands();
+ if (mEfGroup) {
+ EventFlag::deleteEventFlag(&mEfGroup);
+ }
+}
+
+status_t StreamOutHalHidl::getFrameSize(size_t *size) {
+ if (mStream == 0) return NO_INIT;
+ return processReturn("getFrameSize", mStream->getFrameSize(), size);
+}
+
+status_t StreamOutHalHidl::getLatency(uint32_t *latency) {
+ if (mStream == 0) return NO_INIT;
+ if (mWriterClient == gettid() && mCommandMQ) {
+ return callWriterThread(
+ WriteCommand::GET_LATENCY, "getLatency", nullptr, 0,
+ [&](const WriteStatus& writeStatus) {
+ *latency = writeStatus.reply.latencyMs;
+ });
+ } else {
+ return processReturn("getLatency", mStream->getLatency(), latency);
+ }
+}
+
+status_t StreamOutHalHidl::setVolume(float left, float right) {
+ if (mStream == 0) return NO_INIT;
+ return processReturn("setVolume", mStream->setVolume(left, right));
+}
+
+status_t StreamOutHalHidl::write(const void *buffer, size_t bytes, size_t *written) {
+ if (mStream == 0) return NO_INIT;
+ *written = 0;
+
+ 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");
+ return OK;
+ }
+
+ status_t status;
+ if (!mDataMQ) {
+ // In case if playback starts close to the end of a compressed track, the bytes
+ // that need to be written is less than the actual buffer size. Need to use
+ // full buffer size for the MQ since otherwise after seeking back to the middle
+ // data will be truncated.
+ size_t bufferSize;
+ if ((status = getCachedBufferSize(&bufferSize)) != OK) {
+ return status;
+ }
+ if (bytes > bufferSize) bufferSize = bytes;
+ if ((status = prepareForWriting(bufferSize)) != OK) {
+ return status;
+ }
+ }
+
+ status = callWriterThread(
+ WriteCommand::WRITE, "write", static_cast<const uint8_t*>(buffer), bytes,
+ [&] (const WriteStatus& writeStatus) {
+ *written = writeStatus.reply.written;
+ // Diagnostics of the cause of b/35813113.
+ ALOGE_IF(*written > bytes,
+ "hal reports more bytes written than asked for: %lld > %lld",
+ (long long)*written, (long long)bytes);
+ });
+ mStreamPowerLog.log(buffer, *written);
+ return status;
+}
+
+status_t StreamOutHalHidl::callWriterThread(
+ WriteCommand cmd, const char* cmdName,
+ const uint8_t* data, size_t dataSize, StreamOutHalHidl::WriterCallback callback) {
+ if (!mCommandMQ->write(&cmd)) {
+ ALOGE("command message queue write failed for \"%s\"", cmdName);
+ return -EAGAIN;
+ }
+ if (data != nullptr) {
+ size_t availableToWrite = mDataMQ->availableToWrite();
+ if (dataSize > availableToWrite) {
+ ALOGW("truncating write data from %lld to %lld due to insufficient data queue space",
+ (long long)dataSize, (long long)availableToWrite);
+ dataSize = availableToWrite;
+ }
+ if (!mDataMQ->write(data, dataSize)) {
+ ALOGE("data message queue write failed for \"%s\"", cmdName);
+ }
+ }
+ mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY));
+
+ // TODO: Remove manual event flag handling once blocking MQ is implemented. b/33815422
+ uint32_t efState = 0;
+retry:
+ status_t ret = mEfGroup->wait(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL), &efState);
+ if (efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL)) {
+ WriteStatus writeStatus;
+ writeStatus.retval = Result::NOT_INITIALIZED;
+ if (!mStatusMQ->read(&writeStatus)) {
+ ALOGE("status message read failed for \"%s\"", cmdName);
+ }
+ if (writeStatus.retval == Result::OK) {
+ ret = OK;
+ callback(writeStatus);
+ } else {
+ ret = processReturn(cmdName, writeStatus.retval);
+ }
+ return ret;
+ }
+ if (ret == -EAGAIN || ret == -EINTR) {
+ // Spurious wakeup. This normally retries no more than once.
+ goto retry;
+ }
+ return ret;
+}
+
+status_t StreamOutHalHidl::prepareForWriting(size_t bufferSize) {
+ std::unique_ptr<CommandMQ> tempCommandMQ;
+ std::unique_ptr<DataMQ> tempDataMQ;
+ std::unique_ptr<StatusMQ> tempStatusMQ;
+ Result retval;
+ pid_t halThreadPid, halThreadTid;
+ Return<void> ret = mStream->prepareForWriting(
+ 1, bufferSize,
+ [&](Result r,
+ const CommandMQ::Descriptor& commandMQ,
+ const DataMQ::Descriptor& dataMQ,
+ const StatusMQ::Descriptor& statusMQ,
+ const ThreadInfo& halThreadInfo) {
+ retval = r;
+ if (retval == Result::OK) {
+ tempCommandMQ.reset(new CommandMQ(commandMQ));
+ tempDataMQ.reset(new DataMQ(dataMQ));
+ tempStatusMQ.reset(new StatusMQ(statusMQ));
+ if (tempDataMQ->isValid() && tempDataMQ->getEventFlagWord()) {
+ EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &mEfGroup);
+ }
+ halThreadPid = halThreadInfo.pid;
+ halThreadTid = halThreadInfo.tid;
+ }
+ });
+ if (!ret.isOk() || retval != Result::OK) {
+ return processReturn("prepareForWriting", ret, retval);
+ }
+ if (!tempCommandMQ || !tempCommandMQ->isValid() ||
+ !tempDataMQ || !tempDataMQ->isValid() ||
+ !tempStatusMQ || !tempStatusMQ->isValid() ||
+ !mEfGroup) {
+ ALOGE_IF(!tempCommandMQ, "Failed to obtain command message queue for writing");
+ ALOGE_IF(tempCommandMQ && !tempCommandMQ->isValid(),
+ "Command message queue for writing is invalid");
+ ALOGE_IF(!tempDataMQ, "Failed to obtain data message queue for writing");
+ ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "Data message queue for writing is invalid");
+ ALOGE_IF(!tempStatusMQ, "Failed to obtain status message queue for writing");
+ ALOGE_IF(tempStatusMQ && !tempStatusMQ->isValid(),
+ "Status message queue for writing is invalid");
+ ALOGE_IF(!mEfGroup, "Event flag creation for writing failed");
+ return NO_INIT;
+ }
+ requestHalThreadPriority(halThreadPid, halThreadTid);
+
+ mCommandMQ = std::move(tempCommandMQ);
+ mDataMQ = std::move(tempDataMQ);
+ mStatusMQ = std::move(tempStatusMQ);
+ mWriterClient = gettid();
+ return OK;
+}
+
+status_t StreamOutHalHidl::getRenderPosition(uint32_t *dspFrames) {
+ if (mStream == 0) return NO_INIT;
+ Result retval;
+ Return<void> ret = mStream->getRenderPosition(
+ [&](Result r, uint32_t d) {
+ retval = r;
+ if (retval == Result::OK) {
+ *dspFrames = d;
+ }
+ });
+ return processReturn("getRenderPosition", ret, retval);
+}
+
+status_t StreamOutHalHidl::getNextWriteTimestamp(int64_t *timestamp) {
+ if (mStream == 0) return NO_INIT;
+ Result retval;
+ Return<void> ret = mStream->getNextWriteTimestamp(
+ [&](Result r, int64_t t) {
+ retval = r;
+ if (retval == Result::OK) {
+ *timestamp = t;
+ }
+ });
+ return processReturn("getRenderPosition", ret, retval);
+}
+
+status_t StreamOutHalHidl::setCallback(wp<StreamOutHalInterfaceCallback> callback) {
+ if (mStream == 0) return NO_INIT;
+ status_t status = processReturn(
+ "setCallback", mStream->setCallback(new StreamOutCallback(this)));
+ if (status == OK) {
+ mCallback = callback;
+ }
+ return status;
+}
+
+status_t StreamOutHalHidl::supportsPauseAndResume(bool *supportsPause, bool *supportsResume) {
+ if (mStream == 0) return NO_INIT;
+ Return<void> ret = mStream->supportsPauseAndResume(
+ [&](bool p, bool r) {
+ *supportsPause = p;
+ *supportsResume = r;
+ });
+ return processReturn("supportsPauseAndResume", ret);
+}
+
+status_t StreamOutHalHidl::pause() {
+ if (mStream == 0) return NO_INIT;
+ return processReturn("pause", mStream->pause());
+}
+
+status_t StreamOutHalHidl::resume() {
+ if (mStream == 0) return NO_INIT;
+ return processReturn("pause", mStream->resume());
+}
+
+status_t StreamOutHalHidl::supportsDrain(bool *supportsDrain) {
+ if (mStream == 0) return NO_INIT;
+ return processReturn("supportsDrain", mStream->supportsDrain(), supportsDrain);
+}
+
+status_t StreamOutHalHidl::drain(bool earlyNotify) {
+ if (mStream == 0) return NO_INIT;
+ return processReturn(
+ "drain", mStream->drain(earlyNotify ? AudioDrain::EARLY_NOTIFY : AudioDrain::ALL));
+}
+
+status_t StreamOutHalHidl::flush() {
+ if (mStream == 0) return NO_INIT;
+ return processReturn("pause", mStream->flush());
+}
+
+status_t StreamOutHalHidl::getPresentationPosition(uint64_t *frames, struct timespec *timestamp) {
+ if (mStream == 0) return NO_INIT;
+ if (mWriterClient == gettid() && mCommandMQ) {
+ return callWriterThread(
+ WriteCommand::GET_PRESENTATION_POSITION, "getPresentationPosition", nullptr, 0,
+ [&](const WriteStatus& writeStatus) {
+ *frames = writeStatus.reply.presentationPosition.frames;
+ timestamp->tv_sec = writeStatus.reply.presentationPosition.timeStamp.tvSec;
+ timestamp->tv_nsec = writeStatus.reply.presentationPosition.timeStamp.tvNSec;
+ });
+ } else {
+ Result retval;
+ Return<void> ret = mStream->getPresentationPosition(
+ [&](Result r, uint64_t hidlFrames, const TimeSpec& hidlTimeStamp) {
+ retval = r;
+ if (retval == Result::OK) {
+ *frames = hidlFrames;
+ timestamp->tv_sec = hidlTimeStamp.tvSec;
+ timestamp->tv_nsec = hidlTimeStamp.tvNSec;
+ }
+ });
+ return processReturn("getPresentationPosition", ret, retval);
+ }
+}
+
+void StreamOutHalHidl::onWriteReady() {
+ sp<StreamOutHalInterfaceCallback> callback = mCallback.promote();
+ if (callback == 0) return;
+ ALOGV("asyncCallback onWriteReady");
+ callback->onWriteReady();
+}
+
+void StreamOutHalHidl::onDrainReady() {
+ sp<StreamOutHalInterfaceCallback> callback = mCallback.promote();
+ if (callback == 0) return;
+ ALOGV("asyncCallback onDrainReady");
+ callback->onDrainReady();
+}
+
+void StreamOutHalHidl::onError() {
+ sp<StreamOutHalInterfaceCallback> callback = mCallback.promote();
+ if (callback == 0) return;
+ ALOGV("asyncCallback onError");
+ callback->onError();
+}
+
+
+StreamInHalHidl::StreamInHalHidl(const sp<IStreamIn>& stream)
+ : StreamHalHidl(stream.get()), mStream(stream), mReaderClient(0), mEfGroup(nullptr) {
+}
+
+StreamInHalHidl::~StreamInHalHidl() {
+ if (mStream != 0) {
+ processReturn("close", mStream->close());
+ mStream.clear();
+ hardware::IPCThreadState::self()->flushCommands();
+ }
+ if (mEfGroup) {
+ EventFlag::deleteEventFlag(&mEfGroup);
+ }
+}
+
+status_t StreamInHalHidl::getFrameSize(size_t *size) {
+ if (mStream == 0) return NO_INIT;
+ return processReturn("getFrameSize", mStream->getFrameSize(), size);
+}
+
+status_t StreamInHalHidl::setGain(float gain) {
+ if (mStream == 0) return NO_INIT;
+ return processReturn("setGain", mStream->setGain(gain));
+}
+
+status_t StreamInHalHidl::read(void *buffer, size_t bytes, size_t *read) {
+ if (mStream == 0) return NO_INIT;
+ *read = 0;
+
+ if (bytes == 0 && !mDataMQ) {
+ // Can't determine the size for the MQ buffer. Wait for a non-empty read request.
+ return OK;
+ }
+
+ status_t status;
+ if (!mDataMQ && (status = prepareForReading(bytes)) != OK) {
+ return status;
+ }
+
+ ReadParameters params;
+ params.command = ReadCommand::READ;
+ params.params.read = bytes;
+ status = callReaderThread(params, "read",
+ [&](const ReadStatus& readStatus) {
+ const size_t availToRead = mDataMQ->availableToRead();
+ if (!mDataMQ->read(static_cast<uint8_t*>(buffer), std::min(bytes, availToRead))) {
+ ALOGE("data message queue read failed for \"read\"");
+ }
+ ALOGW_IF(availToRead != readStatus.reply.read,
+ "HAL read report inconsistent: mq = %d, status = %d",
+ (int32_t)availToRead, (int32_t)readStatus.reply.read);
+ *read = readStatus.reply.read;
+ });
+ mStreamPowerLog.log(buffer, *read);
+ return status;
+}
+
+status_t StreamInHalHidl::callReaderThread(
+ const ReadParameters& params, const char* cmdName,
+ StreamInHalHidl::ReaderCallback callback) {
+ if (!mCommandMQ->write(¶ms)) {
+ ALOGW("command message queue write failed");
+ return -EAGAIN;
+ }
+ mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL));
+
+ // TODO: Remove manual event flag handling once blocking MQ is implemented. b/33815422
+ uint32_t efState = 0;
+retry:
+ status_t ret = mEfGroup->wait(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY), &efState);
+ if (efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY)) {
+ ReadStatus readStatus;
+ readStatus.retval = Result::NOT_INITIALIZED;
+ if (!mStatusMQ->read(&readStatus)) {
+ ALOGE("status message read failed for \"%s\"", cmdName);
+ }
+ if (readStatus.retval == Result::OK) {
+ ret = OK;
+ callback(readStatus);
+ } else {
+ ret = processReturn(cmdName, readStatus.retval);
+ }
+ return ret;
+ }
+ if (ret == -EAGAIN || ret == -EINTR) {
+ // Spurious wakeup. This normally retries no more than once.
+ goto retry;
+ }
+ return ret;
+}
+
+status_t StreamInHalHidl::prepareForReading(size_t bufferSize) {
+ std::unique_ptr<CommandMQ> tempCommandMQ;
+ std::unique_ptr<DataMQ> tempDataMQ;
+ std::unique_ptr<StatusMQ> tempStatusMQ;
+ Result retval;
+ pid_t halThreadPid, halThreadTid;
+ Return<void> ret = mStream->prepareForReading(
+ 1, bufferSize,
+ [&](Result r,
+ const CommandMQ::Descriptor& commandMQ,
+ const DataMQ::Descriptor& dataMQ,
+ const StatusMQ::Descriptor& statusMQ,
+ const ThreadInfo& halThreadInfo) {
+ retval = r;
+ if (retval == Result::OK) {
+ tempCommandMQ.reset(new CommandMQ(commandMQ));
+ tempDataMQ.reset(new DataMQ(dataMQ));
+ tempStatusMQ.reset(new StatusMQ(statusMQ));
+ if (tempDataMQ->isValid() && tempDataMQ->getEventFlagWord()) {
+ EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &mEfGroup);
+ }
+ halThreadPid = halThreadInfo.pid;
+ halThreadTid = halThreadInfo.tid;
+ }
+ });
+ if (!ret.isOk() || retval != Result::OK) {
+ return processReturn("prepareForReading", ret, retval);
+ }
+ if (!tempCommandMQ || !tempCommandMQ->isValid() ||
+ !tempDataMQ || !tempDataMQ->isValid() ||
+ !tempStatusMQ || !tempStatusMQ->isValid() ||
+ !mEfGroup) {
+ ALOGE_IF(!tempCommandMQ, "Failed to obtain command message queue for writing");
+ ALOGE_IF(tempCommandMQ && !tempCommandMQ->isValid(),
+ "Command message queue for writing is invalid");
+ ALOGE_IF(!tempDataMQ, "Failed to obtain data message queue for reading");
+ ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "Data message queue for reading is invalid");
+ ALOGE_IF(!tempStatusMQ, "Failed to obtain status message queue for reading");
+ ALOGE_IF(tempStatusMQ && !tempStatusMQ->isValid(),
+ "Status message queue for reading is invalid");
+ ALOGE_IF(!mEfGroup, "Event flag creation for reading failed");
+ return NO_INIT;
+ }
+ requestHalThreadPriority(halThreadPid, halThreadTid);
+
+ mCommandMQ = std::move(tempCommandMQ);
+ mDataMQ = std::move(tempDataMQ);
+ mStatusMQ = std::move(tempStatusMQ);
+ mReaderClient = gettid();
+ return OK;
+}
+
+status_t StreamInHalHidl::getInputFramesLost(uint32_t *framesLost) {
+ if (mStream == 0) return NO_INIT;
+ return processReturn("getInputFramesLost", mStream->getInputFramesLost(), framesLost);
+}
+
+status_t StreamInHalHidl::getCapturePosition(int64_t *frames, int64_t *time) {
+ if (mStream == 0) return NO_INIT;
+ if (mReaderClient == gettid() && mCommandMQ) {
+ ReadParameters params;
+ params.command = ReadCommand::GET_CAPTURE_POSITION;
+ return callReaderThread(params, "getCapturePosition",
+ [&](const ReadStatus& readStatus) {
+ *frames = readStatus.reply.capturePosition.frames;
+ *time = readStatus.reply.capturePosition.time;
+ });
+ } else {
+ Result retval;
+ Return<void> ret = mStream->getCapturePosition(
+ [&](Result r, uint64_t hidlFrames, uint64_t hidlTime) {
+ retval = r;
+ if (retval == Result::OK) {
+ *frames = hidlFrames;
+ *time = hidlTime;
+ }
+ });
+ return processReturn("getCapturePosition", ret, retval);
+ }
+}
+
+} // namespace V4_0
+} // namespace android
diff --git a/media/libaudiohal/4.0/StreamHalHidl.h b/media/libaudiohal/4.0/StreamHalHidl.h
new file mode 100644
index 0000000..8d4dc8c
--- /dev/null
+++ b/media/libaudiohal/4.0/StreamHalHidl.h
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_STREAM_HAL_HIDL_4_0_H
+#define ANDROID_HARDWARE_STREAM_HAL_HIDL_4_0_H
+
+#include <atomic>
+
+#include <android/hardware/audio/4.0/IStream.h>
+#include <android/hardware/audio/4.0/IStreamIn.h>
+#include <android/hardware/audio/4.0/IStreamOut.h>
+#include <fmq/EventFlag.h>
+#include <fmq/MessageQueue.h>
+#include <media/audiohal/StreamHalInterface.h>
+
+#include "ConversionHelperHidl.h"
+#include "StreamPowerLog.h"
+
+using ::android::hardware::audio::V4_0::IStream;
+using ::android::hardware::audio::V4_0::IStreamIn;
+using ::android::hardware::audio::V4_0::IStreamOut;
+using ::android::hardware::EventFlag;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::Return;
+using ReadParameters = ::android::hardware::audio::V4_0::IStreamIn::ReadParameters;
+using ReadStatus = ::android::hardware::audio::V4_0::IStreamIn::ReadStatus;
+using WriteCommand = ::android::hardware::audio::V4_0::IStreamOut::WriteCommand;
+using WriteStatus = ::android::hardware::audio::V4_0::IStreamOut::WriteStatus;
+
+namespace android {
+namespace V4_0 {
+
+class DeviceHalHidl;
+
+class StreamHalHidl : public virtual StreamHalInterface, public ConversionHelperHidl
+{
+ public:
+ // Return the sampling rate in Hz - eg. 44100.
+ virtual status_t getSampleRate(uint32_t *rate);
+
+ // Return size of input/output buffer in bytes for this stream - eg. 4800.
+ virtual status_t getBufferSize(size_t *size);
+
+ // Return the channel mask.
+ virtual status_t getChannelMask(audio_channel_mask_t *mask);
+
+ // Return the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT.
+ virtual status_t getFormat(audio_format_t *format);
+
+ // Convenience method.
+ virtual status_t getAudioProperties(
+ uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format);
+
+ // Set audio stream parameters.
+ virtual status_t setParameters(const String8& kvPairs);
+
+ // Get audio stream parameters.
+ virtual status_t getParameters(const String8& keys, String8 *values);
+
+ // Add or remove the effect on the stream.
+ virtual status_t addEffect(sp<EffectHalInterface> effect);
+ virtual status_t removeEffect(sp<EffectHalInterface> effect);
+
+ // Put the audio hardware input/output into standby mode.
+ virtual status_t standby();
+
+ virtual status_t dump(int fd);
+
+ // Start a stream operating in mmap mode.
+ virtual status_t start();
+
+ // Stop a stream operating in mmap mode.
+ virtual status_t stop();
+
+ // Retrieve information on the data buffer in mmap mode.
+ virtual status_t createMmapBuffer(int32_t minSizeFrames,
+ struct audio_mmap_buffer_info *info);
+
+ // Get current read/write position in the mmap buffer
+ virtual status_t getMmapPosition(struct audio_mmap_position *position);
+
+ // Set the priority of the thread that interacts with the HAL
+ // (must match the priority of the audioflinger's thread that calls 'read' / 'write')
+ virtual status_t setHalThreadPriority(int priority);
+
+ protected:
+ // Subclasses can not be constructed directly by clients.
+ explicit StreamHalHidl(IStream *stream);
+
+ // The destructor automatically closes the stream.
+ virtual ~StreamHalHidl();
+
+ status_t getCachedBufferSize(size_t *size);
+
+ bool requestHalThreadPriority(pid_t threadPid, pid_t threadId);
+
+ // mStreamPowerLog is used for audio signal power logging.
+ StreamPowerLog mStreamPowerLog;
+
+ private:
+ const int HAL_THREAD_PRIORITY_DEFAULT = -1;
+ IStream *mStream;
+ int mHalThreadPriority;
+ size_t mCachedBufferSize;
+};
+
+class StreamOutHalHidl : public StreamOutHalInterface, public StreamHalHidl {
+ public:
+ // Return the frame size (number of bytes per sample) of a stream.
+ virtual status_t getFrameSize(size_t *size);
+
+ // Return the audio hardware driver estimated latency in milliseconds.
+ virtual status_t getLatency(uint32_t *latency);
+
+ // Use this method in situations where audio mixing is done in the hardware.
+ virtual status_t setVolume(float left, float right);
+
+ // Write audio buffer to driver.
+ virtual status_t write(const void *buffer, size_t bytes, size_t *written);
+
+ // Return the number of audio frames written by the audio dsp to DAC since
+ // the output has exited standby.
+ virtual status_t getRenderPosition(uint32_t *dspFrames);
+
+ // Get the local time at which the next write to the audio driver will be presented.
+ virtual status_t getNextWriteTimestamp(int64_t *timestamp);
+
+ // Set the callback for notifying completion of non-blocking write and drain.
+ virtual status_t setCallback(wp<StreamOutHalInterfaceCallback> callback);
+
+ // Returns whether pause and resume operations are supported.
+ virtual status_t supportsPauseAndResume(bool *supportsPause, bool *supportsResume);
+
+ // Notifies to the audio driver to resume playback following a pause.
+ virtual status_t pause();
+
+ // Notifies to the audio driver to resume playback following a pause.
+ virtual status_t resume();
+
+ // Returns whether drain operation is supported.
+ virtual status_t supportsDrain(bool *supportsDrain);
+
+ // Requests notification when data buffered by the driver/hardware has been played.
+ virtual status_t drain(bool earlyNotify);
+
+ // Notifies to the audio driver to flush the queued data.
+ virtual status_t flush();
+
+ // Return a recent count of the number of audio frames presented to an external observer.
+ virtual status_t getPresentationPosition(uint64_t *frames, struct timespec *timestamp);
+
+ // Methods used by StreamOutCallback (HIDL).
+ void onWriteReady();
+ void onDrainReady();
+ void onError();
+
+ private:
+ friend class DeviceHalHidl;
+ typedef MessageQueue<WriteCommand, hardware::kSynchronizedReadWrite> CommandMQ;
+ typedef MessageQueue<uint8_t, hardware::kSynchronizedReadWrite> DataMQ;
+ typedef MessageQueue<WriteStatus, hardware::kSynchronizedReadWrite> StatusMQ;
+
+ wp<StreamOutHalInterfaceCallback> mCallback;
+ sp<IStreamOut> mStream;
+ std::unique_ptr<CommandMQ> mCommandMQ;
+ std::unique_ptr<DataMQ> mDataMQ;
+ std::unique_ptr<StatusMQ> mStatusMQ;
+ std::atomic<pid_t> mWriterClient;
+ EventFlag* mEfGroup;
+
+ // Can not be constructed directly by clients.
+ StreamOutHalHidl(const sp<IStreamOut>& stream);
+
+ virtual ~StreamOutHalHidl();
+
+ using WriterCallback = std::function<void(const WriteStatus& writeStatus)>;
+ status_t callWriterThread(
+ WriteCommand cmd, const char* cmdName,
+ const uint8_t* data, size_t dataSize, WriterCallback callback);
+ status_t prepareForWriting(size_t bufferSize);
+};
+
+class StreamInHalHidl : public StreamInHalInterface, public StreamHalHidl {
+ public:
+ // Return the frame size (number of bytes per sample) of a stream.
+ virtual status_t getFrameSize(size_t *size);
+
+ // Set the input gain for the audio driver.
+ virtual status_t setGain(float gain);
+
+ // Read audio buffer in from driver.
+ virtual status_t read(void *buffer, size_t bytes, size_t *read);
+
+ // Return the amount of input frames lost in the audio driver.
+ virtual status_t getInputFramesLost(uint32_t *framesLost);
+
+ // Return a recent count of the number of audio frames received and
+ // the clock time associated with that frame count.
+ virtual status_t getCapturePosition(int64_t *frames, int64_t *time);
+
+ private:
+ friend class DeviceHalHidl;
+ typedef MessageQueue<ReadParameters, hardware::kSynchronizedReadWrite> CommandMQ;
+ typedef MessageQueue<uint8_t, hardware::kSynchronizedReadWrite> DataMQ;
+ typedef MessageQueue<ReadStatus, hardware::kSynchronizedReadWrite> StatusMQ;
+
+ sp<IStreamIn> mStream;
+ std::unique_ptr<CommandMQ> mCommandMQ;
+ std::unique_ptr<DataMQ> mDataMQ;
+ std::unique_ptr<StatusMQ> mStatusMQ;
+ std::atomic<pid_t> mReaderClient;
+ EventFlag* mEfGroup;
+
+ // Can not be constructed directly by clients.
+ StreamInHalHidl(const sp<IStreamIn>& stream);
+
+ virtual ~StreamInHalHidl();
+
+ using ReaderCallback = std::function<void(const ReadStatus& readStatus)>;
+ status_t callReaderThread(
+ const ReadParameters& params, const char* cmdName, ReaderCallback callback);
+ status_t prepareForReading(size_t bufferSize);
+};
+
+} // namespace V4_0
+} // namespace android
+
+#endif // ANDROID_HARDWARE_STREAM_HAL_HIDL_4_0_H
diff --git a/media/libaudiohal/4.0/StreamHalLocal.cpp b/media/libaudiohal/4.0/StreamHalLocal.cpp
new file mode 100644
index 0000000..592a931
--- /dev/null
+++ b/media/libaudiohal/4.0/StreamHalLocal.cpp
@@ -0,0 +1,319 @@
+/*
+ * 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 "StreamHalLocal"
+//#define LOG_NDEBUG 0
+
+#include <hardware/audio.h>
+#include <utils/Log.h>
+
+#include "DeviceHalLocal.h"
+#include "StreamHalLocal.h"
+#include "VersionUtils.h"
+
+namespace android {
+namespace V4_0 {
+
+StreamHalLocal::StreamHalLocal(audio_stream_t *stream, sp<DeviceHalLocal> device)
+ : mDevice(device),
+ mStream(stream) {
+ // Instrument audio signal power logging.
+ // Note: This assumes channel mask, format, and sample rate do not change after creation.
+ if (mStream != nullptr && mStreamPowerLog.isUserDebugOrEngBuild()) {
+ mStreamPowerLog.init(mStream->get_sample_rate(mStream),
+ mStream->get_channels(mStream),
+ mStream->get_format(mStream));
+ }
+}
+
+StreamHalLocal::~StreamHalLocal() {
+ mStream = 0;
+ mDevice.clear();
+}
+
+status_t StreamHalLocal::getSampleRate(uint32_t *rate) {
+ *rate = mStream->get_sample_rate(mStream);
+ return OK;
+}
+
+status_t StreamHalLocal::getBufferSize(size_t *size) {
+ *size = mStream->get_buffer_size(mStream);
+ return OK;
+}
+
+status_t StreamHalLocal::getChannelMask(audio_channel_mask_t *mask) {
+ *mask = mStream->get_channels(mStream);
+ return OK;
+}
+
+status_t StreamHalLocal::getFormat(audio_format_t *format) {
+ *format = mStream->get_format(mStream);
+ return OK;
+}
+
+status_t StreamHalLocal::getAudioProperties(
+ uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format) {
+ *sampleRate = mStream->get_sample_rate(mStream);
+ *mask = mStream->get_channels(mStream);
+ *format = mStream->get_format(mStream);
+ return OK;
+}
+
+status_t StreamHalLocal::setParameters(const String8& kvPairs) {
+ return mStream->set_parameters(mStream, kvPairs.string());
+}
+
+status_t StreamHalLocal::getParameters(const String8& keys, String8 *values) {
+ char *halValues = mStream->get_parameters(mStream, keys.string());
+ if (halValues != NULL) {
+ values->setTo(halValues);
+ free(halValues);
+ } else {
+ values->clear();
+ }
+ return OK;
+}
+
+status_t StreamHalLocal::addEffect(sp<EffectHalInterface>) {
+ LOG_ALWAYS_FATAL("Local streams can not have effects");
+ return INVALID_OPERATION;
+}
+
+status_t StreamHalLocal::removeEffect(sp<EffectHalInterface>) {
+ LOG_ALWAYS_FATAL("Local streams can not have effects");
+ return INVALID_OPERATION;
+}
+
+status_t StreamHalLocal::standby() {
+ return mStream->standby(mStream);
+}
+
+status_t StreamHalLocal::dump(int fd) {
+ status_t status = mStream->dump(mStream, fd);
+ mStreamPowerLog.dump(fd);
+ return status;
+}
+
+status_t StreamHalLocal::setHalThreadPriority(int) {
+ // Don't need to do anything as local hal is executed by audioflinger directly
+ // on the same thread.
+ return OK;
+}
+
+StreamOutHalLocal::StreamOutHalLocal(audio_stream_out_t *stream, sp<DeviceHalLocal> device)
+ : StreamHalLocal(&stream->common, device), mStream(stream) {
+}
+
+StreamOutHalLocal::~StreamOutHalLocal() {
+ mCallback.clear();
+ mDevice->closeOutputStream(mStream);
+ mStream = 0;
+}
+
+status_t StreamOutHalLocal::getFrameSize(size_t *size) {
+ *size = audio_stream_out_frame_size(mStream);
+ return OK;
+}
+
+status_t StreamOutHalLocal::getLatency(uint32_t *latency) {
+ *latency = mStream->get_latency(mStream);
+ return OK;
+}
+
+status_t StreamOutHalLocal::setVolume(float left, float right) {
+ if (mStream->set_volume == NULL) return INVALID_OPERATION;
+ return mStream->set_volume(mStream, left, right);
+}
+
+status_t StreamOutHalLocal::write(const void *buffer, size_t bytes, size_t *written) {
+ ssize_t writeResult = mStream->write(mStream, buffer, bytes);
+ if (writeResult > 0) {
+ *written = writeResult;
+ mStreamPowerLog.log(buffer, *written);
+ return OK;
+ } else {
+ *written = 0;
+ return writeResult;
+ }
+}
+
+status_t StreamOutHalLocal::getRenderPosition(uint32_t *dspFrames) {
+ return mStream->get_render_position(mStream, dspFrames);
+}
+
+status_t StreamOutHalLocal::getNextWriteTimestamp(int64_t *timestamp) {
+ if (mStream->get_next_write_timestamp == NULL) return INVALID_OPERATION;
+ return mStream->get_next_write_timestamp(mStream, timestamp);
+}
+
+status_t StreamOutHalLocal::setCallback(wp<StreamOutHalInterfaceCallback> callback) {
+ if (mStream->set_callback == NULL) return INVALID_OPERATION;
+ status_t result = mStream->set_callback(mStream, StreamOutHalLocal::asyncCallback, this);
+ if (result == OK) {
+ mCallback = callback;
+ }
+ return result;
+}
+
+// static
+int StreamOutHalLocal::asyncCallback(stream_callback_event_t event, void*, void *cookie) {
+ // We act as if we gave a wp<StreamOutHalLocal> to HAL. This way we should handle
+ // correctly the case when the callback is invoked while StreamOutHalLocal's destructor is
+ // already running, because the destructor is invoked after the refcount has been atomically
+ // decremented.
+ wp<StreamOutHalLocal> weakSelf(static_cast<StreamOutHalLocal*>(cookie));
+ sp<StreamOutHalLocal> self = weakSelf.promote();
+ if (self == 0) return 0;
+ sp<StreamOutHalInterfaceCallback> callback = self->mCallback.promote();
+ if (callback == 0) return 0;
+ ALOGV("asyncCallback() event %d", event);
+ switch (event) {
+ case STREAM_CBK_EVENT_WRITE_READY:
+ callback->onWriteReady();
+ break;
+ case STREAM_CBK_EVENT_DRAIN_READY:
+ callback->onDrainReady();
+ break;
+ case STREAM_CBK_EVENT_ERROR:
+ callback->onError();
+ break;
+ default:
+ ALOGW("asyncCallback() unknown event %d", event);
+ break;
+ }
+ return 0;
+}
+
+status_t StreamOutHalLocal::supportsPauseAndResume(bool *supportsPause, bool *supportsResume) {
+ *supportsPause = mStream->pause != NULL;
+ *supportsResume = mStream->resume != NULL;
+ return OK;
+}
+
+status_t StreamOutHalLocal::pause() {
+ if (mStream->pause == NULL) return INVALID_OPERATION;
+ return mStream->pause(mStream);
+}
+
+status_t StreamOutHalLocal::resume() {
+ if (mStream->resume == NULL) return INVALID_OPERATION;
+ return mStream->resume(mStream);
+}
+
+status_t StreamOutHalLocal::supportsDrain(bool *supportsDrain) {
+ *supportsDrain = mStream->drain != NULL;
+ return OK;
+}
+
+status_t StreamOutHalLocal::drain(bool earlyNotify) {
+ if (mStream->drain == NULL) return INVALID_OPERATION;
+ return mStream->drain(mStream, earlyNotify ? AUDIO_DRAIN_EARLY_NOTIFY : AUDIO_DRAIN_ALL);
+}
+
+status_t StreamOutHalLocal::flush() {
+ if (mStream->flush == NULL) return INVALID_OPERATION;
+ return mStream->flush(mStream);
+}
+
+status_t StreamOutHalLocal::getPresentationPosition(uint64_t *frames, struct timespec *timestamp) {
+ if (mStream->get_presentation_position == NULL) return INVALID_OPERATION;
+ return mStream->get_presentation_position(mStream, frames, timestamp);
+}
+
+status_t StreamOutHalLocal::start() {
+ if (mStream->start == NULL) return INVALID_OPERATION;
+ return mStream->start(mStream);
+}
+
+status_t StreamOutHalLocal::stop() {
+ if (mStream->stop == NULL) return INVALID_OPERATION;
+ return mStream->stop(mStream);
+}
+
+status_t StreamOutHalLocal::createMmapBuffer(int32_t minSizeFrames,
+ struct audio_mmap_buffer_info *info) {
+ if (mStream->create_mmap_buffer == NULL) return INVALID_OPERATION;
+ return mStream->create_mmap_buffer(mStream, minSizeFrames, info);
+}
+
+status_t StreamOutHalLocal::getMmapPosition(struct audio_mmap_position *position) {
+ if (mStream->get_mmap_position == NULL) return INVALID_OPERATION;
+ return mStream->get_mmap_position(mStream, position);
+}
+
+StreamInHalLocal::StreamInHalLocal(audio_stream_in_t *stream, sp<DeviceHalLocal> device)
+ : StreamHalLocal(&stream->common, device), mStream(stream) {
+}
+
+StreamInHalLocal::~StreamInHalLocal() {
+ mDevice->closeInputStream(mStream);
+ mStream = 0;
+}
+
+status_t StreamInHalLocal::getFrameSize(size_t *size) {
+ *size = audio_stream_in_frame_size(mStream);
+ return OK;
+}
+
+status_t StreamInHalLocal::setGain(float gain) {
+ return mStream->set_gain(mStream, gain);
+}
+
+status_t StreamInHalLocal::read(void *buffer, size_t bytes, size_t *read) {
+ ssize_t readResult = mStream->read(mStream, buffer, bytes);
+ if (readResult > 0) {
+ *read = readResult;
+ mStreamPowerLog.log( buffer, *read);
+ return OK;
+ } else {
+ *read = 0;
+ return readResult;
+ }
+}
+
+status_t StreamInHalLocal::getInputFramesLost(uint32_t *framesLost) {
+ *framesLost = mStream->get_input_frames_lost(mStream);
+ return OK;
+}
+
+status_t StreamInHalLocal::getCapturePosition(int64_t *frames, int64_t *time) {
+ if (mStream->get_capture_position == NULL) return INVALID_OPERATION;
+ return mStream->get_capture_position(mStream, frames, time);
+}
+
+status_t StreamInHalLocal::start() {
+ if (mStream->start == NULL) return INVALID_OPERATION;
+ return mStream->start(mStream);
+}
+
+status_t StreamInHalLocal::stop() {
+ if (mStream->stop == NULL) return INVALID_OPERATION;
+ return mStream->stop(mStream);
+}
+
+status_t StreamInHalLocal::createMmapBuffer(int32_t minSizeFrames,
+ struct audio_mmap_buffer_info *info) {
+ if (mStream->create_mmap_buffer == NULL) return INVALID_OPERATION;
+ return mStream->create_mmap_buffer(mStream, minSizeFrames, info);
+}
+
+status_t StreamInHalLocal::getMmapPosition(struct audio_mmap_position *position) {
+ if (mStream->get_mmap_position == NULL) return INVALID_OPERATION;
+ return mStream->get_mmap_position(mStream, position);
+}
+
+} // namespace V4_0
+} // namespace android
diff --git a/media/libaudiohal/4.0/StreamHalLocal.h b/media/libaudiohal/4.0/StreamHalLocal.h
new file mode 100644
index 0000000..076bc4c
--- /dev/null
+++ b/media/libaudiohal/4.0/StreamHalLocal.h
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_STREAM_HAL_LOCAL_4_0_H
+#define ANDROID_HARDWARE_STREAM_HAL_LOCAL_4_0_H
+
+#include <media/audiohal/StreamHalInterface.h>
+#include "StreamPowerLog.h"
+
+namespace android {
+namespace V4_0 {
+
+class DeviceHalLocal;
+
+class StreamHalLocal : public virtual StreamHalInterface
+{
+ public:
+ // Return the sampling rate in Hz - eg. 44100.
+ virtual status_t getSampleRate(uint32_t *rate);
+
+ // Return size of input/output buffer in bytes for this stream - eg. 4800.
+ virtual status_t getBufferSize(size_t *size);
+
+ // Return the channel mask.
+ virtual status_t getChannelMask(audio_channel_mask_t *mask);
+
+ // Return the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT.
+ virtual status_t getFormat(audio_format_t *format);
+
+ // Convenience method.
+ virtual status_t getAudioProperties(
+ uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format);
+
+ // Set audio stream parameters.
+ virtual status_t setParameters(const String8& kvPairs);
+
+ // Get audio stream parameters.
+ virtual status_t getParameters(const String8& keys, String8 *values);
+
+ // Add or remove the effect on the stream.
+ virtual status_t addEffect(sp<EffectHalInterface> effect);
+ virtual status_t removeEffect(sp<EffectHalInterface> effect);
+
+ // Put the audio hardware input/output into standby mode.
+ virtual status_t standby();
+
+ virtual status_t dump(int fd);
+
+ // Start a stream operating in mmap mode.
+ virtual status_t start() = 0;
+
+ // Stop a stream operating in mmap mode.
+ virtual status_t stop() = 0;
+
+ // Retrieve information on the data buffer in mmap mode.
+ virtual status_t createMmapBuffer(int32_t minSizeFrames,
+ struct audio_mmap_buffer_info *info) = 0;
+
+ // Get current read/write position in the mmap buffer
+ virtual status_t getMmapPosition(struct audio_mmap_position *position) = 0;
+
+ // Set the priority of the thread that interacts with the HAL
+ // (must match the priority of the audioflinger's thread that calls 'read' / 'write')
+ virtual status_t setHalThreadPriority(int priority);
+
+ protected:
+ // Subclasses can not be constructed directly by clients.
+ StreamHalLocal(audio_stream_t *stream, sp<DeviceHalLocal> device);
+
+ // The destructor automatically closes the stream.
+ virtual ~StreamHalLocal();
+
+ sp<DeviceHalLocal> mDevice;
+
+ // mStreamPowerLog is used for audio signal power logging.
+ StreamPowerLog mStreamPowerLog;
+
+ private:
+ audio_stream_t *mStream;
+};
+
+class StreamOutHalLocal : public StreamOutHalInterface, public StreamHalLocal {
+ public:
+ // Return the frame size (number of bytes per sample) of a stream.
+ virtual status_t getFrameSize(size_t *size);
+
+ // Return the audio hardware driver estimated latency in milliseconds.
+ virtual status_t getLatency(uint32_t *latency);
+
+ // Use this method in situations where audio mixing is done in the hardware.
+ virtual status_t setVolume(float left, float right);
+
+ // Write audio buffer to driver.
+ virtual status_t write(const void *buffer, size_t bytes, size_t *written);
+
+ // Return the number of audio frames written by the audio dsp to DAC since
+ // the output has exited standby.
+ virtual status_t getRenderPosition(uint32_t *dspFrames);
+
+ // Get the local time at which the next write to the audio driver will be presented.
+ virtual status_t getNextWriteTimestamp(int64_t *timestamp);
+
+ // Set the callback for notifying completion of non-blocking write and drain.
+ virtual status_t setCallback(wp<StreamOutHalInterfaceCallback> callback);
+
+ // Returns whether pause and resume operations are supported.
+ virtual status_t supportsPauseAndResume(bool *supportsPause, bool *supportsResume);
+
+ // Notifies to the audio driver to resume playback following a pause.
+ virtual status_t pause();
+
+ // Notifies to the audio driver to resume playback following a pause.
+ virtual status_t resume();
+
+ // Returns whether drain operation is supported.
+ virtual status_t supportsDrain(bool *supportsDrain);
+
+ // Requests notification when data buffered by the driver/hardware has been played.
+ virtual status_t drain(bool earlyNotify);
+
+ // Notifies to the audio driver to flush the queued data.
+ virtual status_t flush();
+
+ // Return a recent count of the number of audio frames presented to an external observer.
+ virtual status_t getPresentationPosition(uint64_t *frames, struct timespec *timestamp);
+
+ // Start a stream operating in mmap mode.
+ virtual status_t start();
+
+ // Stop a stream operating in mmap mode.
+ virtual status_t stop();
+
+ // Retrieve information on the data buffer in mmap mode.
+ virtual status_t createMmapBuffer(int32_t minSizeFrames,
+ struct audio_mmap_buffer_info *info);
+
+ // Get current read/write position in the mmap buffer
+ virtual status_t getMmapPosition(struct audio_mmap_position *position);
+
+ private:
+ audio_stream_out_t *mStream;
+ wp<StreamOutHalInterfaceCallback> mCallback;
+
+ friend class DeviceHalLocal;
+
+ // Can not be constructed directly by clients.
+ StreamOutHalLocal(audio_stream_out_t *stream, sp<DeviceHalLocal> device);
+
+ virtual ~StreamOutHalLocal();
+
+ static int asyncCallback(stream_callback_event_t event, void *param, void *cookie);
+};
+
+class StreamInHalLocal : public StreamInHalInterface, public StreamHalLocal {
+ public:
+ // Return the frame size (number of bytes per sample) of a stream.
+ virtual status_t getFrameSize(size_t *size);
+
+ // Set the input gain for the audio driver.
+ virtual status_t setGain(float gain);
+
+ // Read audio buffer in from driver.
+ virtual status_t read(void *buffer, size_t bytes, size_t *read);
+
+ // Return the amount of input frames lost in the audio driver.
+ virtual status_t getInputFramesLost(uint32_t *framesLost);
+
+ // Return a recent count of the number of audio frames received and
+ // the clock time associated with that frame count.
+ virtual status_t getCapturePosition(int64_t *frames, int64_t *time);
+
+ // Start a stream operating in mmap mode.
+ virtual status_t start();
+
+ // Stop a stream operating in mmap mode.
+ virtual status_t stop();
+
+ // Retrieve information on the data buffer in mmap mode.
+ virtual status_t createMmapBuffer(int32_t minSizeFrames,
+ struct audio_mmap_buffer_info *info);
+
+ // Get current read/write position in the mmap buffer
+ virtual status_t getMmapPosition(struct audio_mmap_position *position);
+
+ private:
+ audio_stream_in_t *mStream;
+
+ friend class DeviceHalLocal;
+
+ // Can not be constructed directly by clients.
+ StreamInHalLocal(audio_stream_in_t *stream, sp<DeviceHalLocal> device);
+
+ virtual ~StreamInHalLocal();
+};
+
+} // namespace V4_0
+} // namespace android
+
+#endif // ANDROID_HARDWARE_STREAM_HAL_LOCAL_4_0_H
diff --git a/media/libaudiohal/4.0/StreamPowerLog.h b/media/libaudiohal/4.0/StreamPowerLog.h
new file mode 100644
index 0000000..57b7201
--- /dev/null
+++ b/media/libaudiohal/4.0/StreamPowerLog.h
@@ -0,0 +1,104 @@
+/*
+ * 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_HARDWARE_STREAM_POWER_LOG_4_0_H
+#define ANDROID_HARDWARE_STREAM_POWER_LOG_4_0_H
+
+#include <audio_utils/clock.h>
+#include <audio_utils/PowerLog.h>
+#include <cutils/properties.h>
+#include <system/audio.h>
+
+namespace android {
+namespace V4_0 {
+
+class StreamPowerLog {
+public:
+ StreamPowerLog() :
+ mIsUserDebugOrEngBuild(is_userdebug_or_eng_build()),
+ mPowerLog(nullptr),
+ mFrameSize(0) {
+ // use init() to set up the power log.
+ }
+
+ ~StreamPowerLog() {
+ power_log_destroy(mPowerLog); // OK for null mPowerLog
+ mPowerLog = nullptr;
+ }
+
+ // A one-time initialization (do not call twice) before using StreamPowerLog.
+ void init(uint32_t sampleRate, audio_channel_mask_t channelMask, audio_format_t format) {
+ if (mPowerLog == nullptr) {
+ // Note: A way to get channel count for both input and output channel masks
+ // but does not check validity of the channel mask.
+ const uint32_t channelCount = popcount(audio_channel_mask_get_bits(channelMask));
+ mFrameSize = channelCount * audio_bytes_per_sample(format);
+ if (mFrameSize > 0) {
+ const size_t kPowerLogFramesPerEntry =
+ (long long)sampleRate * kPowerLogSamplingIntervalMs / 1000;
+ mPowerLog = power_log_create(
+ sampleRate,
+ channelCount,
+ format,
+ kPowerLogEntries,
+ kPowerLogFramesPerEntry);
+ }
+ }
+ // mPowerLog may be NULL (not the right build, format not accepted, etc.).
+ }
+
+ // Dump the power log to fd.
+ void dump(int fd) const {
+ // OK for null mPowerLog
+ (void)power_log_dump(
+ mPowerLog, fd, " " /* prefix */, kPowerLogLines, 0 /* limit_ns */);
+ }
+
+ // Log the audio data contained in buffer.
+ void log(const void *buffer, size_t sizeInBytes) const {
+ if (mPowerLog != nullptr) { // mFrameSize is always nonzero if mPowerLog exists.
+ power_log_log(
+ mPowerLog, buffer, sizeInBytes / mFrameSize, audio_utils_get_real_time_ns());
+ }
+ }
+
+ bool isUserDebugOrEngBuild() const {
+ return mIsUserDebugOrEngBuild;
+ }
+
+private:
+
+ static inline bool is_userdebug_or_eng_build() {
+ char value[PROPERTY_VALUE_MAX];
+ (void)property_get("ro.build.type", value, "unknown"); // ignore actual length
+ return strcmp(value, "userdebug") == 0 || strcmp(value, "eng") == 0;
+ }
+
+ // Audio signal power log configuration.
+ static const size_t kPowerLogLines = 40;
+ static const size_t kPowerLogSamplingIntervalMs = 50;
+ static const size_t kPowerLogEntries = (1 /* minutes */ * 60 /* seconds */ * 1000 /* msec */
+ / kPowerLogSamplingIntervalMs);
+
+ const bool mIsUserDebugOrEngBuild;
+ power_log_t *mPowerLog;
+ size_t mFrameSize;
+};
+
+} // namespace V4_0
+} // namespace android
+
+#endif // ANDROID_HARDWARE_STREAM_POWER_LOG_4_0_H
diff --git a/media/libaudiohal/4.0/VersionUtils.h b/media/libaudiohal/4.0/VersionUtils.h
new file mode 100644
index 0000000..1246c2e
--- /dev/null
+++ b/media/libaudiohal/4.0/VersionUtils.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_VERSION_UTILS_4_0_H
+#define ANDROID_HARDWARE_VERSION_UTILS_4_0_H
+
+#include <android/hardware/audio/4.0/types.h>
+#include <hidl/HidlSupport.h>
+
+using ::android::hardware::audio::V4_0::ParameterValue;
+using ::android::hardware::audio::V4_0::Result;
+using ::android::hardware::Return;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::hidl_string;
+
+namespace android {
+namespace V4_0 {
+namespace utils {
+
+template <class T, class Callback>
+Return<void> getParameters(T& object, hidl_vec<ParameterValue> context,
+ hidl_vec<hidl_string> keys, Callback callback) {
+ return object->getParameters(context, keys, callback);
+}
+
+template <class T>
+Return<Result> setParameters(T& object, hidl_vec<ParameterValue> context,
+ hidl_vec<ParameterValue> keys) {
+ return object->setParameters(context, keys);
+}
+
+} // namespace utils
+} // namespace V4_0
+} // namespace android
+
+#endif // ANDROID_HARDWARE_VERSION_UTILS_4_0_H
diff --git a/media/libaudiohal/4.0/include/libaudiohal/4.0/DevicesFactoryHalHybrid.h b/media/libaudiohal/4.0/include/libaudiohal/4.0/DevicesFactoryHalHybrid.h
new file mode 100644
index 0000000..abf6de0
--- /dev/null
+++ b/media/libaudiohal/4.0/include/libaudiohal/4.0/DevicesFactoryHalHybrid.h
@@ -0,0 +1,49 @@
+/*
+ * 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_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_4_0_H
+#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_4_0_H
+
+#include <media/audiohal/DevicesFactoryHalInterface.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace V4_0 {
+
+class DevicesFactoryHalHybrid : public DevicesFactoryHalInterface
+{
+ public:
+ // Opens a device with the specified name. To close the device, it is
+ // necessary to release references to the returned object.
+ virtual status_t openDevice(const char *name, sp<DeviceHalInterface> *device);
+
+ private:
+ friend class DevicesFactoryHalInterface;
+
+ // Can not be constructed directly by clients.
+ DevicesFactoryHalHybrid();
+
+ virtual ~DevicesFactoryHalHybrid();
+
+ sp<DevicesFactoryHalInterface> mLocalFactory;
+ sp<DevicesFactoryHalInterface> mHidlFactory;
+};
+
+} // namespace V4_0
+} // namespace android
+
+#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_4_0_H
diff --git a/media/libaudiohal/4.0/include/libaudiohal/4.0/EffectsFactoryHalHidl.h b/media/libaudiohal/4.0/include/libaudiohal/4.0/EffectsFactoryHalHidl.h
new file mode 100644
index 0000000..680b7a1
--- /dev/null
+++ b/media/libaudiohal/4.0/include/libaudiohal/4.0/EffectsFactoryHalHidl.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_4_0_H
+#define ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_4_0_H
+
+#include <android/hardware/audio/effect/4.0/IEffectsFactory.h>
+#include <android/hardware/audio/effect/4.0/types.h>
+#include <media/audiohal/EffectsFactoryHalInterface.h>
+
+#include "ConversionHelperHidl.h"
+
+namespace android {
+namespace V4_0 {
+
+using ::android::hardware::audio::effect::V4_0::EffectDescriptor;
+using ::android::hardware::audio::effect::V4_0::IEffectsFactory;
+using ::android::hardware::hidl_vec;
+
+class EffectsFactoryHalHidl : public EffectsFactoryHalInterface, public ConversionHelperHidl
+{
+ public:
+ // Returns the number of different effects in all loaded libraries.
+ virtual status_t queryNumberEffects(uint32_t *pNumEffects);
+
+ // Returns a descriptor of the next available effect.
+ virtual status_t getDescriptor(uint32_t index,
+ effect_descriptor_t *pDescriptor);
+
+ virtual status_t getDescriptor(const effect_uuid_t *pEffectUuid,
+ effect_descriptor_t *pDescriptor);
+
+ // Creates an effect engine of the specified type.
+ // To release the effect engine, it is necessary to release references
+ // to the returned effect object.
+ virtual status_t createEffect(const effect_uuid_t *pEffectUuid,
+ int32_t sessionId, int32_t ioId,
+ sp<EffectHalInterface> *effect);
+
+ virtual status_t dumpEffects(int fd);
+
+ status_t allocateBuffer(size_t size, sp<EffectBufferHalInterface>* buffer) override;
+ status_t mirrorBuffer(void* external, size_t size,
+ sp<EffectBufferHalInterface>* buffer) override;
+
+ private:
+ friend class EffectsFactoryHalInterface;
+
+ sp<IEffectsFactory> mEffectsFactory;
+ hidl_vec<EffectDescriptor> mLastDescriptors;
+
+ // Can not be constructed directly by clients.
+ EffectsFactoryHalHidl();
+ virtual ~EffectsFactoryHalHidl();
+
+ status_t queryAllDescriptors();
+};
+
+} // namespace V4_0
+} // namespace android
+
+#endif // ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_4_0_H
diff --git a/media/libaudiohal/Android.bp b/media/libaudiohal/Android.bp
index 7ecb9d8..3a5df27 100644
--- a/media/libaudiohal/Android.bp
+++ b/media/libaudiohal/Android.bp
@@ -12,9 +12,12 @@
],
shared_libs: [
- "android.hardware.audio@2.0",
"android.hardware.audio.effect@2.0",
+ "android.hardware.audio.effect@4.0",
+ "android.hardware.audio@2.0",
+ "android.hardware.audio@4.0",
"libaudiohal@2.0",
+ "libaudiohal@4.0",
"libutils",
],
diff --git a/media/libaudiohal/DevicesFactoryHalInterface.cpp b/media/libaudiohal/DevicesFactoryHalInterface.cpp
index cfec3d6..4c8eaf6 100644
--- a/media/libaudiohal/DevicesFactoryHalInterface.cpp
+++ b/media/libaudiohal/DevicesFactoryHalInterface.cpp
@@ -14,16 +14,20 @@
* limitations under the License.
*/
-#include "DevicesFactoryHalHybrid.h"
#include <android/hardware/audio/2.0/IDevicesFactory.h>
+#include <android/hardware/audio/4.0/IDevicesFactory.h>
-using namespace ::android::hardware::audio;
+#include <DevicesFactoryHalHybrid.h>
+#include <libaudiohal/4.0/DevicesFactoryHalHybrid.h>
namespace android {
// static
sp<DevicesFactoryHalInterface> DevicesFactoryHalInterface::create() {
- if (V2_0::IDevicesFactory::getService() != nullptr) {
+ if (hardware::audio::V4_0::IDevicesFactory::getService() != nullptr) {
+ return new V4_0::DevicesFactoryHalHybrid();
+ }
+ if (hardware::audio::V2_0::IDevicesFactory::getService() != nullptr) {
return new DevicesFactoryHalHybrid();
}
return nullptr;
diff --git a/media/libaudiohal/EffectsFactoryHalInterface.cpp b/media/libaudiohal/EffectsFactoryHalInterface.cpp
index 01a171e..ead1fa2 100644
--- a/media/libaudiohal/EffectsFactoryHalInterface.cpp
+++ b/media/libaudiohal/EffectsFactoryHalInterface.cpp
@@ -14,21 +14,21 @@
* limitations under the License.
*/
-#define LOG_TAG "EffectsFactoryHalHidl"
-//#define LOG_NDEBUG 0
+#include <android/hardware/audio/effect/2.0/IEffectsFactory.h>
+#include <android/hardware/audio/effect/4.0/IEffectsFactory.h>
-#include "EffectHalHidl.h"
-#include "EffectsFactoryHalHidl.h"
+#include <EffectsFactoryHalHidl.h>
+#include <libaudiohal/4.0/EffectsFactoryHalHidl.h>
-using ::android::hardware::Return;
-
-using namespace ::android::hardware::audio::effect;
namespace android {
// static
sp<EffectsFactoryHalInterface> EffectsFactoryHalInterface::create() {
- if (V2_0::IEffectsFactory::getService() != nullptr) {
+ if (hardware::audio::effect::V4_0::IEffectsFactory::getService() != nullptr) {
+ return new V4_0::EffectsFactoryHalHidl();
+ }
+ if (hardware::audio::effect::V2_0::IEffectsFactory::getService() != nullptr) {
return new EffectsFactoryHalHidl();
}
return nullptr;
diff --git a/media/libmedia/NdkWrapper.cpp b/media/libmedia/NdkWrapper.cpp
index 5e47b48..5418af9 100644
--- a/media/libmedia/NdkWrapper.cpp
+++ b/media/libmedia/NdkWrapper.cpp
@@ -81,11 +81,16 @@
AMEDIAFORMAT_KEY_STRIDE,
AMEDIAFORMAT_KEY_TRACK_ID,
AMEDIAFORMAT_KEY_WIDTH,
+ AMEDIAFORMAT_KEY_DISPLAY_HEIGHT,
+ AMEDIAFORMAT_KEY_DISPLAY_WIDTH,
+ AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID,
+ AMEDIAFORMAT_KEY_TRACK_INDEX,
};
static const char *AMediaFormatKeyGroupInt64[] = {
AMEDIAFORMAT_KEY_DURATION,
AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER,
+ AMEDIAFORMAT_KEY_TIME_US,
};
static const char *AMediaFormatKeyGroupString[] = {
@@ -96,6 +101,14 @@
static const char *AMediaFormatKeyGroupBuffer[] = {
AMEDIAFORMAT_KEY_HDR_STATIC_INFO,
+ AMEDIAFORMAT_KEY_SEI,
+ AMEDIAFORMAT_KEY_MPEG_USER_DATA,
+};
+
+static const char *AMediaFormatKeyGroupCsd[] = {
+ AMEDIAFORMAT_KEY_CSD_0,
+ AMEDIAFORMAT_KEY_CSD_1,
+ AMEDIAFORMAT_KEY_CSD_2,
};
static const char *AMediaFormatKeyGroupRect[] = {
@@ -111,6 +124,10 @@
static status_t translateErrorCode(media_status_t err) {
if (err == AMEDIA_OK) {
return OK;
+ } else if (err == AMEDIA_ERROR_END_OF_STREAM) {
+ return ERROR_END_OF_STREAM;
+ } else if (err == AMEDIA_ERROR_IO) {
+ return ERROR_IO;
} else if (err == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
return -EAGAIN;
}
@@ -303,11 +320,19 @@
}
sp<AMessage> AMediaFormatWrapper::toAMessage() const {
+ sp<AMessage> msg;
+ writeToAMessage(msg);
+ return msg;
+}
+
+void AMediaFormatWrapper::writeToAMessage(sp<AMessage> &msg) const {
if (mAMediaFormat == NULL) {
- return NULL;
+ msg = NULL;
}
- sp<AMessage> msg = new AMessage;
+ if (msg == NULL) {
+ msg = new AMessage;
+ }
for (auto& key : AMediaFormatKeyGroupInt32) {
int32_t val;
if (getInt32(key, &val)) {
@@ -334,6 +359,16 @@
msg->setBuffer(key, buffer);
}
}
+ for (auto& key : AMediaFormatKeyGroupCsd) {
+ void *data;
+ size_t size;
+ if (getBuffer(key, &data, &size)) {
+ sp<ABuffer> buffer = ABuffer::CreateAsCopy(data, size);
+ buffer->meta()->setInt32(AMEDIAFORMAT_KEY_CSD, 1);
+ buffer->meta()->setInt64(AMEDIAFORMAT_KEY_TIME_US, 0);
+ msg->setBuffer(key, buffer);
+ }
+ }
for (auto& key : AMediaFormatKeyGroupRect) {
int32_t left, top, right, bottom;
if (getRect(key, &left, &top, &right, &bottom)) {
@@ -351,7 +386,6 @@
}
}
}
- return msg;
}
const char* AMediaFormatWrapper::toString() const {
@@ -1150,6 +1184,15 @@
return AMediaExtractor_getSampleTime(mAMediaExtractor);
}
+status_t AMediaExtractorWrapper::getSampleFormat(sp<AMediaFormatWrapper> &formatWrapper) {
+ if (mAMediaExtractor == NULL) {
+ return DEAD_OBJECT;
+ }
+ AMediaFormat *format = AMediaFormat_new();
+ formatWrapper = new AMediaFormatWrapper(format);
+ return translateErrorCode(AMediaExtractor_getSampleFormat(mAMediaExtractor, format));
+}
+
int64_t AMediaExtractorWrapper::getCachedDuration() {
if (mAMediaExtractor == NULL) {
return -1;
diff --git a/media/libmedia/TypeConverter.cpp b/media/libmedia/TypeConverter.cpp
index 4cadeb1..1096df4 100644
--- a/media/libmedia/TypeConverter.cpp
+++ b/media/libmedia/TypeConverter.cpp
@@ -308,8 +308,6 @@
MAKE_STRING_FROM_ENUM(AUDIO_USAGE_ASSISTANCE_SONIFICATION),
MAKE_STRING_FROM_ENUM(AUDIO_USAGE_GAME),
MAKE_STRING_FROM_ENUM(AUDIO_USAGE_VIRTUAL_SOURCE),
- MAKE_STRING_FROM_ENUM(AUDIO_USAGE_CNT),
- MAKE_STRING_FROM_ENUM(AUDIO_USAGE_MAX),
TERMINATOR
};
@@ -325,8 +323,6 @@
MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_VOICE_COMMUNICATION),
MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_REMOTE_SUBMIX),
MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_UNPROCESSED),
- MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_CNT),
- MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_MAX),
MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_FM_TUNER),
MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_HOTWORD),
TERMINATOR
diff --git a/media/libmedia/include/media/NdkWrapper.h b/media/libmedia/include/media/NdkWrapper.h
index b71b758..191665a 100644
--- a/media/libmedia/include/media/NdkWrapper.h
+++ b/media/libmedia/include/media/NdkWrapper.h
@@ -45,6 +45,7 @@
class MetaData;
struct AMediaFormatWrapper : public RefBase {
+
static sp<AMediaFormatWrapper> Create(const sp<AMessage> &message);
AMediaFormatWrapper();
@@ -54,6 +55,7 @@
AMediaFormat *getAMediaFormat() const;
sp<AMessage> toAMessage() const ;
+ void writeToAMessage(sp<AMessage>&) const ;
const char* toString() const ;
status_t release();
@@ -313,6 +315,8 @@
int64_t getSampleTime();
+ status_t getSampleFormat(sp<AMediaFormatWrapper> &formatWrapper);
+
int64_t getCachedDuration();
bool advance();
diff --git a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
index 790581a..196b103 100644
--- a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
+++ b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
@@ -35,13 +35,13 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/DataSourceFactory.h>
-#include <media/stagefright/FileSource.h>
#include <media/stagefright/InterfaceUtils.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaClock.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaExtractorFactory.h>
#include <media/stagefright/MetaData.h>
+#include <media/stagefright/NdkUtils.h>
#include <media/stagefright/Utils.h>
#include "../../libstagefright/include/NuCachedSource2.h"
#include "../../libstagefright/include/HTTPBase.h"
@@ -161,29 +161,40 @@
}
status_t NuPlayer2::GenericSource2::initFromDataSource() {
- sp<IMediaExtractor> extractor;
- CHECK(mDataSource != NULL);
+ mExtractor = new AMediaExtractorWrapper(AMediaExtractor_new());
+ CHECK(mDataSource != NULL || mFd != -1);
sp<DataSource> dataSource = mDataSource;
+ const int fd = mFd;
+ const int64_t offset = mOffset;
+ const int64_t length = mLength;
mLock.unlock();
// This might take long time if data source is not reliable.
- extractor = MediaExtractorFactory::Create(dataSource, NULL);
+ status_t err;
+ if (dataSource != nullptr) {
+ mDataSourceWrapper = new AMediaDataSourceWrapper(dataSource);
+ err = mExtractor->setDataSource(mDataSourceWrapper->getAMediaDataSource());
+ } else {
+ err = mExtractor->setDataSource(fd, offset, length);
+ }
- if (extractor == NULL) {
- ALOGE("initFromDataSource, cannot create extractor!");
+ if (err != OK) {
+ ALOGE("initFromDataSource, failed to create data source!");
+ mLock.lock();
return UNKNOWN_ERROR;
}
- sp<MetaData> fileMeta = extractor->getMetaData();
-
- size_t numtracks = extractor->countTracks();
+ size_t numtracks = mExtractor->getTrackCount();
if (numtracks == 0) {
ALOGE("initFromDataSource, source has no track!");
+ mLock.lock();
return UNKNOWN_ERROR;
}
mLock.lock();
- mFileMeta = fileMeta;
+ mFd = -1;
+ mDataSource = dataSource;
+ mFileMeta = convertMediaFormatWrapperToMetaData(mExtractor->getFormat());
if (mFileMeta != NULL) {
int64_t duration;
if (mFileMeta->findInt64(kKeyDuration, &duration)) {
@@ -196,18 +207,22 @@
mMimes.clear();
for (size_t i = 0; i < numtracks; ++i) {
- sp<IMediaSource> track = extractor->getTrack(i);
- if (track == NULL) {
- continue;
- }
- sp<MetaData> meta = extractor->getTrackMetaData(i);
- if (meta == NULL) {
+ sp<AMediaFormatWrapper> trackFormat = mExtractor->getTrackFormat(i);
+ if (trackFormat == NULL) {
ALOGE("no metadata for track %zu", i);
return UNKNOWN_ERROR;
}
+ sp<AMediaExtractorWrapper> trackExtractor = new AMediaExtractorWrapper(AMediaExtractor_new());
+ if (mDataSourceWrapper != nullptr) {
+ err = trackExtractor->setDataSource(mDataSourceWrapper->getAMediaDataSource());
+ } else {
+ err = trackExtractor->setDataSource(fd, offset, length);
+ }
+
const char *mime;
+ sp<MetaData> meta = convertMediaFormatWrapperToMetaData(trackFormat);
CHECK(meta->findCString(kKeyMIMEType, &mime));
ALOGV("initFromDataSource track[%zu]: %s", i, mime);
@@ -217,11 +232,11 @@
// extractor operation, some extractors might modify meta
// during getTrack() and make it invalid.
if (!strncasecmp(mime, "audio/", 6)) {
- if (mAudioTrack.mSource == NULL) {
+ if (mAudioTrack.mExtractor == NULL) {
mAudioTrack.mIndex = i;
- mAudioTrack.mSource = track;
- mAudioTrack.mPackets =
- new AnotherPacketSource(mAudioTrack.mSource->getFormat());
+ mAudioTrack.mExtractor = trackExtractor;
+ mAudioTrack.mExtractor->selectTrack(i);
+ mAudioTrack.mPackets = new AnotherPacketSource(meta);
if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
mAudioIsVorbis = true;
@@ -232,18 +247,18 @@
mMimes.add(String8(mime));
}
} else if (!strncasecmp(mime, "video/", 6)) {
- if (mVideoTrack.mSource == NULL) {
+ if (mVideoTrack.mExtractor == NULL) {
mVideoTrack.mIndex = i;
- mVideoTrack.mSource = track;
- mVideoTrack.mPackets =
- new AnotherPacketSource(mVideoTrack.mSource->getFormat());
+ mVideoTrack.mExtractor = trackExtractor;
+ mVideoTrack.mExtractor->selectTrack(i);
+ mVideoTrack.mPackets = new AnotherPacketSource(meta);
// video always at the beginning
mMimes.insertAt(String8(mime), 0);
}
}
- mSources.push(track);
+ mExtractors.push(trackExtractor);
int64_t durationUs;
if (meta->findInt64(kKeyDuration, &durationUs)) {
if (durationUs > mDurationUs) {
@@ -259,10 +274,10 @@
}
}
- ALOGV("initFromDataSource mSources.size(): %zu mIsSecure: %d mime[0]: %s", mSources.size(),
+ ALOGV("initFromDataSource mExtractors.size(): %zu mIsSecure: %d mime[0]: %s", mExtractors.size(),
mIsSecure, (mMimes.isEmpty() ? "NONE" : mMimes[0].string()));
- if (mSources.size() == 0) {
+ if (mExtractors.size() == 0) {
ALOGE("b/23705695");
return UNKNOWN_ERROR;
}
@@ -294,31 +309,10 @@
return OK;
}
-status_t NuPlayer2::GenericSource2::startSources() {
- // Start the selected A/V tracks now before we start buffering.
- // Widevine sources might re-initialize crypto when starting, if we delay
- // this to start(), all data buffered during prepare would be wasted.
- // (We don't actually start reading until start().)
- //
- // TODO: this logic may no longer be relevant after the removal of widevine
- // support
- if (mAudioTrack.mSource != NULL && mAudioTrack.mSource->start() != OK) {
- ALOGE("failed to start audio track!");
- return UNKNOWN_ERROR;
- }
-
- if (mVideoTrack.mSource != NULL && mVideoTrack.mSource->start() != OK) {
- ALOGE("failed to start video track!");
- return UNKNOWN_ERROR;
- }
-
- return OK;
-}
-
int64_t NuPlayer2::GenericSource2::getLastReadPosition() {
- if (mAudioTrack.mSource != NULL) {
+ if (mAudioTrack.mExtractor != NULL) {
return mAudioTimeUs;
- } else if (mVideoTrack.mSource != NULL) {
+ } else if (mVideoTrack.mExtractor != NULL) {
return mVideoTimeUs;
} else {
return 0;
@@ -391,51 +385,16 @@
if (!mDisconnected) {
mDataSource = dataSource;
}
- } else {
- if (property_get_bool("media.stagefright.extractremote", true) &&
- !FileSource::requiresDrm(mFd, mOffset, mLength, nullptr /* mime */)) {
- sp<IBinder> binder =
- defaultServiceManager()->getService(String16("media.extractor"));
- if (binder != nullptr) {
- ALOGD("FileSource remote");
- sp<IMediaExtractorService> mediaExService(
- interface_cast<IMediaExtractorService>(binder));
- sp<IDataSource> source =
- mediaExService->makeIDataSource(mFd, mOffset, mLength);
- ALOGV("IDataSource(FileSource): %p %d %lld %lld",
- source.get(), mFd, (long long)mOffset, (long long)mLength);
- if (source.get() != nullptr) {
- mDataSource = CreateDataSourceFromIDataSource(source);
- if (mDataSource != nullptr) {
- // Close the local file descriptor as it is not needed anymore.
- close(mFd);
- mFd = -1;
- }
- } else {
- ALOGW("extractor service cannot make data source");
- }
- } else {
- ALOGW("extractor service not running");
- }
- }
- if (mDataSource == nullptr) {
- ALOGD("FileSource local");
- mDataSource = new FileSource(mFd, mOffset, mLength);
- }
- // TODO: close should always be done on mFd, see the lines following
- // CreateDataSourceFromIDataSource above,
- // and the FileSource constructor should dup the mFd argument as needed.
- mFd = -1;
}
- if (mDataSource == NULL) {
+ if (mFd == -1 && mDataSource == NULL) {
ALOGE("Failed to create data source!");
notifyPreparedAndCleanup(UNKNOWN_ERROR);
return;
}
}
- if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
+ if (mDataSource != nullptr && mDataSource->flags() & DataSource::kIsCachingDataSource) {
mCachedSource = static_cast<NuCachedSource2 *>(mDataSource.get());
}
@@ -452,7 +411,7 @@
return;
}
- if (mVideoTrack.mSource != NULL) {
+ if (mVideoTrack.mExtractor != NULL) {
sp<MetaData> meta = getFormatMeta_l(false /* audio */);
sp<AMessage> msg = new AMessage;
err = convertMetaDataToMessage(meta, &msg);
@@ -479,13 +438,6 @@
void NuPlayer2::GenericSource2::finishPrepareAsync() {
ALOGV("finishPrepareAsync");
- status_t err = startSources();
- if (err != OK) {
- ALOGE("Failed to init start data source!");
- notifyPreparedAndCleanup(err);
- return;
- }
-
if (mIsStreaming) {
mCachedSource->resumeFetchingIfNecessary();
mPreparing = true;
@@ -494,11 +446,11 @@
notifyPrepared();
}
- if (mAudioTrack.mSource != NULL) {
+ if (mAudioTrack.mExtractor != NULL) {
postReadBuffer(MEDIA_TRACK_TYPE_AUDIO);
}
- if (mVideoTrack.mSource != NULL) {
+ if (mVideoTrack.mExtractor != NULL) {
postReadBuffer(MEDIA_TRACK_TYPE_VIDEO);
}
}
@@ -520,11 +472,11 @@
Mutex::Autolock _l(mLock);
ALOGI("start");
- if (mAudioTrack.mSource != NULL) {
+ if (mAudioTrack.mExtractor != NULL) {
postReadBuffer(MEDIA_TRACK_TYPE_AUDIO);
}
- if (mVideoTrack.mSource != NULL) {
+ if (mVideoTrack.mExtractor != NULL) {
postReadBuffer(MEDIA_TRACK_TYPE_VIDEO);
}
@@ -563,6 +515,9 @@
} else if (httpSource != NULL) {
static_cast<HTTPBase *>(httpSource.get())->disconnect();
}
+
+ mDataSourceWrapper = NULL;
+
}
status_t NuPlayer2::GenericSource2::feedMoreTSData() {
@@ -630,30 +585,27 @@
{
int32_t trackIndex;
CHECK(msg->findInt32("trackIndex", &trackIndex));
- const sp<IMediaSource> source = mSources.itemAt(trackIndex);
+ const sp<AMediaExtractorWrapper> extractor = mExtractors.itemAt(trackIndex);
Track* track;
- const char *mime;
+ AString mime;
media_track_type trackType, counterpartType;
- sp<MetaData> meta = source->getFormat();
- meta->findCString(kKeyMIMEType, &mime);
- if (!strncasecmp(mime, "audio/", 6)) {
+ sp<AMediaFormatWrapper> format = extractor->getTrackFormat(trackIndex);
+ format->getString(AMEDIAFORMAT_KEY_MIME, &mime);
+ if (!strncasecmp(mime.c_str(), "audio/", 6)) {
track = &mAudioTrack;
trackType = MEDIA_TRACK_TYPE_AUDIO;
counterpartType = MEDIA_TRACK_TYPE_VIDEO;;
} else {
- CHECK(!strncasecmp(mime, "video/", 6));
+ CHECK(!strncasecmp(mime.c_str(), "video/", 6));
track = &mVideoTrack;
trackType = MEDIA_TRACK_TYPE_VIDEO;
counterpartType = MEDIA_TRACK_TYPE_AUDIO;;
}
- if (track->mSource != NULL) {
- track->mSource->stop();
- }
- track->mSource = source;
- track->mSource->start();
+ track->mExtractor = extractor;
+ track->mExtractor->selectSingleTrack(trackIndex);
track->mIndex = trackIndex;
++mAudioDataGeneration;
++mVideoDataGeneration;
@@ -786,11 +738,10 @@
return;
}
- uint32_t textType;
- const void *data;
+ void *data = NULL;
size_t size = 0;
- if (mTimedTextTrack.mSource->getFormat()->findData(
- kKeyTextFormatData, &textType, &data, &size)) {
+ if (mTimedTextTrack.mExtractor->getTrackFormat(mTimedTextTrack.mIndex)->getBuffer(
+ "text", &data, &size)) {
mGlobalTimedText = new ABuffer(size);
if (mGlobalTimedText->data()) {
memcpy(mGlobalTimedText->data(), data, size);
@@ -806,19 +757,36 @@
}
}
+sp<AMessage> NuPlayer2::GenericSource2::getFormat(bool audio) {
+ Mutex::Autolock _l(mLock);
+ return getFormat_l(audio);
+}
+
sp<MetaData> NuPlayer2::GenericSource2::getFormatMeta(bool audio) {
Mutex::Autolock _l(mLock);
return getFormatMeta_l(audio);
}
-sp<MetaData> NuPlayer2::GenericSource2::getFormatMeta_l(bool audio) {
- sp<IMediaSource> source = audio ? mAudioTrack.mSource : mVideoTrack.mSource;
+sp<AMessage> NuPlayer2::GenericSource2::getFormat_l(bool audio) {
+ sp<AMediaExtractorWrapper> extractor = audio ? mAudioTrack.mExtractor : mVideoTrack.mExtractor;
+ size_t trackIndex = audio ? mAudioTrack.mIndex : mVideoTrack.mIndex;
- if (source == NULL) {
+ if (extractor == NULL) {
return NULL;
}
- return source->getFormat();
+ return extractor->getTrackFormat(trackIndex)->toAMessage();
+}
+
+sp<MetaData> NuPlayer2::GenericSource2::getFormatMeta_l(bool audio) {
+ sp<AMediaExtractorWrapper> extractor = audio ? mAudioTrack.mExtractor : mVideoTrack.mExtractor;
+ size_t trackIndex = audio ? mAudioTrack.mIndex : mVideoTrack.mIndex;
+
+ if (extractor == NULL) {
+ return NULL;
+ }
+
+ return convertMediaFormatWrapperToMetaData(extractor->getTrackFormat(trackIndex));
}
status_t NuPlayer2::GenericSource2::dequeueAccessUnit(
@@ -833,7 +801,7 @@
Track *track = audio ? &mAudioTrack : &mVideoTrack;
- if (track->mSource == NULL) {
+ if (track->mExtractor == NULL) {
return -EWOULDBLOCK;
}
@@ -878,11 +846,11 @@
}
if (result != OK) {
- if (mSubtitleTrack.mSource != NULL) {
+ if (mSubtitleTrack.mExtractor != NULL) {
mSubtitleTrack.mPackets->clear();
mFetchSubtitleDataGeneration++;
}
- if (mTimedTextTrack.mSource != NULL) {
+ if (mTimedTextTrack.mExtractor != NULL) {
mTimedTextTrack.mPackets->clear();
mFetchTimedTextDataGeneration++;
}
@@ -898,7 +866,7 @@
mVideoLastDequeueTimeUs = timeUs;
}
- if (mSubtitleTrack.mSource != NULL
+ if (mSubtitleTrack.mExtractor != NULL
&& !mSubtitleTrack.mPackets->hasBufferAvailable(&eosResult)) {
sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, this);
msg->setInt64("timeUs", timeUs);
@@ -906,7 +874,7 @@
msg->post();
}
- if (mTimedTextTrack.mSource != NULL
+ if (mTimedTextTrack.mExtractor != NULL
&& !mTimedTextTrack.mPackets->hasBufferAvailable(&eosResult)) {
sp<AMessage> msg = new AMessage(kWhatFetchTimedTextData, this);
msg->setInt64("timeUs", timeUs);
@@ -925,50 +893,47 @@
size_t NuPlayer2::GenericSource2::getTrackCount() const {
Mutex::Autolock _l(mLock);
- return mSources.size();
+ return mExtractors.size();
}
sp<AMessage> NuPlayer2::GenericSource2::getTrackInfo(size_t trackIndex) const {
Mutex::Autolock _l(mLock);
- size_t trackCount = mSources.size();
+ size_t trackCount = mExtractors.size();
if (trackIndex >= trackCount) {
return NULL;
}
- sp<AMessage> format = new AMessage();
- sp<MetaData> meta = mSources.itemAt(trackIndex)->getFormat();
- if (meta == NULL) {
+ sp<AMessage> format = mExtractors.itemAt(trackIndex)->getTrackFormat(trackIndex)->toAMessage();
+ if (format == NULL) {
ALOGE("no metadata for track %zu", trackIndex);
return NULL;
}
- const char *mime;
- CHECK(meta->findCString(kKeyMIMEType, &mime));
- format->setString("mime", mime);
+ AString mime;
+ CHECK(format->findString(AMEDIAFORMAT_KEY_MIME, &mime));
int32_t trackType;
- if (!strncasecmp(mime, "video/", 6)) {
+ if (!strncasecmp(mime.c_str(), "video/", 6)) {
trackType = MEDIA_TRACK_TYPE_VIDEO;
- } else if (!strncasecmp(mime, "audio/", 6)) {
+ } else if (!strncasecmp(mime.c_str(), "audio/", 6)) {
trackType = MEDIA_TRACK_TYPE_AUDIO;
- } else if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
+ } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_TEXT_3GPP)) {
trackType = MEDIA_TRACK_TYPE_TIMEDTEXT;
} else {
trackType = MEDIA_TRACK_TYPE_UNKNOWN;
}
format->setInt32("type", trackType);
- const char *lang;
- if (!meta->findCString(kKeyMediaLanguage, &lang)) {
- lang = "und";
+ AString lang;
+ if (!format->findString("language", &lang)) {
+ format->setString("language", "und");
}
- format->setString("language", lang);
if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) {
int32_t isAutoselect = 1, isDefault = 0, isForced = 0;
- meta->findInt32(kKeyTrackIsAutoselect, &isAutoselect);
- meta->findInt32(kKeyTrackIsDefault, &isDefault);
- meta->findInt32(kKeyTrackIsForced, &isForced);
+ format->findInt32(AMEDIAFORMAT_KEY_IS_AUTOSELECT, &isAutoselect);
+ format->findInt32(AMEDIAFORMAT_KEY_IS_DEFAULT, &isDefault);
+ format->findInt32(AMEDIAFORMAT_KEY_IS_FORCED_SUBTITLE, &isForced);
format->setInt32("auto", !!isAutoselect);
format->setInt32("default", !!isDefault);
@@ -998,7 +963,7 @@
break;
}
- if (track != NULL && track->mSource != NULL) {
+ if (track != NULL && track->mExtractor != NULL) {
return track->mIndex;
}
@@ -1009,49 +974,45 @@
Mutex::Autolock _l(mLock);
ALOGV("%s track: %zu", select ? "select" : "deselect", trackIndex);
- if (trackIndex >= mSources.size()) {
+ if (trackIndex >= mExtractors.size()) {
return BAD_INDEX;
}
if (!select) {
Track* track = NULL;
- if (mSubtitleTrack.mSource != NULL && trackIndex == mSubtitleTrack.mIndex) {
+ if (mSubtitleTrack.mExtractor != NULL && trackIndex == mSubtitleTrack.mIndex) {
track = &mSubtitleTrack;
mFetchSubtitleDataGeneration++;
- } else if (mTimedTextTrack.mSource != NULL && trackIndex == mTimedTextTrack.mIndex) {
+ } else if (mTimedTextTrack.mExtractor != NULL && trackIndex == mTimedTextTrack.mIndex) {
track = &mTimedTextTrack;
mFetchTimedTextDataGeneration++;
}
if (track == NULL) {
return INVALID_OPERATION;
}
- track->mSource->stop();
- track->mSource = NULL;
+ track->mExtractor = NULL;
track->mPackets->clear();
return OK;
}
- const sp<IMediaSource> source = mSources.itemAt(trackIndex);
- sp<MetaData> meta = source->getFormat();
+ const sp<AMediaExtractorWrapper> extractor = mExtractors.itemAt(trackIndex);
+ sp<MetaData> meta = convertMediaFormatWrapperToMetaData(extractor->getTrackFormat(trackIndex));
const char *mime;
CHECK(meta->findCString(kKeyMIMEType, &mime));
if (!strncasecmp(mime, "text/", 5)) {
bool isSubtitle = strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP);
Track *track = isSubtitle ? &mSubtitleTrack : &mTimedTextTrack;
- if (track->mSource != NULL && track->mIndex == trackIndex) {
+ if (track->mExtractor != NULL && track->mIndex == trackIndex) {
return OK;
}
track->mIndex = trackIndex;
- if (track->mSource != NULL) {
- track->mSource->stop();
- }
- track->mSource = mSources.itemAt(trackIndex);
- track->mSource->start();
+ track->mExtractor = mExtractors.itemAt(trackIndex);
+ track->mExtractor->selectSingleTrack(trackIndex);
if (track->mPackets == NULL) {
- track->mPackets = new AnotherPacketSource(track->mSource->getFormat());
+ track->mPackets = new AnotherPacketSource(meta);
} else {
track->mPackets->clear();
- track->mPackets->setFormat(track->mSource->getFormat());
+ track->mPackets->setFormat(meta);
}
@@ -1062,7 +1023,7 @@
}
status_t eosResult; // ignored
- if (mSubtitleTrack.mSource != NULL
+ if (mSubtitleTrack.mExtractor != NULL
&& !mSubtitleTrack.mPackets->hasBufferAvailable(&eosResult)) {
sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, this);
msg->setInt64("timeUs", timeUs);
@@ -1074,7 +1035,7 @@
msg2->setInt32("generation", mFetchTimedTextDataGeneration);
msg2->post();
- if (mTimedTextTrack.mSource != NULL
+ if (mTimedTextTrack.mExtractor != NULL
&& !mTimedTextTrack.mPackets->hasBufferAvailable(&eosResult)) {
sp<AMessage> msg = new AMessage(kWhatFetchTimedTextData, this);
msg->setInt64("timeUs", timeUs);
@@ -1086,7 +1047,7 @@
} else if (!strncasecmp(mime, "audio/", 6) || !strncasecmp(mime, "video/", 6)) {
bool audio = !strncasecmp(mime, "audio/", 6);
Track *track = audio ? &mAudioTrack : &mVideoTrack;
- if (track->mSource != NULL && track->mIndex == trackIndex) {
+ if (track->mExtractor != NULL && track->mIndex == trackIndex) {
return OK;
}
@@ -1133,7 +1094,7 @@
}
status_t NuPlayer2::GenericSource2::doSeek(int64_t seekTimeUs, MediaPlayer2SeekMode mode) {
- if (mVideoTrack.mSource != NULL) {
+ if (mVideoTrack.mExtractor != NULL) {
++mVideoDataGeneration;
int64_t actualTimeUs;
@@ -1145,18 +1106,18 @@
mVideoLastDequeueTimeUs = actualTimeUs;
}
- if (mAudioTrack.mSource != NULL) {
+ if (mAudioTrack.mExtractor != NULL) {
++mAudioDataGeneration;
readBuffer(MEDIA_TRACK_TYPE_AUDIO, seekTimeUs, MediaPlayer2SeekMode::SEEK_CLOSEST);
mAudioLastDequeueTimeUs = seekTimeUs;
}
- if (mSubtitleTrack.mSource != NULL) {
+ if (mSubtitleTrack.mExtractor != NULL) {
mSubtitleTrack.mPackets->clear();
mFetchSubtitleDataGeneration++;
}
- if (mTimedTextTrack.mSource != NULL) {
+ if (mTimedTextTrack.mExtractor != NULL) {
mTimedTextTrack.mPackets->clear();
mFetchTimedTextDataGeneration++;
}
@@ -1227,10 +1188,12 @@
}
if (trackType == MEDIA_TRACK_TYPE_TIMEDTEXT) {
- const char *mime;
- CHECK(mTimedTextTrack.mSource != NULL
- && mTimedTextTrack.mSource->getFormat()->findCString(kKeyMIMEType, &mime));
- meta->setString("mime", mime);
+ AString mime;
+ sp<AMediaExtractorWrapper> extractor = mTimedTextTrack.mExtractor;
+ size_t trackIndex = mTimedTextTrack.mIndex;
+ CHECK(extractor != NULL
+ && extractor->getTrackFormat(trackIndex)->getString(AMEDIAFORMAT_KEY_MIME, &mime));
+ meta->setString("mime", mime.c_str());
}
int64_t durationUs;
@@ -1239,7 +1202,7 @@
}
if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) {
- meta->setInt32("trackIndex", mSubtitleTrack.mIndex);
+ meta->setInt32(AMEDIAFORMAT_KEY_TRACK_INDEX, mSubtitleTrack.mIndex);
}
uint32_t dataType; // unused
@@ -1255,7 +1218,7 @@
if (mb->meta_data().findData(
kKeyMpegUserData, &dataType, &mpegUserDataPointer, &mpegUserDataLength)) {
sp<ABuffer> mpegUserData = ABuffer::CreateAsCopy(mpegUserDataPointer, mpegUserDataLength);
- meta->setBuffer("mpegUserData", mpegUserData);
+ meta->setBuffer(AMEDIAFORMAT_KEY_MPEG_USER_DATA, mpegUserData);
}
mb->release();
@@ -1327,7 +1290,7 @@
TRESPASS();
}
- if (track->mSource == NULL) {
+ if (track->mExtractor == NULL) {
return;
}
@@ -1335,109 +1298,77 @@
*actualTimeUs = seekTimeUs;
}
- MediaSource::ReadOptions options;
bool seeking = false;
+ sp<AMediaExtractorWrapper> extractor = track->mExtractor;
if (seekTimeUs >= 0) {
- options.setSeekTo(seekTimeUs, mode);
+ extractor->seekTo(seekTimeUs, mode);
seeking = true;
}
- const bool couldReadMultiple = (track->mSource->supportReadMultiple());
-
- if (couldReadMultiple) {
- options.setNonBlocking();
- }
-
int32_t generation = getDataGeneration(trackType);
for (size_t numBuffers = 0; numBuffers < maxBuffers; ) {
- Vector<MediaBufferBase *> mediaBuffers;
- status_t err = NO_ERROR;
+ Vector<sp<ABuffer> > aBuffers;
- sp<IMediaSource> source = track->mSource;
mLock.unlock();
- if (couldReadMultiple) {
- err = source->readMultiple(
- &mediaBuffers, maxBuffers - numBuffers, &options);
- } else {
- MediaBufferBase *mbuf = NULL;
- err = source->read(&mbuf, &options);
- if (err == OK && mbuf != NULL) {
- mediaBuffers.push_back(mbuf);
- }
+
+ sp<AMediaFormatWrapper> format;
+ ssize_t sampleSize = -1;
+ status_t err = extractor->getSampleFormat(format);
+ if (err == OK) {
+ sampleSize = extractor->getSampleSize();
}
+
+ if (err != OK || sampleSize < 0) {
+ mLock.lock();
+ track->mPackets->signalEOS(err != OK ? err : ERROR_END_OF_STREAM);
+ break;
+ }
+
+ sp<ABuffer> abuf = new ABuffer(sampleSize);
+ sampleSize = extractor->readSampleData(abuf);
mLock.lock();
- options.clearNonPersistent();
-
- size_t id = 0;
- size_t count = mediaBuffers.size();
-
// in case track has been changed since we don't have lock for some time.
if (generation != getDataGeneration(trackType)) {
- for (; id < count; ++id) {
- mediaBuffers[id]->release();
- }
break;
}
- for (; id < count; ++id) {
- int64_t timeUs;
- MediaBufferBase *mbuf = mediaBuffers[id];
- if (!mbuf->meta_data().findInt64(kKeyTime, &timeUs)) {
- mbuf->meta_data().dumpToLog();
- track->mPackets->signalEOS(ERROR_MALFORMED);
- break;
- }
- if (trackType == MEDIA_TRACK_TYPE_AUDIO) {
- mAudioTimeUs = timeUs;
- } else if (trackType == MEDIA_TRACK_TYPE_VIDEO) {
- mVideoTimeUs = timeUs;
- }
-
- queueDiscontinuityIfNeeded(seeking, formatChange, trackType, track);
-
- sp<ABuffer> buffer = mediaBufferToABuffer(mbuf, trackType);
- if (numBuffers == 0 && actualTimeUs != nullptr) {
- *actualTimeUs = timeUs;
- }
- if (seeking && buffer != nullptr) {
- sp<AMessage> meta = buffer->meta();
- if (meta != nullptr && mode == MediaPlayer2SeekMode::SEEK_CLOSEST
- && seekTimeUs > timeUs) {
- sp<AMessage> extra = new AMessage;
- extra->setInt64("resume-at-mediaTimeUs", seekTimeUs);
- meta->setMessage("extra", extra);
- }
- }
-
- track->mPackets->queueAccessUnit(buffer);
- formatChange = false;
- seeking = false;
- ++numBuffers;
- }
- if (id < count) {
- // Error, some mediaBuffer doesn't have kKeyTime.
- for (; id < count; ++id) {
- mediaBuffers[id]->release();
- }
+ int64_t timeUs = extractor->getSampleTime();
+ if (timeUs < 0) {
+ track->mPackets->signalEOS(ERROR_MALFORMED);
break;
}
- if (err == WOULD_BLOCK) {
- break;
- } else if (err == INFO_FORMAT_CHANGED) {
-#if 0
- track->mPackets->queueDiscontinuity(
- ATSParser::DISCONTINUITY_FORMATCHANGE,
- NULL,
- false /* discard */);
-#endif
- } else if (err != OK) {
- queueDiscontinuityIfNeeded(seeking, formatChange, trackType, track);
- track->mPackets->signalEOS(err);
- break;
+ sp<AMessage> meta = abuf->meta();
+ format->writeToAMessage(meta);
+ meta->setInt64("timeUs", timeUs);
+ if (trackType == MEDIA_TRACK_TYPE_AUDIO) {
+ mAudioTimeUs = timeUs;
+ } else if (trackType == MEDIA_TRACK_TYPE_VIDEO) {
+ mVideoTimeUs = timeUs;
}
+
+ queueDiscontinuityIfNeeded(seeking, formatChange, trackType, track);
+
+ if (numBuffers == 0 && actualTimeUs != nullptr) {
+ *actualTimeUs = timeUs;
+ }
+ if (seeking) {
+ if (meta != nullptr && mode == MediaPlayer2SeekMode::SEEK_CLOSEST
+ && seekTimeUs > timeUs) {
+ sp<AMessage> extra = new AMessage;
+ extra->setInt64("resume-at-mediaTimeUs", seekTimeUs);
+ meta->setMessage("extra", extra);
+ }
+ }
+
+ track->mPackets->queueAccessUnit(abuf);
+ formatChange = false;
+ seeking = false;
+ ++numBuffers;
+ extractor->advance();
+
}
if (mIsStreaming
@@ -1453,7 +1384,7 @@
if (mPreparing || mSentPauseOnBuffering) {
Track *counterTrack =
(trackType == MEDIA_TRACK_TYPE_VIDEO ? &mAudioTrack : &mVideoTrack);
- if (counterTrack->mSource != NULL) {
+ if (counterTrack->mExtractor != NULL) {
durationUs = counterTrack->mPackets->getBufferedDurationUs(&finalResult);
}
if (finalResult == ERROR_END_OF_STREAM || durationUs >= markUs) {
@@ -1649,26 +1580,22 @@
// same source without being reset (called by prepareAsync/initFromDataSource)
mIsDrmReleased = false;
- if (mFileMeta == NULL) {
- ALOGI("checkDrmInfo: No metadata");
+ if (mExtractor == NULL) {
+ ALOGV("checkDrmInfo: No extractor");
return OK; // letting the caller responds accordingly
}
- uint32_t type;
- const void *pssh;
- size_t psshsize;
-
- if (!mFileMeta->findData(kKeyPssh, &type, &pssh, &psshsize)) {
+ PsshInfo *psshInfo = mExtractor->getPsshInfo();
+ if (psshInfo == NULL) {
ALOGV("checkDrmInfo: No PSSH");
return OK; // source without DRM info
}
- sp<ABuffer> drmInfoBuffer = NuPlayer2Drm::retrieveDrmInfo(pssh, psshsize);
- ALOGV("checkDrmInfo: MEDIA2_DRM_INFO PSSH size: %d drmInfoBuffer size: %d",
- (int)psshsize, (int)drmInfoBuffer->size());
+ sp<ABuffer> drmInfoBuffer = NuPlayer2Drm::retrieveDrmInfo(psshInfo);
+ ALOGV("checkDrmInfo: MEDIA_DRM_INFO PSSH drm info size: %d", (int)drmInfoBuffer->size());
if (drmInfoBuffer->size() == 0) {
- ALOGE("checkDrmInfo: Unexpected drmInfoBuffer size: 0");
+ ALOGE("checkDrmInfo: Unexpected parcel size: 0");
return UNKNOWN_ERROR;
}
diff --git a/media/libmediaplayer2/nuplayer2/GenericSource2.h b/media/libmediaplayer2/nuplayer2/GenericSource2.h
index 896c397..9bc5182 100644
--- a/media/libmediaplayer2/nuplayer2/GenericSource2.h
+++ b/media/libmediaplayer2/nuplayer2/GenericSource2.h
@@ -25,6 +25,9 @@
#include <media/stagefright/MediaBuffer.h>
#include <mediaplayer2/mediaplayer2.h>
+#include <media/NdkMediaDataSource.h>
+#include <media/NdkMediaExtractor.h>
+#include <media/NdkWrapper.h>
namespace android {
@@ -101,6 +104,7 @@
virtual void onMessageReceived(const sp<AMessage> &msg);
+ virtual sp<AMessage> getFormat(bool audio);
virtual sp<MetaData> getFormatMeta(bool audio);
private:
@@ -122,19 +126,14 @@
struct Track {
size_t mIndex;
- sp<IMediaSource> mSource;
+ sp<AMediaExtractorWrapper> mExtractor;
sp<AnotherPacketSource> mPackets;
};
- Vector<sp<IMediaSource> > mSources;
- Track mAudioTrack;
int64_t mAudioTimeUs;
int64_t mAudioLastDequeueTimeUs;
- Track mVideoTrack;
int64_t mVideoTimeUs;
int64_t mVideoLastDequeueTimeUs;
- Track mSubtitleTrack;
- Track mTimedTextTrack;
BufferingSettings mBufferingSettings;
int32_t mPrevBufferPercentage;
@@ -164,12 +163,20 @@
sp<NuCachedSource2> mCachedSource;
sp<DataSource> mHttpSource;
sp<MetaData> mFileMeta;
+ sp<AMediaDataSourceWrapper> mDataSourceWrapper;
+ sp<AMediaExtractorWrapper> mExtractor;
+ Vector<sp<AMediaExtractorWrapper> > mExtractors;
bool mStarted;
bool mPreparing;
int64_t mBitrate;
uint32_t mPendingReadBufferTypes;
sp<ABuffer> mGlobalTimedText;
+ Track mVideoTrack;
+ Track mAudioTrack;
+ Track mSubtitleTrack;
+ Track mTimedTextTrack;
+
mutable Mutex mLock;
sp<ALooper> mLooper;
@@ -227,6 +234,7 @@
void sendCacheStats();
+ sp<AMessage> getFormat_l(bool audio);
sp<MetaData> getFormatMeta_l(bool audio);
int32_t getDataGeneration(media_track_type type) const;
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
index 5971a8b..060b698 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
@@ -2842,7 +2842,7 @@
void NuPlayer2::sendSubtitleData(const sp<ABuffer> &buffer, int32_t baseIndex) {
int32_t trackIndex;
int64_t timeUs, durationUs;
- CHECK(buffer->meta()->findInt32("trackIndex", &trackIndex));
+ CHECK(buffer->meta()->findInt32(AMEDIAFORMAT_KEY_TRACK_INDEX, &trackIndex));
CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
CHECK(buffer->meta()->findInt64("durationUs", &durationUs));
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp
index e4afd5b..e48e388 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp
@@ -21,6 +21,7 @@
#include "NuPlayer2CCDecoder.h"
+#include <media/NdkMediaFormat.h>
#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -301,7 +302,7 @@
// returns true if a new CC track is found
bool NuPlayer2::CCDecoder::extractFromMPEGUserData(const sp<ABuffer> &accessUnit) {
sp<ABuffer> mpegUserData;
- if (!accessUnit->meta()->findBuffer("mpegUserData", &mpegUserData)
+ if (!accessUnit->meta()->findBuffer(AMEDIAFORMAT_KEY_MPEG_USER_DATA, &mpegUserData)
|| mpegUserData == NULL) {
return false;
}
@@ -538,7 +539,7 @@
dumpBytePair(ccBuf);
#endif
- ccBuf->meta()->setInt32("trackIndex", mSelectedTrack);
+ ccBuf->meta()->setInt32(AMEDIAFORMAT_KEY_TRACK_INDEX, mSelectedTrack);
ccBuf->meta()->setInt64("timeUs", timeUs);
ccBuf->meta()->setInt64("durationUs", 0ll);
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 5cbf976..9ab9aae 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -105,7 +105,7 @@
: MediaRecorderBase(opPackageName),
mWriter(NULL),
mOutputFd(-1),
- mAudioSource(AUDIO_SOURCE_CNT),
+ mAudioSource((audio_source_t)AUDIO_SOURCE_CNT), // initialize with invalid value
mVideoSource(VIDEO_SOURCE_LIST_END),
mStarted(false),
mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
@@ -2047,7 +2047,7 @@
stop();
// No audio or video source by default
- mAudioSource = AUDIO_SOURCE_CNT;
+ mAudioSource = (audio_source_t)AUDIO_SOURCE_CNT; // reset to invalid value
mVideoSource = VIDEO_SOURCE_LIST_END;
// Default parameters
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index e7c3deb..cbc3015 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -1233,7 +1233,7 @@
}
if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) {
- meta->setInt32("trackIndex", mSubtitleTrack.mIndex);
+ meta->setInt32("track-index", mSubtitleTrack.mIndex);
}
uint32_t dataType; // unused
@@ -1249,7 +1249,7 @@
if (mb->meta_data().findData(
kKeyMpegUserData, &dataType, &mpegUserDataPointer, &mpegUserDataLength)) {
sp<ABuffer> mpegUserData = ABuffer::CreateAsCopy(mpegUserDataPointer, mpegUserDataLength);
- meta->setBuffer("mpegUserData", mpegUserData);
+ meta->setBuffer("mpeg-user-data", mpegUserData);
}
mb->release();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index dce3e0a..bd83c6d 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -2702,7 +2702,7 @@
void NuPlayer::sendSubtitleData(const sp<ABuffer> &buffer, int32_t baseIndex) {
int32_t trackIndex;
int64_t timeUs, durationUs;
- CHECK(buffer->meta()->findInt32("trackIndex", &trackIndex));
+ CHECK(buffer->meta()->findInt32("track-index", &trackIndex));
CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
CHECK(buffer->meta()->findInt64("durationUs", &durationUs));
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
index 0a8b97f..0402fca 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
@@ -301,7 +301,7 @@
// returns true if a new CC track is found
bool NuPlayer::CCDecoder::extractFromMPEGUserData(const sp<ABuffer> &accessUnit) {
sp<ABuffer> mpegUserData;
- if (!accessUnit->meta()->findBuffer("mpegUserData", &mpegUserData)
+ if (!accessUnit->meta()->findBuffer("mpeg-user-data", &mpegUserData)
|| mpegUserData == NULL) {
return false;
}
@@ -538,7 +538,7 @@
dumpBytePair(ccBuf);
#endif
- ccBuf->meta()->setInt32("trackIndex", mSelectedTrack);
+ ccBuf->meta()->setInt32("track-index", mSelectedTrack);
ccBuf->meta()->setInt64("timeUs", timeUs);
ccBuf->meta()->setInt64("durationUs", 0ll);
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 65348e5..13d80f5 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -137,7 +137,6 @@
"libstagefright_omx",
"libstagefright_omx_utils",
"libstagefright_xmlparser",
- "libdl",
"libRScpp",
"libhidlallocatorutils",
"libhidlbase",
diff --git a/media/libstagefright/CCodecBufferChannel.cpp b/media/libstagefright/CCodecBufferChannel.cpp
index 449c6aa..5ce49d9 100644
--- a/media/libstagefright/CCodecBufferChannel.cpp
+++ b/media/libstagefright/CCodecBufferChannel.cpp
@@ -760,19 +760,18 @@
ALOGV("graphic = %s", graphic ? "true" : "false");
std::shared_ptr<C2BlockPool> pool;
- err = GetCodec2BlockPool(
- graphic ? C2BlockPool::BASIC_GRAPHIC : C2BlockPool::BASIC_LINEAR,
- component,
- &pool);
+ if (graphic) {
+ err = GetCodec2BlockPool(C2BlockPool::BASIC_GRAPHIC, component, &pool);
+ } else {
+ err = CreateCodec2BlockPool(C2PlatformAllocatorStore::ION,
+ component, &pool);
+ }
+
if (err == C2_OK) {
(*buffers)->setPool(pool);
} else {
// TODO: error
}
- // TODO: remove once we switch to proper buffer pool.
- if (!graphic) {
- *buffers = (*buffers)->toArrayMode();
- }
}
{
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index 540cf8c..f6fc813 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -446,12 +446,17 @@
ssize_t NuMediaExtractor::fetchAllTrackSamples(
int64_t seekTimeUs, MediaSource::ReadOptions::SeekMode mode) {
TrackInfo *minInfo = NULL;
- ssize_t minIndex = -1;
+ ssize_t minIndex = ERROR_END_OF_STREAM;
for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
TrackInfo *info = &mSelectedTracks.editItemAt(i);
fetchTrackSamples(info, seekTimeUs, mode);
+ status_t err = info->mFinalResult;
+ if (err != OK && err != ERROR_END_OF_STREAM) {
+ return err;
+ }
+
if (info->mSamples.empty()) {
continue;
}
@@ -721,7 +726,8 @@
ssize_t minIndex = fetchAllTrackSamples();
if (minIndex < 0) {
- return ERROR_END_OF_STREAM;
+ status_t err = minIndex;
+ return err;
}
TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
diff --git a/media/libstagefright/codec2/SimpleC2Component.cpp b/media/libstagefright/codec2/SimpleC2Component.cpp
index 333cfeb..0753b08 100644
--- a/media/libstagefright/codec2/SimpleC2Component.cpp
+++ b/media/libstagefright/codec2/SimpleC2Component.cpp
@@ -302,12 +302,15 @@
if (err != C2_OK) {
return err;
}
- err = GetCodec2BlockPool(
- (outputFormat.value == C2FormatVideo)
- ? C2BlockPool::BASIC_GRAPHIC
- : C2BlockPool::BASIC_LINEAR,
- shared_from_this(),
- &mOutputBlockPool);
+ if (outputFormat.value == C2FormatVideo) {
+ err = GetCodec2BlockPool(
+ C2BlockPool::BASIC_GRAPHIC,
+ shared_from_this(), &mOutputBlockPool);
+ } else {
+ err = CreateCodec2BlockPool(
+ C2PlatformAllocatorStore::ION,
+ shared_from_this(), &mOutputBlockPool);
+ }
if (err != C2_OK) {
return err;
}
diff --git a/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp b/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp
index 038be48..f389abc 100644
--- a/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp
+++ b/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp
@@ -232,7 +232,8 @@
class C2BufferTest : public ::testing::Test {
public:
C2BufferTest()
- : mLinearAllocator(std::make_shared<C2AllocatorIon>('i')),
+ : mBlockPoolId(C2BlockPool::PLATFORM_START),
+ mLinearAllocator(std::make_shared<C2AllocatorIon>('i')),
mSize(0u),
mAddr(nullptr),
mGraphicAllocator(std::make_shared<C2AllocatorGralloc>('g')) {
@@ -281,7 +282,7 @@
}
std::shared_ptr<C2BlockPool> makeLinearBlockPool() {
- return std::make_shared<C2BasicLinearBlockPool>(mLinearAllocator);
+ return std::make_shared<C2PooledBlockPool>(mLinearAllocator, mBlockPoolId++);
}
void allocateGraphic(uint32_t width, uint32_t height) {
@@ -328,6 +329,7 @@
}
private:
+ C2BlockPool::local_id_t mBlockPoolId;
std::shared_ptr<C2Allocator> mLinearAllocator;
std::shared_ptr<C2LinearAllocation> mLinearAllocation;
size_t mSize;
@@ -745,4 +747,23 @@
EXPECT_FALSE(buffer->hasInfo(info2->type()));
}
+TEST_F(C2BufferTest, MultipleLinearMapTest) {
+ std::shared_ptr<C2BlockPool> pool(makeLinearBlockPool());
+ constexpr size_t kCapacity = 524288u;
+ for (int i = 0; i < 100; ++i) {
+ std::vector<C2WriteView> wViews;
+ std::vector<C2ReadView> rViews;
+ for (int j = 0; j < 16; ++j) {
+ std::shared_ptr<C2LinearBlock> block;
+ ASSERT_EQ(C2_OK, pool->fetchLinearBlock(
+ kCapacity,
+ { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE },
+ &block));
+ wViews.push_back(block->map().get());
+ C2ConstLinearBlock cBlock = block->share(0, kCapacity / 2, C2Fence());
+ rViews.push_back(cBlock.map().get());
+ }
+ }
+}
+
} // namespace android
diff --git a/media/libstagefright/codec2/vndk/Android.bp b/media/libstagefright/codec2/vndk/Android.bp
index 47afd42..95e7c39 100644
--- a/media/libstagefright/codec2/vndk/Android.bp
+++ b/media/libstagefright/codec2/vndk/Android.bp
@@ -39,14 +39,17 @@
shared_libs: [
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.mapper@2.0",
+ "android.hardware.media.bufferpool@1.0",
"libbinder",
"libcutils",
"libdl",
"libhardware",
"libhidlbase",
"libion",
+ "libfmq",
"liblog",
"libstagefright_foundation",
+ "libstagefright_bufferpool@1.0",
"libui",
"libutils",
],
diff --git a/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp b/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp
index cf7658a..664c09e 100644
--- a/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp
+++ b/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp
@@ -18,6 +18,8 @@
#define LOG_TAG "C2AllocatorIon"
#include <utils/Log.h>
+#include <list>
+
#include <ion/ion.h>
#include <sys/mman.h>
#include <unistd.h>
@@ -148,6 +150,7 @@
* so that we can capture the error.
*
* \param ionFd ion client (ownership transferred to created object)
+ * \param owned whehter native_handle_t is owned by an allocation or not.
* \param capacity size of allocation
* \param bufferFd buffer handle (ownership transferred to created object). Must be
* invalid if err is not 0.
@@ -155,14 +158,14 @@
* invalid if err is not 0.
* \param err errno during buffer allocation or import
*/
- Impl(int ionFd, size_t capacity, int bufferFd, ion_user_handle_t buffer, C2Allocator::id_t id, int err)
+ Impl(int ionFd, bool owned, size_t capacity, int bufferFd, ion_user_handle_t buffer, C2Allocator::id_t id, int err)
: mIonFd(ionFd),
+ mHandleOwned(owned),
mHandle(bufferFd, capacity),
mBuffer(buffer),
mId(id),
mInit(c2_map_errno<ENOMEM, EACCES, EINVAL>(err)),
- mMapFd(-1),
- mMapSize(0) {
+ mMapFd(-1) {
if (mInit != C2_OK) {
// close ionFd now on error
if (mIonFd >= 0) {
@@ -188,7 +191,7 @@
static Impl *Import(int ionFd, size_t capacity, int bufferFd, C2Allocator::id_t id) {
ion_user_handle_t buffer = -1;
int ret = ion_import(ionFd, bufferFd, &buffer);
- return new Impl(ionFd, capacity, bufferFd, buffer, id, ret);
+ return new Impl(ionFd, false, capacity, bufferFd, buffer, id, ret);
}
/**
@@ -215,13 +218,14 @@
buffer = -1;
}
}
- return new Impl(ionFd, size, bufferFd, buffer, id, ret);
+ return new Impl(ionFd, true, size, bufferFd, buffer, id, ret);
}
c2_status_t map(size_t offset, size_t size, C2MemoryUsage usage, C2Fence *fence, void **addr) {
(void)fence; // TODO: wait for fence
*addr = nullptr;
- if (mMapSize > 0) {
+ if (!mMappings.empty()) {
+ ALOGV("multiple map");
// TODO: technically we should return DUPLICATE here, but our block views don't
// actually unmap, so we end up remapping an ion buffer multiple times.
//
@@ -244,54 +248,63 @@
size_t alignmentBytes = offset % PAGE_SIZE;
size_t mapOffset = offset - alignmentBytes;
size_t mapSize = size + alignmentBytes;
+ Mapping map = { nullptr, alignmentBytes, mapSize };
c2_status_t err = C2_OK;
if (mMapFd == -1) {
int ret = ion_map(mIonFd, mBuffer, mapSize, prot,
- flags, mapOffset, (unsigned char**)&mMapAddr, &mMapFd);
+ flags, mapOffset, (unsigned char**)&map.addr, &mMapFd);
if (ret) {
mMapFd = -1;
- *addr = nullptr;
+ map.addr = *addr = nullptr;
err = c2_map_errno<EINVAL>(-ret);
} else {
- *addr = (uint8_t *)mMapAddr + alignmentBytes;
- mMapAlignmentBytes = alignmentBytes;
- mMapSize = mapSize;
+ *addr = (uint8_t *)map.addr + alignmentBytes;
}
} else {
- mMapAddr = mmap(nullptr, mapSize, prot, flags, mMapFd, mapOffset);
- if (mMapAddr == MAP_FAILED) {
- mMapAddr = *addr = nullptr;
+ map.addr = mmap(nullptr, mapSize, prot, flags, mMapFd, mapOffset);
+ if (map.addr == MAP_FAILED) {
+ map.addr = *addr = nullptr;
err = c2_map_errno<EINVAL>(errno);
} else {
- *addr = (uint8_t *)mMapAddr + alignmentBytes;
- mMapAlignmentBytes = alignmentBytes;
- mMapSize = mapSize;
+ *addr = (uint8_t *)map.addr + alignmentBytes;
}
}
+ if (map.addr) {
+ mMappings.push_back(map);
+ }
return err;
}
c2_status_t unmap(void *addr, size_t size, C2Fence *fence) {
- if (mMapFd < 0 || mMapSize == 0) {
+ if (mMapFd < 0 || mMappings.empty()) {
return C2_NOT_FOUND;
}
- if (addr != (uint8_t *)mMapAddr + mMapAlignmentBytes ||
- size + mMapAlignmentBytes != mMapSize) {
- return C2_BAD_VALUE;
+ for (auto it = mMappings.begin(); it != mMappings.end(); ++it) {
+ if (addr != (uint8_t *)it->addr + it->alignmentBytes ||
+ size + it->alignmentBytes != it->size) {
+ continue;
+ }
+ int err = munmap(it->addr, it->size);
+ if (err != 0) {
+ return c2_map_errno<EINVAL>(errno);
+ }
+ if (fence) {
+ *fence = C2Fence(); // not using fences
+ }
+ (void)mMappings.erase(it);
+ return C2_OK;
}
- int err = munmap(mMapAddr, mMapSize);
- if (err != 0) {
- return c2_map_errno<EINVAL>(errno);
- }
- if (fence) {
- *fence = C2Fence(); // not using fences
- }
- mMapSize = 0;
- return C2_OK;
+ return C2_BAD_VALUE;
}
~Impl() {
+ if (!mMappings.empty()) {
+ ALOGD("Dangling mappings!");
+ for (const Mapping &map : mMappings) {
+ (void)munmap(map.addr, map.size);
+ }
+ }
if (mMapFd >= 0) {
close(mMapFd);
mMapFd = -1;
@@ -302,7 +315,9 @@
if (mIonFd >= 0) {
close(mIonFd);
}
- native_handle_close(&mHandle);
+ if (mHandleOwned) {
+ native_handle_close(&mHandle);
+ }
}
c2_status_t status() const {
@@ -323,14 +338,18 @@
private:
int mIonFd;
+ bool mHandleOwned;
C2HandleIon mHandle;
ion_user_handle_t mBuffer;
C2Allocator::id_t mId;
c2_status_t mInit;
int mMapFd; // only one for now
- void *mMapAddr;
- size_t mMapAlignmentBytes;
- size_t mMapSize;
+ struct Mapping {
+ void *addr;
+ size_t alignmentBytes;
+ size_t size;
+ };
+ std::list<Mapping> mMappings;
};
c2_status_t C2AllocationIon::map(
diff --git a/media/libstagefright/codec2/vndk/C2Buffer.cpp b/media/libstagefright/codec2/vndk/C2Buffer.cpp
index dc765f5..91b21c2 100644
--- a/media/libstagefright/codec2/vndk/C2Buffer.cpp
+++ b/media/libstagefright/codec2/vndk/C2Buffer.cpp
@@ -25,8 +25,16 @@
#include <C2BufferPriv.h>
#include <C2BlockInternal.h>
+#include <ClientManager.h>
+
namespace {
+using android::hardware::media::bufferpool::V1_0::ResultStatus;
+using android::hardware::media::bufferpool::V1_0::implementation::BufferPoolAllocation;
+using android::hardware::media::bufferpool::V1_0::implementation::BufferPoolAllocator;
+using android::hardware::media::bufferpool::V1_0::implementation::ClientManager;
+using android::hardware::media::bufferpool::V1_0::implementation::ConnectionId;
+
// This anonymous namespace contains the helper classes that allow our implementation to create
// block/buffer objects.
//
@@ -341,6 +349,257 @@
return std::shared_ptr<C2LinearBlock>(new C2LinearBlock(impl, *impl));
}
+/**
+ * Wrapped C2Allocator which is injected to buffer pool on behalf of
+ * C2BlockPool.
+ */
+class _C2BufferPoolAllocator : public BufferPoolAllocator {
+public:
+ _C2BufferPoolAllocator(const std::shared_ptr<C2Allocator> &allocator)
+ : mAllocator(allocator) {}
+
+ ~_C2BufferPoolAllocator() override {}
+
+ ResultStatus allocate(const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<BufferPoolAllocation> *alloc) override;
+
+ bool compatible(const std::vector<uint8_t> &newParams,
+ const std::vector<uint8_t> &oldParams) override;
+
+ // Methods for codec2 component (C2BlockPool).
+ /**
+ * Transforms linear allocation parameters for C2Allocator to parameters
+ * for buffer pool.
+ *
+ * @param capacity size of linear allocation
+ * @param usage memory usage pattern for linear allocation
+ * @param params allocation parameters for buffer pool
+ */
+ void getLinearParams(uint32_t capacity, C2MemoryUsage usage,
+ std::vector<uint8_t> *params);
+
+ /**
+ * Transforms graphic allocation parameters for C2Allocator to parameters
+ * for buffer pool.
+ *
+ * @param width width of graphic allocation
+ * @param height height of graphic allocation
+ * @param format color format of graphic allocation
+ * @param params allocation parameter for buffer pool
+ */
+ void getGraphicParams(uint32_t width, uint32_t height,
+ uint32_t format, C2MemoryUsage usage,
+ std::vector<uint8_t> *params);
+
+ /**
+ * Transforms an existing native handle to an C2LinearAllcation.
+ * Wrapper to C2Allocator#priorLinearAllocation
+ */
+ c2_status_t priorLinearAllocation(
+ const C2Handle *handle,
+ std::shared_ptr<C2LinearAllocation> *c2Allocation);
+
+ /**
+ * Transforms an existing native handle to an C2GraphicAllcation.
+ * Wrapper to C2Allocator#priorGraphicAllocation
+ */
+ c2_status_t priorGraphicAllocation(
+ const C2Handle *handle,
+ std::shared_ptr<C2GraphicAllocation> *c2Allocation);
+
+private:
+ static constexpr int kMaxIntParams = 5; // large enough number;
+
+ enum AllocType : uint8_t {
+ ALLOC_NONE = 0,
+
+ ALLOC_LINEAR,
+ ALLOC_GRAPHIC,
+ };
+
+ union AllocParams {
+ struct {
+ AllocType allocType;
+ C2MemoryUsage usage;
+ uint32_t params[kMaxIntParams];
+ } data;
+ uint8_t array[0];
+
+ AllocParams() : data{ALLOC_NONE, {0, 0}, {0}} {}
+ AllocParams(C2MemoryUsage usage, uint32_t capacity)
+ : data{ALLOC_LINEAR, usage, {[0] = capacity}} {}
+ AllocParams(
+ C2MemoryUsage usage,
+ uint32_t width, uint32_t height, uint32_t format)
+ : data{ALLOC_GRAPHIC, usage, {width, height, format}} {}
+ };
+
+ const std::shared_ptr<C2Allocator> mAllocator;
+};
+
+struct LinearAllocationDtor {
+ LinearAllocationDtor(const std::shared_ptr<C2LinearAllocation> &alloc)
+ : mAllocation(alloc) {}
+
+ void operator()(BufferPoolAllocation *poolAlloc) { delete poolAlloc; }
+
+ const std::shared_ptr<C2LinearAllocation> mAllocation;
+};
+
+ResultStatus _C2BufferPoolAllocator::allocate(
+ const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<BufferPoolAllocation> *alloc) {
+ AllocParams c2Params;
+ memcpy(&c2Params, params.data(), std::min(sizeof(AllocParams), params.size()));
+ std::shared_ptr<C2LinearAllocation> c2Linear;
+ c2_status_t status = C2_BAD_VALUE;
+ switch(c2Params.data.allocType) {
+ case ALLOC_NONE:
+ break;
+ case ALLOC_LINEAR:
+ status = mAllocator->newLinearAllocation(
+ c2Params.data.params[0], c2Params.data.usage, &c2Linear);
+ if (status == C2_OK && c2Linear) {
+ BufferPoolAllocation *ptr = new BufferPoolAllocation(c2Linear->handle());
+ if (ptr) {
+ *alloc = std::shared_ptr<BufferPoolAllocation>(
+ ptr, LinearAllocationDtor(c2Linear));
+ if (*alloc) {
+ return ResultStatus::OK;
+ }
+ delete ptr;
+ }
+ return ResultStatus::NO_MEMORY;
+ }
+ break;
+ case ALLOC_GRAPHIC:
+ // TODO
+ break;
+ default:
+ break;
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+bool _C2BufferPoolAllocator::compatible(
+ const std::vector<uint8_t> &newParams,
+ const std::vector<uint8_t> &oldParams) {
+ size_t newSize = newParams.size();
+ size_t oldSize = oldParams.size();
+
+ // TODO: support not exact matching. e.g) newCapacity < oldCapacity
+ if (newSize == oldSize) {
+ for (size_t i = 0; i < newSize; ++i) {
+ if (newParams[i] != oldParams[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+void _C2BufferPoolAllocator::getLinearParams(
+ uint32_t capacity, C2MemoryUsage usage, std::vector<uint8_t> *params) {
+ AllocParams c2Params(usage, capacity);
+ params->assign(c2Params.array, c2Params.array + sizeof(AllocParams));
+}
+
+void _C2BufferPoolAllocator::getGraphicParams(
+ uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage,
+ std::vector<uint8_t> *params) {
+ AllocParams c2Params(usage, width, height, format);
+ params->assign(c2Params.array, c2Params.array + sizeof(AllocParams));
+}
+
+c2_status_t _C2BufferPoolAllocator::priorLinearAllocation(
+ const C2Handle *handle,
+ std::shared_ptr<C2LinearAllocation> *c2Allocation) {
+ return mAllocator->priorLinearAllocation(handle, c2Allocation);
+}
+
+c2_status_t _C2BufferPoolAllocator::priorGraphicAllocation(
+ const C2Handle *handle,
+ std::shared_ptr<C2GraphicAllocation> *c2Allocation) {
+ return mAllocator->priorGraphicAllocation(handle, c2Allocation);
+}
+
+class C2PooledBlockPool::Impl {
+public:
+ Impl(const std::shared_ptr<C2Allocator> &allocator)
+ : mInit(C2_OK),
+ mBufferPoolManager(ClientManager::getInstance()),
+ mAllocator(std::make_shared<_C2BufferPoolAllocator>(allocator)) {
+ if (mAllocator && mBufferPoolManager) {
+ if (mBufferPoolManager->create(
+ mAllocator, &mConnectionId) == ResultStatus::OK) {
+ return;
+ }
+ }
+ mInit = C2_NO_INIT;
+ }
+
+ ~Impl() {
+ if (mInit == C2_OK) {
+ mBufferPoolManager->close(mConnectionId);
+ }
+ }
+
+ c2_status_t fetchLinearBlock(
+ uint32_t capacity, C2MemoryUsage usage,
+ std::shared_ptr<C2LinearBlock> *block /* nonnull */) {
+ block->reset();
+ if (mInit != C2_OK) {
+ return mInit;
+ }
+ std::vector<uint8_t> params;
+ mAllocator->getLinearParams(capacity, usage, ¶ms);
+ std::shared_ptr<_C2BlockPoolData> poolData;
+ ResultStatus status = mBufferPoolManager->allocate(
+ mConnectionId, params, &poolData);
+ if (status == ResultStatus::OK) {
+ std::shared_ptr<C2LinearAllocation> alloc;
+ c2_status_t err = mAllocator->priorLinearAllocation(
+ poolData->mHandle, &alloc);
+ if (err == C2_OK && poolData && alloc) {
+ *block = _C2BlockFactory::CreateLinearBlock(
+ alloc, poolData, 0, capacity);
+ if (*block) {
+ return C2_OK;
+ }
+ }
+ return C2_NO_MEMORY;
+ }
+ if (status == ResultStatus::NO_MEMORY) {
+ return C2_NO_MEMORY;
+ }
+ return C2_CORRUPTED;
+ }
+
+private:
+ c2_status_t mInit;
+ const android::sp<ClientManager> mBufferPoolManager;
+ ConnectionId mConnectionId; // locally
+ const std::shared_ptr<_C2BufferPoolAllocator> mAllocator;
+};
+
+C2PooledBlockPool::C2PooledBlockPool(
+ const std::shared_ptr<C2Allocator> &allocator, const local_id_t localId)
+ : mAllocator(allocator), mLocalId(localId), mImpl(new Impl(allocator)) {}
+
+C2PooledBlockPool::~C2PooledBlockPool() {
+}
+
+c2_status_t C2PooledBlockPool::fetchLinearBlock(
+ uint32_t capacity,
+ C2MemoryUsage usage,
+ std::shared_ptr<C2LinearBlock> *block /* nonnull */) {
+ if (mImpl) {
+ return mImpl->fetchLinearBlock(capacity, usage, block);
+ }
+ return C2_CORRUPTED;
+}
+
/* ========================================== 2D BLOCK ========================================= */
/**
diff --git a/media/libstagefright/codec2/vndk/C2Store.cpp b/media/libstagefright/codec2/vndk/C2Store.cpp
index 216a897..6f752ae 100644
--- a/media/libstagefright/codec2/vndk/C2Store.cpp
+++ b/media/libstagefright/codec2/vndk/C2Store.cpp
@@ -152,6 +152,39 @@
return res;
}
+c2_status_t CreateCodec2BlockPool(
+ C2PlatformAllocatorStore::id_t allocatorId,
+ std::shared_ptr<const C2Component> component,
+ std::shared_ptr<C2BlockPool> *pool) {
+ pool->reset();
+ if (!component) {
+ return C2_BAD_VALUE;
+ }
+ // TODO: support caching block pool along with GetCodec2BlockPool.
+ static std::atomic_int sBlockPoolId(C2BlockPool::PLATFORM_START);
+ std::shared_ptr<C2AllocatorStore> allocatorStore = GetCodec2PlatformAllocatorStore();
+ std::shared_ptr<C2Allocator> allocator;
+ c2_status_t res = C2_NOT_FOUND;
+
+ switch (allocatorId) {
+ case C2PlatformAllocatorStore::ION:
+ res = allocatorStore->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &allocator);
+ if (res == C2_OK) {
+ *pool = std::make_shared<C2PooledBlockPool>(allocator, sBlockPoolId++);
+ if (!*pool) {
+ res = C2_NO_MEMORY;
+ }
+ }
+ break;
+ case C2PlatformAllocatorStore::GRALLOC:
+ // TODO: support gralloc
+ break;
+ default:
+ break;
+ }
+ return res;
+}
+
class C2PlatformComponentStore : public C2ComponentStore {
public:
virtual std::vector<std::shared_ptr<const C2Component::Traits>> listComponents() override;
diff --git a/media/libstagefright/codec2/vndk/include/C2BufferPriv.h b/media/libstagefright/codec2/vndk/include/C2BufferPriv.h
index 977cf7b..211c13a 100644
--- a/media/libstagefright/codec2/vndk/include/C2BufferPriv.h
+++ b/media/libstagefright/codec2/vndk/include/C2BufferPriv.h
@@ -71,4 +71,32 @@
const std::shared_ptr<C2Allocator> mAllocator;
};
+class C2PooledBlockPool : public C2BlockPool {
+public:
+ C2PooledBlockPool(const std::shared_ptr<C2Allocator> &allocator, const local_id_t localId);
+
+ virtual ~C2PooledBlockPool() override;
+
+ virtual C2Allocator::id_t getAllocatorId() const override {
+ return mAllocator->getId();
+ }
+
+ virtual local_id_t getLocalId() const override {
+ return mLocalId;
+ }
+
+ virtual c2_status_t fetchLinearBlock(
+ uint32_t capacity,
+ C2MemoryUsage usage,
+ std::shared_ptr<C2LinearBlock> *block /* nonnull */) override;
+
+ // TODO: fetchGraphicBlock, fetchCircularBlock
+private:
+ const std::shared_ptr<C2Allocator> mAllocator;
+ const local_id_t mLocalId;
+
+ class Impl;
+ std::unique_ptr<Impl> mImpl;
+};
+
#endif // STAGEFRIGHT_CODEC2_BUFFER_PRIV_H_
diff --git a/media/libstagefright/codec2/vndk/include/C2PlatformSupport.h b/media/libstagefright/codec2/vndk/include/C2PlatformSupport.h
index afa51ee..211ee0c 100644
--- a/media/libstagefright/codec2/vndk/include/C2PlatformSupport.h
+++ b/media/libstagefright/codec2/vndk/include/C2PlatformSupport.h
@@ -82,6 +82,23 @@
std::shared_ptr<C2BlockPool> *pool);
/**
+ * Creates a block pool.
+ * \param allocatorId the allocator ID which is used to allocate blocks
+ * \param component the component using the block pool (must be non-null)
+ * \param pool pointer to where the created block pool shall be store on success.
+ * nullptr will be stored here on failure
+ *
+ * \retval C2_OK the operation was successful
+ * \retval C2_BAD_VALUE the component is null
+ * \retval C2_NOT_FOUND if the allocator does not exist
+ * \retval C2_NO_MEMORY not enough memory to create a block pool
+ */
+c2_status_t CreateCodec2BlockPool(
+ C2PlatformAllocatorStore::id_t allocatorId,
+ std::shared_ptr<const C2Component> component,
+ std::shared_ptr<C2BlockPool> *pool);
+
+/**
* Returns the platform component store.
* \retval nullptr if the platform component store could not be obtained
*/
diff --git a/media/libstagefright/codecs/cmds/codec2.cpp b/media/libstagefright/codecs/cmds/codec2.cpp
index 0ec1a77..5558bcf 100644
--- a/media/libstagefright/codecs/cmds/codec2.cpp
+++ b/media/libstagefright/codecs/cmds/codec2.cpp
@@ -93,6 +93,8 @@
sp<IProducerListener> mProducerListener;
+ std::atomic_int mLinearPoolId;
+
std::shared_ptr<C2Allocator> mAllocIon;
std::shared_ptr<C2BlockPool> mLinearPool;
@@ -137,12 +139,13 @@
SimplePlayer::SimplePlayer()
: mListener(new Listener(this)),
mProducerListener(new DummyProducerListener),
+ mLinearPoolId(C2BlockPool::PLATFORM_START),
mComposerClient(new SurfaceComposerClient) {
CHECK_EQ(mComposerClient->initCheck(), (status_t)OK);
std::shared_ptr<C2AllocatorStore> store = GetCodec2PlatformAllocatorStore();
CHECK_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mAllocIon), C2_OK);
- mLinearPool = std::make_shared<C2BasicLinearBlockPool>(mAllocIon);
+ mLinearPool = std::make_shared<C2PooledBlockPool>(mAllocIon, mLinearPoolId++);
mControl = mComposerClient->createSurface(
String8("A Surface"),
@@ -284,7 +287,7 @@
});
long numFrames = 0;
- mLinearPool.reset(new C2BasicLinearBlockPool(mAllocIon));
+ mLinearPool.reset(new C2PooledBlockPool(mAllocIon, mLinearPoolId++));
for (;;) {
size_t size = 0u;
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 8488d10..ece0692 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -218,7 +218,7 @@
}
sp<ABuffer> mpegUserData;
- if (buffer->meta()->findBuffer("mpegUserData", &mpegUserData) && mpegUserData != NULL) {
+ if (buffer->meta()->findBuffer("mpeg-user-data", &mpegUserData) && mpegUserData != NULL) {
bufmeta.setData(
kKeyMpegUserData, 0, mpegUserData->data(), mpegUserData->size());
}
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 9e18fd3..0b7bd26 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -1475,7 +1475,7 @@
mpegUserData->data() + i * sizeof(size_t),
&userDataPositions[i], sizeof(size_t));
}
- accessUnit->meta()->setBuffer("mpegUserData", mpegUserData);
+ accessUnit->meta()->setBuffer("mpeg-user-data", mpegUserData);
}
}
diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp
index ea43d2e..ac837a3 100644
--- a/media/ndk/NdkMediaExtractor.cpp
+++ b/media/ndk/NdkMediaExtractor.cpp
@@ -43,7 +43,12 @@
static media_status_t translate_error(status_t err) {
if (err == OK) {
return AMEDIA_OK;
+ } else if (err == ERROR_END_OF_STREAM) {
+ return AMEDIA_ERROR_END_OF_STREAM;
+ } else if (err == ERROR_IO) {
+ return AMEDIA_ERROR_IO;
}
+
ALOGE("sf error code: %d", err);
return AMEDIA_ERROR_UNKNOWN;
}
@@ -411,5 +416,64 @@
return -1;
}
+EXPORT
+media_status_t AMediaExtractor_getSampleFormat(AMediaExtractor *ex, AMediaFormat *fmt) {
+ if (fmt == NULL) {
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ sp<MetaData> sampleMeta;
+ status_t err = ex->mImpl->getSampleMeta(&sampleMeta);
+ if (err != OK) {
+ return translate_error(err);
+ }
+
+ sp<AMessage> meta;
+ AMediaFormat_getFormat(fmt, &meta);
+ meta->clear();
+
+ int32_t layerId;
+ if (sampleMeta->findInt32(kKeyTemporalLayerId, &layerId)) {
+ meta->setInt32(AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID, layerId);
+ }
+
+ size_t trackIndex;
+ err = ex->mImpl->getSampleTrackIndex(&trackIndex);
+ if (err == OK) {
+ meta->setInt32(AMEDIAFORMAT_KEY_TRACK_INDEX, trackIndex);
+ sp<AMessage> trackFormat;
+ AString mime;
+ err = ex->mImpl->getTrackFormat(trackIndex, &trackFormat);
+ if (err == OK
+ && trackFormat != NULL
+ && trackFormat->findString(AMEDIAFORMAT_KEY_MIME, &mime)) {
+ meta->setString(AMEDIAFORMAT_KEY_MIME, mime);
+ }
+ }
+
+ int64_t durationUs;
+ if (sampleMeta->findInt64(kKeyDuration, &durationUs)) {
+ meta->setInt64(AMEDIAFORMAT_KEY_DURATION, durationUs);
+ }
+
+ uint32_t dataType; // unused
+ const void *seiData;
+ size_t seiLength;
+ if (sampleMeta->findData(kKeySEI, &dataType, &seiData, &seiLength)) {
+ sp<ABuffer> sei = ABuffer::CreateAsCopy(seiData, seiLength);;
+ meta->setBuffer(AMEDIAFORMAT_KEY_SEI, sei);
+ }
+
+ const void *mpegUserDataPointer;
+ size_t mpegUserDataLength;
+ if (sampleMeta->findData(
+ kKeyMpegUserData, &dataType, &mpegUserDataPointer, &mpegUserDataLength)) {
+ sp<ABuffer> mpegUserData = ABuffer::CreateAsCopy(mpegUserDataPointer, mpegUserDataLength);
+ meta->setBuffer(AMEDIAFORMAT_KEY_MPEG_USER_DATA, mpegUserData);
+ }
+
+ return AMEDIA_OK;
+}
+
} // extern "C"
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index b86876b..9bf450c 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -286,7 +286,13 @@
EXPORT const char* AMEDIAFORMAT_KEY_COLOR_STANDARD = "color-standard";
EXPORT const char* AMEDIAFORMAT_KEY_COLOR_TRANSFER = "color-transfer";
EXPORT const char* AMEDIAFORMAT_KEY_COMPLEXITY = "complexity";
+EXPORT const char* AMEDIAFORMAT_KEY_CSD = "csd";
+EXPORT const char* AMEDIAFORMAT_KEY_CSD_0 = "csd-0";
+EXPORT const char* AMEDIAFORMAT_KEY_CSD_1 = "csd-1";
+EXPORT const char* AMEDIAFORMAT_KEY_CSD_2 = "csd-2";
EXPORT const char* AMEDIAFORMAT_KEY_DISPLAY_CROP = "crop";
+EXPORT const char* AMEDIAFORMAT_KEY_DISPLAY_HEIGHT = "display-height";
+EXPORT const char* AMEDIAFORMAT_KEY_DISPLAY_WIDTH = "display-width";
EXPORT const char* AMEDIAFORMAT_KEY_DURATION = "durationUs";
EXPORT const char* AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
EXPORT const char* AMEDIAFORMAT_KEY_FRAME_RATE = "frame-rate";
@@ -309,6 +315,7 @@
EXPORT const char* AMEDIAFORMAT_KEY_MAX_INPUT_SIZE = "max-input-size";
EXPORT const char* AMEDIAFORMAT_KEY_MAX_WIDTH = "max-width";
EXPORT const char* AMEDIAFORMAT_KEY_MIME = "mime";
+EXPORT const char* AMEDIAFORMAT_KEY_MPEG_USER_DATA = "mpeg-user-data";
EXPORT const char* AMEDIAFORMAT_KEY_OPERATING_RATE = "operating-rate";
EXPORT const char* AMEDIAFORMAT_KEY_PCM_ENCODING = "pcm-encoding";
EXPORT const char* AMEDIAFORMAT_KEY_PRIORITY = "priority";
@@ -317,10 +324,14 @@
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_RATE = "sample-rate";
+EXPORT const char* AMEDIAFORMAT_KEY_SEI = "sei";
EXPORT const char* AMEDIAFORMAT_KEY_SLICE_HEIGHT = "slice-height";
EXPORT const char* AMEDIAFORMAT_KEY_STRIDE = "stride";
+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_TIME_US = "timeUs";
EXPORT const char* AMEDIAFORMAT_KEY_TRACK_ID = "track-id";
+EXPORT const char* AMEDIAFORMAT_KEY_TRACK_INDEX = "track-index";
EXPORT const char* AMEDIAFORMAT_KEY_WIDTH = "width";
diff --git a/media/ndk/include/media/NdkMediaError.h b/media/ndk/include/media/NdkMediaError.h
index e48fcbe..13aacc9 100644
--- a/media/ndk/include/media/NdkMediaError.h
+++ b/media/ndk/include/media/NdkMediaError.h
@@ -53,6 +53,8 @@
AMEDIA_ERROR_INVALID_OBJECT = AMEDIA_ERROR_BASE - 3,
AMEDIA_ERROR_INVALID_PARAMETER = AMEDIA_ERROR_BASE - 4,
AMEDIA_ERROR_INVALID_OPERATION = AMEDIA_ERROR_BASE - 5,
+ AMEDIA_ERROR_END_OF_STREAM = AMEDIA_ERROR_BASE - 6,
+ AMEDIA_ERROR_IO = AMEDIA_ERROR_BASE - 7,
AMEDIA_DRM_ERROR_BASE = -20000,
AMEDIA_DRM_NOT_PROVISIONED = AMEDIA_DRM_ERROR_BASE - 1,
diff --git a/media/ndk/include/media/NdkMediaExtractor.h b/media/ndk/include/media/NdkMediaExtractor.h
index 3c9e23d..f7b9cfd 100644
--- a/media/ndk/include/media/NdkMediaExtractor.h
+++ b/media/ndk/include/media/NdkMediaExtractor.h
@@ -203,6 +203,17 @@
*/
int64_t AMediaExtractor_getCachedDuration(AMediaExtractor *);
+/**
+ * Read the current sample's metadata format into |fmt|. Examples of sample metadata are
+ * SEI (supplemental enhancement information) and MPEG user data, both of which can embed
+ * closed-caption data.
+ *
+ * Returns AMEDIA_OK on success or AMEDIA_ERROR_* to indicate failure reason.
+ * Existing key-value pairs in |fmt| would be removed if this API returns AMEDIA_OK.
+ * The contents of |fmt| is undefined if this API returns AMEDIA_ERROR_*.
+ */
+media_status_t AMediaExtractor_getSampleFormat(AMediaExtractor *ex, AMediaFormat *fmt);
+
#endif /* __ANDROID_API__ >= 28 */
#endif /* __ANDROID_API__ >= 21 */
diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h
index b6489c7..1da9197 100644
--- a/media/ndk/include/media/NdkMediaFormat.h
+++ b/media/ndk/include/media/NdkMediaFormat.h
@@ -100,7 +100,13 @@
extern const char* AMEDIAFORMAT_KEY_COLOR_STANDARD;
extern const char* AMEDIAFORMAT_KEY_COLOR_TRANSFER;
extern const char* AMEDIAFORMAT_KEY_COMPLEXITY;
+extern const char* AMEDIAFORMAT_KEY_CSD;
+extern const char* AMEDIAFORMAT_KEY_CSD_0;
+extern const char* AMEDIAFORMAT_KEY_CSD_1;
+extern const char* AMEDIAFORMAT_KEY_CSD_2;
extern const char* AMEDIAFORMAT_KEY_DISPLAY_CROP;
+extern const char* AMEDIAFORMAT_KEY_DISPLAY_HEIGHT;
+extern const char* AMEDIAFORMAT_KEY_DISPLAY_WIDTH;
extern const char* AMEDIAFORMAT_KEY_DURATION;
extern const char* AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL;
extern const char* AMEDIAFORMAT_KEY_FRAME_RATE;
@@ -123,6 +129,7 @@
extern const char* AMEDIAFORMAT_KEY_MAX_INPUT_SIZE;
extern const char* AMEDIAFORMAT_KEY_MAX_WIDTH;
extern const char* AMEDIAFORMAT_KEY_MIME;
+extern const char* AMEDIAFORMAT_KEY_MPEG_USER_DATA;
extern const char* AMEDIAFORMAT_KEY_OPERATING_RATE;
extern const char* AMEDIAFORMAT_KEY_PCM_ENCODING;
extern const char* AMEDIAFORMAT_KEY_PRIORITY;
@@ -131,10 +138,14 @@
extern const char* AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER;
extern const char* AMEDIAFORMAT_KEY_ROTATION;
extern const char* AMEDIAFORMAT_KEY_SAMPLE_RATE;
+extern const char* AMEDIAFORMAT_KEY_SEI;
extern const char* AMEDIAFORMAT_KEY_SLICE_HEIGHT;
extern const char* AMEDIAFORMAT_KEY_STRIDE;
+extern const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID;
extern const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYERING;
+extern const char* AMEDIAFORMAT_KEY_TIME_US;
extern const char* AMEDIAFORMAT_KEY_TRACK_ID;
+extern const char* AMEDIAFORMAT_KEY_TRACK_INDEX;
extern const char* AMEDIAFORMAT_KEY_WIDTH;
#endif /* __ANDROID_API__ >= 21 */
diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt
index 17c1a0d..d8c41d2 100644
--- a/media/ndk/libmediandk.map.txt
+++ b/media/ndk/libmediandk.map.txt
@@ -159,6 +159,7 @@
AMediaExtractor_getPsshInfo;
AMediaExtractor_getSampleCryptoInfo;
AMediaExtractor_getSampleFlags;
+ AMediaExtractor_getSampleFormat; # introduced=28
AMediaExtractor_getSampleSize; # introduced=28
AMediaExtractor_getSampleTime;
AMediaExtractor_getSampleTrackIndex;
diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
index 9ee4928..3d812f8 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
@@ -41,6 +41,7 @@
void onDisconnected();
void onCustomLayoutChanged(in List<Bundle> commandButtonlist);
+ void onAllowedCommandsChanged(in Bundle commands);
void onCustomCommand(in Bundle command, in Bundle args, in ResultReceiver receiver);
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index 4a4f250..dd23148 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -351,7 +351,7 @@
@Override
public void setVolumeTo_impl(int value, int flags) {
// TODO(hdmoon): sanity check
- final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_SET_VOLUME);
+ final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAYBACK_SET_VOLUME);
if (binder != null) {
try {
binder.setVolumeTo(mSessionCallbackStub, value, flags);
@@ -366,7 +366,7 @@
@Override
public void adjustVolume_impl(int direction, int flags) {
// TODO(hdmoon): sanity check
- final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_SET_VOLUME);
+ final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAYBACK_SET_VOLUME);
if (binder != null) {
try {
binder.adjustVolume(mSessionCallbackStub, direction, flags);
@@ -561,7 +561,7 @@
Bundle args = new Bundle();
args.putInt(MediaSession2Stub.ARGUMENT_KEY_ITEM_INDEX, item);
sendTransportControlCommand(
- MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_TO_PLAYLIST_ITEM, args);
+ MediaSession2.COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM, args);
*/
}
@@ -776,6 +776,12 @@
});
}
+ void onAllowedCommandsChanged(final CommandGroup commands) {
+ mCallbackExecutor.execute(() -> {
+ mCallback.onAllowedCommandsChanged(mInstance, commands);
+ });
+ }
+
void onCustomLayoutChanged(final List<CommandButton> layout) {
mCallbackExecutor.execute(() -> {
mCallback.onCustomLayoutChanged(mInstance, layout);
diff --git a/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java b/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
index 252512c..039ff8f 100644
--- a/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
@@ -44,14 +44,11 @@
private DataSourceDesc mDataSourceDesc;
// From the public API
- public MediaItem2Impl(Context context, String mediaId,
- DataSourceDesc dsd, MediaMetadata2 metadata, @Flags int flags) {
+ public MediaItem2Impl(@NonNull Context context, @NonNull String mediaId,
+ @Nullable DataSourceDesc dsd, @Nullable MediaMetadata2 metadata, @Flags int flags) {
if (mediaId == null) {
throw new IllegalArgumentException("mediaId shouldn't be null");
}
- if (dsd == null) {
- throw new IllegalArgumentException("dsd shouldn't be null");
- }
if (metadata != null && !TextUtils.equals(mediaId, metadata.getMediaId())) {
throw new IllegalArgumentException("metadata's id should be matched with the mediaid");
}
@@ -186,10 +183,7 @@
}
@Override
- public Builder setDataSourceDesc_impl(@NonNull DataSourceDesc dataSourceDesc) {
- if (dataSourceDesc == null) {
- throw new IllegalArgumentException("dataSourceDesc shouldn't be null");
- }
+ public Builder setDataSourceDesc_impl(@Nullable DataSourceDesc dataSourceDesc) {
mDataSourceDesc = dataSourceDesc;
return mInstance;
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
index a21fda6..6a6a385 100644
--- a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
@@ -24,6 +24,7 @@
import android.media.MediaLibraryService2.MediaLibrarySession.Builder;
import android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
import android.media.MediaPlayerBase;
+import android.media.MediaPlaylistAgent;
import android.media.MediaSession2;
import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSessionService2;
@@ -67,10 +68,12 @@
public static class MediaLibrarySessionImpl extends MediaSession2Impl
implements MediaLibrarySessionProvider {
public MediaLibrarySessionImpl(Context context,
- MediaPlayerBase player, String id, VolumeProvider2 volumeProvider,
+ MediaPlayerBase player, String id, MediaPlaylistAgent mplc,
+ VolumeProvider2 volumeProvider,
PendingIntent sessionActivity, Executor callbackExecutor,
MediaLibrarySessionCallback callback) {
- super(context, player, id, volumeProvider, sessionActivity, callbackExecutor, callback);
+ super(context, player, id, mplc, volumeProvider, sessionActivity, callbackExecutor,
+ callback);
// Don't put any extra initialization here. Here's the reason.
// System service will recognize this session inside of the super constructor and would
// connect to this session assuming that initialization is finished. However, if any
@@ -138,8 +141,8 @@
@Override
public MediaLibrarySession build_impl() {
- return new MediaLibrarySessionImpl(mContext, mPlayer, mId, mVolumeProvider,
- mSessionActivity, mCallbackExecutor, mCallback).getInstance();
+ return new MediaLibrarySessionImpl(mContext, mPlayer, mId, mMplc,
+ mVolumeProvider, mSessionActivity, mCallbackExecutor, mCallback).getInstance();
}
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java b/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
index b8e651e..451368f 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
@@ -215,6 +215,27 @@
}
@Override
+ public void onAllowedCommandsChanged(Bundle commandsBundle) {
+ final MediaController2Impl controller;
+ try {
+ controller = getController();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ if (controller == null) {
+ // TODO(jaewan): Revisit here. Could be a bug
+ return;
+ }
+ CommandGroup commands = CommandGroup.fromBundle(controller.getContext(), commandsBundle);
+ if (commands == null) {
+ Log.w(TAG, "onAllowedCommandsChanged(): Ignoring null commands");
+ return;
+ }
+ controller.onAllowedCommandsChanged(commands);
+ }
+
+ @Override
public void onCustomCommand(Bundle commandBundle, Bundle args, ResultReceiver receiver) {
final MediaController2Impl controller;
try {
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index c407e5a..1f24d4e 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -21,14 +21,12 @@
import static android.media.SessionToken2.TYPE_SESSION;
import static android.media.SessionToken2.TYPE_SESSION_SERVICE;
-import android.Manifest.permission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.media.AudioAttributes;
import android.media.AudioManager;
@@ -39,7 +37,7 @@
import android.media.MediaMetadata2;
import android.media.MediaPlayerBase;
import android.media.MediaPlayerBase.PlayerEventCallback;
-import android.media.MediaPlaylistController;
+import android.media.MediaPlaylistAgent;
import android.media.MediaSession2;
import android.media.MediaSession2.Builder;
import android.media.MediaSession2.Command;
@@ -64,7 +62,6 @@
import android.support.annotation.GuardedBy;
import android.text.TextUtils;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.Log;
import java.lang.ref.WeakReference;
@@ -108,6 +105,8 @@
@GuardedBy("mLock")
private MediaPlayerBase mPlayer;
@GuardedBy("mLock")
+ private MediaPlaylistAgent mMplc;
+ @GuardedBy("mLock")
private VolumeProvider2 mVolumeProvider;
@GuardedBy("mLock")
private PlaybackInfo mPlaybackInfo;
@@ -116,17 +115,18 @@
/**
* Can be only called by the {@link Builder#build()}.
- *
* @param context
* @param player
* @param id
+ * @param mplc
* @param volumeProvider
* @param sessionActivity
* @param callbackExecutor
* @param callback
*/
public MediaSession2Impl(Context context, MediaPlayerBase player, String id,
- VolumeProvider2 volumeProvider, PendingIntent sessionActivity,
+ MediaPlaylistAgent mplc, VolumeProvider2 volumeProvider,
+ PendingIntent sessionActivity,
Executor callbackExecutor, SessionCallback callback) {
// TODO(jaewan): Keep other params.
mInstance = createInstance();
@@ -140,6 +140,7 @@
mSessionActivity = sessionActivity;
mSessionStub = new MediaSession2Stub(this);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ mMplc = mplc;
// Infer type from the id and package name.
String libraryService = getServiceName(context, MediaLibraryService2.SERVICE_INTERFACE, id);
@@ -203,13 +204,13 @@
}
@Override
- public void updatePlayer_impl(MediaPlayerBase player, MediaPlaylistController mpcl,
+ public void updatePlayer_impl(MediaPlayerBase player, MediaPlaylistAgent mplc,
VolumeProvider2 volumeProvider) throws IllegalArgumentException {
ensureCallingThread();
if (player == null) {
throw new IllegalArgumentException("player shouldn't be null");
}
- // TODO(jaewan): Handle mpcl
+ mMplc = mplc;
setPlayer(player, volumeProvider);
}
@@ -428,7 +429,13 @@
@Override
public void setAllowedCommands_impl(ControllerInfo controller, CommandGroup commands) {
- // TODO(jaewan): Implement
+ if (controller == null) {
+ throw new IllegalArgumentException("controller shouldn't be null");
+ }
+ if (commands == null) {
+ throw new IllegalArgumentException("commands shouldn't be null");
+ }
+ mSessionStub.setAllowedCommands(controller, commands);
}
@Override
@@ -882,8 +889,7 @@
@Override
public void addAllPredefinedCommands_impl() {
- final int COMMAND_CODE_MAX = 22;
- for (int i = 1; i <= COMMAND_CODE_MAX; i++) {
+ for (int i = 1; i <= MediaSession2.COMMAND_CODE_MAX; i++) {
mCommands.add(new Command(mContext, i));
}
}
@@ -971,31 +977,13 @@
mInstance = instance;
mUid = uid;
mPackageName = packageName;
-
- // TODO(jaewan): Remove this workaround
- if ("com.android.server.media".equals(packageName)) {
- mIsTrusted = true;
- } else if (context.checkPermission(permission.MEDIA_CONTENT_CONTROL, pid, uid) ==
- PackageManager.PERMISSION_GRANTED) {
- mIsTrusted = true;
- } else {
- // TODO(jaewan): Also consider enabled notification listener.
- mIsTrusted = false;
- // System apps may bind across the user so uid can be differ.
- // Skip sanity check for the system app.
- try {
- int uidForPackage = context.getPackageManager().getPackageUid(packageName, 0);
- if (uid != uidForPackage) {
- throw new IllegalArgumentException("Illegal call from uid=" + uid +
- ", pkg=" + packageName + ". Expected uid" + uidForPackage);
- }
- } catch (NameNotFoundException e) {
- // Rethrow exception with different name because binder methods only accept
- // RemoteException.
- throw new IllegalArgumentException(e);
- }
- }
mControllerBinder = callback;
+ MediaSessionManager manager =
+ (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
+ // Ask server whether the controller is trusted.
+ // App cannot know this because apps cannot query enabled notification listener for
+ // another package, but system server can do.
+ mIsTrusted = manager.isTrusted(uid, packageName);
}
@Override
@@ -1287,6 +1275,7 @@
String mId;
Executor mCallbackExecutor;
C mCallback;
+ MediaPlaylistAgent mMplc;
VolumeProvider2 mVolumeProvider;
PendingIntent mSessionActivity;
@@ -1307,17 +1296,25 @@
mId = "";
}
- public void setPlayer_impl(MediaPlayerBase player, MediaPlaylistController mplc,
- VolumeProvider2 volumeProvider) {
- // TODO: Use MediaPlaylistController
+ public void setPlayer_impl(MediaPlayerBase player) {
if (player == null) {
throw new IllegalArgumentException("player shouldn't be null");
}
mPlayer = player;
- mVolumeProvider = volumeProvider;
+ }
+
+ @Override
+ public void setPlaylistController_impl(MediaPlaylistAgent mplc) {
+ if (mplc == null) {
+ throw new IllegalArgumentException("mplc shouldn't be null");
+ }
+ mMplc = mplc;
}
public void setVolumeProvider_impl(VolumeProvider2 volumeProvider) {
+ if (volumeProvider == null) {
+ throw new IllegalArgumentException("volumeProvider shouldn't be null");
+ }
mVolumeProvider = volumeProvider;
}
@@ -1360,8 +1357,8 @@
mCallback = new SessionCallback(mContext) {};
}
- return new MediaSession2Impl(mContext, mPlayer, mId, mVolumeProvider,
- mSessionActivity, mCallbackExecutor, mCallback).getInstance();
+ return new MediaSession2Impl(mContext, mPlayer, mId, mMplc,
+ mVolumeProvider, mSessionActivity, mCallbackExecutor, mCallback).getInstance();
}
}
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index 785248c..d4164f6 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -267,7 +267,7 @@
final Bundle paramsBundle = (params != null) ? params.toBundle() : null;
final PendingIntent sessionActivity = session.getSessionActivity();
final List<MediaItem2> playlist =
- allowedCommands.hasCommand(MediaSession2.COMMAND_CODE_PLAYLIST_GET)
+ allowedCommands.hasCommand(MediaSession2.COMMAND_CODE_PLAYLIST_GET_LIST)
? session.getInstance().getPlaylist() : null;
final List<Bundle> playlistBundle;
if (playlist != null) {
@@ -332,23 +332,23 @@
throws RuntimeException {
final MediaSession2Impl session = getSession();
final ControllerInfo controller = getControllerIfAble(
- caller, MediaSession2.COMMAND_CODE_SET_VOLUME);
+ caller, MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME);
if (session == null || controller == null) {
return;
}
session.getCallbackExecutor().execute(() -> {
- if (getControllerIfAble(caller, MediaSession2.COMMAND_CODE_SET_VOLUME) == null) {
+ if (getControllerIfAble(caller, MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME) == null) {
return;
}
// TODO(jaewan): Sanity check.
Command command = new Command(
- session.getContext(), MediaSession2.COMMAND_CODE_SET_VOLUME);
+ session.getContext(), MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME);
boolean accepted = session.getCallback().onCommandRequest(session.getInstance(),
controller, command);
if (!accepted) {
// Don't run rejected command.
if (DEBUG) {
- Log.d(TAG, "Command " + MediaSession2.COMMAND_CODE_SET_VOLUME + " from "
+ Log.d(TAG, "Command " + MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME + " from "
+ controller + " was rejected by " + session);
}
return;
@@ -368,23 +368,23 @@
throws RuntimeException {
final MediaSession2Impl session = getSession();
final ControllerInfo controller = getControllerIfAble(
- caller, MediaSession2.COMMAND_CODE_SET_VOLUME);
+ caller, MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME);
if (session == null || controller == null) {
return;
}
session.getCallbackExecutor().execute(() -> {
- if (getControllerIfAble(caller, MediaSession2.COMMAND_CODE_SET_VOLUME) == null) {
+ if (getControllerIfAble(caller, MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME) == null) {
return;
}
// TODO(jaewan): Sanity check.
Command command = new Command(
- session.getContext(), MediaSession2.COMMAND_CODE_SET_VOLUME);
+ session.getContext(), MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME);
boolean accepted = session.getCallback().onCommandRequest(session.getInstance(),
controller, command);
if (!accepted) {
// Don't run rejected command.
if (DEBUG) {
- Log.d(TAG, "Command " + MediaSession2.COMMAND_CODE_SET_VOLUME + " from "
+ Log.d(TAG, "Command " + MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME + " from "
+ controller + " was rejected by " + session);
}
return;
@@ -435,10 +435,10 @@
session.getInstance().stop();
break;
case MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM:
- session.getInstance().skipToPrevious();
+ session.getInstance().skipToPreviousItem();
break;
case MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM:
- session.getInstance().skipToNext();
+ session.getInstance().skipToNextItem();
break;
case MediaSession2.COMMAND_CODE_PLAYBACK_PREPARE:
session.getInstance().prepare();
@@ -452,18 +452,21 @@
case MediaSession2.COMMAND_CODE_PLAYBACK_SEEK_TO:
session.getInstance().seekTo(args.getLong(ARGUMENT_KEY_POSITION));
break;
- case MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_TO_PLAYLIST_ITEM:
+ case MediaSession2.COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM:
// TODO(jaewan): Implement
/*
session.getInstance().skipToPlaylistItem(
args.getInt(ARGUMENT_KEY_ITEM_INDEX));
*/
break;
+ // TODO(jaewan): Remove (b/74116823)
+ /*
case MediaSession2.COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS:
session.getInstance().setPlaylistParams(
PlaylistParams.fromBundle(session.getContext(),
args.getBundle(ARGUMENT_KEY_PLAYLIST_PARAMS)));
break;
+ */
default:
// TODO(jaewan): Resend unknown (new) commands through the custom command.
}
@@ -910,7 +913,7 @@
final List<ControllerInfo> list = getControllers();
for (int i = 0; i < list.size(); i++) {
final IMediaSession2Callback controllerBinder = getControllerBinderIfAble(
- list.get(i), MediaSession2.COMMAND_CODE_PLAYLIST_GET);
+ list.get(i), MediaSession2.COMMAND_CODE_PLAYLIST_GET_LIST);
if (controllerBinder != null) {
try {
controllerBinder.onPlaylistChanged(bundleList);
@@ -955,6 +958,22 @@
}
}
+ public void setAllowedCommands(ControllerInfo controller, CommandGroup commands) {
+ synchronized (mLock) {
+ mAllowedCommandGroupMap.put(controller, commands);
+ }
+ final IMediaSession2Callback controllerBinder = getControllerBinderIfAble(controller);
+ if (controllerBinder == null) {
+ return;
+ }
+ try {
+ controllerBinder.onAllowedCommandsChanged(commands.toBundle());
+ } catch (RemoteException e) {
+ Log.w(TAG, "Controller is gone", e);
+ // TODO(jaewan): What to do when the controller is gone?
+ }
+ }
+
public void sendCustomCommand(ControllerInfo controller, Command command, Bundle args,
ResultReceiver receiver) {
if (receiver != null && controller == null) {
diff --git a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
index 1940953..69febc2 100644
--- a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
@@ -75,6 +75,13 @@
static final String KEY_STATE_IS_ADVERTISEMENT = "MediaTypeAdvertisement";
static final String EVENT_UPDATE_MEDIA_TYPE_STATUS = "UpdateMediaTypeStatus";
+ // String for receiving command to show subtitle from MediaSession.
+ static final String COMMAND_SHOW_SUBTITLE = "showSubtitle";
+ // String for receiving command to hide subtitle from MediaSession.
+ static final String COMMAND_HIDE_SUBTITLE = "hideSubtitle";
+ // TODO: remove once the implementation is revised
+ public static final String COMMAND_SET_FULLSCREEN = "setFullscreen";
+
private static final int MAX_PROGRESS = 1000;
private static final int DEFAULT_PROGRESS_UPDATE_TIME_MS = 1000;
private static final int REWIND_TIME_MS = 10000;
@@ -743,12 +750,12 @@
if (!mSubtitleIsEnabled) {
mSubtitleButton.setImageDrawable(
mResources.getDrawable(R.drawable.ic_media_subtitle_enabled, null));
- mController.sendCommand(MediaControlView2.COMMAND_SHOW_SUBTITLE, null, null);
+ mController.sendCommand(MediaControlView2Impl.COMMAND_SHOW_SUBTITLE, null, null);
mSubtitleIsEnabled = true;
} else {
mSubtitleButton.setImageDrawable(
mResources.getDrawable(R.drawable.ic_media_subtitle_disabled, null));
- mController.sendCommand(MediaControlView2.COMMAND_HIDE_SUBTITLE, null, null);
+ mController.sendCommand(MediaControlView2Impl.COMMAND_HIDE_SUBTITLE, null, null);
mSubtitleIsEnabled = false;
}
}
@@ -768,7 +775,7 @@
}
Bundle args = new Bundle();
args.putBoolean(ARGUMENT_KEY_FULLSCREEN, isEnteringFullScreen);
- mController.sendCommand(MediaControlView2.COMMAND_SET_FULLSCREEN, args, null);
+ mController.sendCommand(MediaControlView2Impl.COMMAND_SET_FULLSCREEN, args, null);
mIsFullScreen = isEnteringFullScreen;
}
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
index d23395c..c3ca057 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
@@ -1001,13 +1001,13 @@
mRouteSessionCallback.onCommand(command, args, receiver);
} else {
switch (command) {
- case MediaControlView2.COMMAND_SHOW_SUBTITLE:
+ case MediaControlView2Impl.COMMAND_SHOW_SUBTITLE:
mInstance.setSubtitleEnabled(true);
break;
- case MediaControlView2.COMMAND_HIDE_SUBTITLE:
+ case MediaControlView2Impl.COMMAND_HIDE_SUBTITLE:
mInstance.setSubtitleEnabled(false);
break;
- case MediaControlView2.COMMAND_SET_FULLSCREEN:
+ case MediaControlView2Impl.COMMAND_SET_FULLSCREEN:
if (mFullScreenRequestListener != null) {
mFullScreenRequestListener.onFullScreenRequest(
mInstance,
diff --git a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
index d1c7717..e58bd02 100644
--- a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
@@ -339,7 +339,9 @@
final CountDownLatch latch = new CountDownLatch(1);
final SessionCallbackProxy callbackProxy = new SessionCallbackProxy(mContext) {
@Override
- public void onSubscribe(ControllerInfo info, String parentId, Bundle extras) {
+ public void onSubscribe(@NonNull MediaLibrarySession session,
+ @NonNull ControllerInfo info, @NonNull String parentId,
+ @Nullable Bundle extras) {
if (Process.myUid() == info.getUid()) {
assertEquals(testParentId, parentId);
assertTrue(TestUtils.equals(testExtras, extras));
@@ -361,7 +363,8 @@
final CountDownLatch latch = new CountDownLatch(1);
final SessionCallbackProxy callbackProxy = new SessionCallbackProxy(mContext) {
@Override
- public void onUnsubscribe(ControllerInfo info, String parentId) {
+ public void onUnsubscribe(@NonNull MediaLibrarySession session,
+ @NonNull ControllerInfo info, @NonNull String parentId) {
if (Process.myUid() == info.getUid()) {
assertEquals(testParentId, parentId);
latch.countDown();
@@ -387,21 +390,29 @@
final CountDownLatch latch = new CountDownLatch(3);
final SessionCallbackProxy sessionCallbackProxy = new SessionCallbackProxy(mContext) {
@Override
- public CommandGroup onConnect(ControllerInfo controller) {
- final MockMediaLibraryService2 service = (MockMediaLibraryService2)
- TestServiceRegistry.getInstance().getServiceInstance();
- final MediaLibrarySession session = (MediaLibrarySession) service.getSession();
- // Shouldn't trigger onChildrenChanged() for the browser, because it hasn't
- // subscribed.
- session.notifyChildrenChanged(testParentId1, testChildrenCount, null);
- session.notifyChildrenChanged(controller, testParentId1, testChildrenCount, null);
- return super.onConnect(controller);
+ public CommandGroup onConnect(@NonNull MediaSession2 session,
+ @NonNull ControllerInfo controller) {
+ if (Process.myUid() == controller.getUid()) {
+ assertTrue(session instanceof MediaLibrarySession);
+ if (mSession != null) {
+ mSession.close();
+ }
+ mSession = session;
+ // Shouldn't trigger onChildrenChanged() for the browser, because it hasn't
+ // subscribed.
+ ((MediaLibrarySession) session).notifyChildrenChanged(
+ testParentId1, testChildrenCount, null);
+ ((MediaLibrarySession) session).notifyChildrenChanged(
+ controller, testParentId1, testChildrenCount, null);
+ }
+ return super.onConnect(session, controller);
}
@Override
- public void onSubscribe(ControllerInfo info, String parentId, Bundle extras) {
+ public void onSubscribe(@NonNull MediaLibrarySession session,
+ @NonNull ControllerInfo info, @NonNull String parentId,
+ @Nullable Bundle extras) {
if (Process.myUid() == info.getUid()) {
- final MediaLibrarySession session = (MediaLibrarySession) mSession;
session.notifyChildrenChanged(testParentId2, testChildrenCount, null);
session.notifyChildrenChanged(info, testParentId2, testChildrenCount,
testExtras);
@@ -435,12 +446,6 @@
final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
final MediaBrowser2 browser = (MediaBrowser2) createController(
token, true, controllerCallbackProxy);
- final MockMediaLibraryService2 service =
- (MockMediaLibraryService2) TestServiceRegistry.getInstance().getServiceInstance();
- if (mSession != null) {
- mSession.close();
- }
- mSession = service.getSession();
assertTrue(mSession instanceof MediaLibrarySession);
browser.subscribe(testParentId2, null);
// This ensures that onChildrenChanged() is only called for the expected reasons.
@@ -494,13 +499,17 @@
mCallbackProxy.onCustomCommand(command, args, receiver);
}
-
@Override
public void onCustomLayoutChanged(MediaController2 controller, List<CommandButton> layout) {
mCallbackProxy.onCustomLayoutChanged(layout);
}
@Override
+ public void onAllowedCommandsChanged(MediaController2 controller, CommandGroup commands) {
+ mCallbackProxy.onAllowedCommandsChanged(commands);
+ }
+
+ @Override
public void onGetLibraryRootDone(MediaBrowser2 browser, Bundle rootHints,
String rootMediaId, Bundle rootExtra) {
super.onGetLibraryRootDone(browser, rootHints, rootMediaId, rootExtra);
diff --git a/packages/MediaComponents/test/src/android/media/MediaController2Test.java b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
index e6ad098..bd5f031 100644
--- a/packages/MediaComponents/test/src/android/media/MediaController2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
@@ -32,6 +32,7 @@
import android.os.HandlerThread;
import android.os.Process;
import android.os.ResultReceiver;
+import android.support.annotation.NonNull;
import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -118,8 +119,8 @@
@Ignore
@Test
- public void testSkipToPrevious() throws InterruptedException {
- mController.skipToPrevious();
+ public void testSkipToPreviousItem() throws InterruptedException {
+ mController.skipToPreviousItem();
try {
assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
} catch (InterruptedException e) {
@@ -129,8 +130,8 @@
}
@Test
- public void testSkipToNext() throws InterruptedException {
- mController.skipToNext();
+ public void testSkipToNextItem() throws InterruptedException {
+ mController.skipToNextItem();
try {
assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
} catch (InterruptedException e) {
@@ -211,7 +212,7 @@
} catch (InterruptedException e) {
fail(e.getMessage());
}
- assertTrue(mPlayer.mSetCurrentPlaylistItemCalled);
+ assertTrue(mPlayer.mSetCurrentPlaylistCalled);
assertEquals(itemIndex, mPlayer.mCurrentItem);
}
*/
@@ -622,9 +623,9 @@
player.notifyPlaybackState(state);
controller.stop();
player.notifyPlaybackState(state);
- controller.skipToNext();
+ controller.skipToNextItem();
player.notifyPlaybackState(state);
- controller.skipToPrevious();
+ controller.skipToPreviousItem();
}
// This may hang if deadlock happens.
latch.countDown();
@@ -656,15 +657,6 @@
assertEquals(SessionToken2.TYPE_SESSION_SERVICE, token.getType());
}
- private void connectToService(SessionToken2 token) throws InterruptedException {
- if (mSession != null) {
- mSession.close();
- }
- mController = createController(token);
- mSession = TestServiceRegistry.getInstance().getServiceInstance().getSession();
- mPlayer = (MockPlayer) mSession.getPlayer();
- }
-
@Test
public void testConnectToService_sessionService() throws InterruptedException {
testConnectToService(MockMediaSessionService2.ID);
@@ -680,17 +672,24 @@
final CountDownLatch latch = new CountDownLatch(1);
final SessionCallbackProxy proxy = new SessionCallbackProxy(mContext) {
@Override
- public CommandGroup onConnect(ControllerInfo controller) {
+ public CommandGroup onConnect(@NonNull MediaSession2 session,
+ @NonNull ControllerInfo controller) {
if (Process.myUid() == controller.getUid()) {
+ if (mSession != null) {
+ mSession.close();
+ }
+ mSession = session;
+ mPlayer = (MockPlayer) session.getPlayer();
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertFalse(controller.isTrusted());
latch.countDown();
}
- return super.onConnect(controller);
+ return super.onConnect(session, controller);
}
};
TestServiceRegistry.getInstance().setSessionCallbackProxy(proxy);
- connectToService(TestUtils.getServiceToken(mContext, id));
+
+ mController = createController(TestUtils.getServiceToken(mContext, id));
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
// Test command from controller to session service
@@ -718,11 +717,14 @@
testControllerAfterSessionIsGone(mSession.getToken().getId());
}
+ // TODO(jaewan): Re-enable this test
@Ignore
@Test
public void testControllerAfterSessionIsGone_sessionService() throws InterruptedException {
+ /*
connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
testControllerAfterSessionIsGone(MockMediaSessionService2.ID);
+ */
}
@Test
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
index 8c1a749..afb191f 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
@@ -42,7 +42,6 @@
import android.support.annotation.NonNull;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import android.text.TextUtils;
import org.junit.After;
import org.junit.Before;
@@ -189,18 +188,18 @@
}
@Test
- public void testSkipToNext() throws Exception {
+ public void testSkipToNextItem() throws Exception {
sHandler.postAndSync(() -> {
- mSession.skipToNext();
+ mSession.skipToNextItem();
assertTrue(mPlayer.mSkipToNextCalled);
});
}
@Ignore
@Test
- public void testSkipToPrevious() throws Exception {
+ public void testSkipToPreviousItem() throws Exception {
sHandler.postAndSync(() -> {
- mSession.skipToPrevious();
+ mSession.skipToPreviousItem();
assertTrue(mPlayer.mSkipToPreviousCalled);
});
}
@@ -356,7 +355,7 @@
assertEquals(1, callback.commands.size());
assertEquals(MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE,
(long) callback.commands.get(0).getCommandCode());
- controller.skipToNext();
+ controller.skipToNextItem();
assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mSkipToNextCalled);
assertFalse(mPlayer.mPauseCalled);
@@ -426,6 +425,38 @@
}
@Test
+ public void testSetAllowedCommands() throws InterruptedException {
+ final CommandGroup commands = new CommandGroup(mContext);
+ commands.addCommand(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_PLAY));
+ commands.addCommand(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE));
+ commands.addCommand(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_STOP));
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
+ @Override
+ public void onAllowedCommandsChanged(CommandGroup commandsOut) {
+ assertNotNull(commandsOut);
+ List<Command> expected = commands.getCommands();
+ List<Command> actual = commandsOut.getCommands();
+
+ assertNotNull(actual);
+ assertEquals(expected.size(), actual.size());
+ for (int i = 0; i < expected.size(); i++) {
+ assertEquals(expected.get(i), actual.get(i));
+ }
+ latch.countDown();
+ }
+ };
+
+ final MediaController2 controller = createController(mSession.getToken(), true, callback);
+ ControllerInfo controllerInfo = getTestControllerInfo();
+ assertNotNull(controllerInfo);
+
+ mSession.setAllowedCommands(controllerInfo, commands);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
public void testSendCustomAction() throws InterruptedException {
final Command testCommand =
new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_PREPARE);
@@ -457,13 +488,12 @@
private ControllerInfo getTestControllerInfo() {
List<ControllerInfo> controllers = mSession.getConnectedControllers();
assertNotNull(controllers);
- final String packageName = mContext.getPackageName();
for (int i = 0; i < controllers.size(); i++) {
- if (TextUtils.equals(packageName, controllers.get(i).getPackageName())) {
+ if (Process.myUid() == controllers.get(i).getUid()) {
return controllers.get(i);
}
}
- fail("Fails to get custom command");
+ fail("Failed to get test controller info");
return null;
}
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
index b32400f..83706c0 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
@@ -69,6 +69,7 @@
default void onPlaybackInfoChanged(MediaController2.PlaybackInfo info) {}
default void onPlaybackStateChanged(PlaybackState2 state) {}
default void onCustomLayoutChanged(List<CommandButton> layout) {}
+ default void onAllowedCommandsChanged(CommandGroup commands) {}
default void onCustomCommand(Command command, Bundle args, ResultReceiver receiver) {}
}
@@ -247,6 +248,11 @@
public void onCustomLayoutChanged(MediaController2 controller, List<CommandButton> layout) {
mCallbackProxy.onCustomLayoutChanged(layout);
}
+
+ @Override
+ public void onAllowedCommandsChanged(MediaController2 controller, CommandGroup commands) {
+ mCallbackProxy.onAllowedCommandsChanged(commands);
+ }
}
public class TestMediaController extends MediaController2 implements TestControllerInterface {
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2_PermissionTest.java b/packages/MediaComponents/test/src/android/media/MediaSession2_PermissionTest.java
index d89cecd..ca36513 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2_PermissionTest.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2_PermissionTest.java
@@ -16,19 +16,38 @@
package android.media;
-import static android.media.MediaSession2.*;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_FAST_FORWARD;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_PLAY;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_REWIND;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_SEEK_TO;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_STOP;
+import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_MEDIA_ID;
+import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_SEARCH;
+import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_URI;
+import static android.media.MediaSession2.COMMAND_CODE_PREPARE_FROM_MEDIA_ID;
+import static android.media.MediaSession2.COMMAND_CODE_PREPARE_FROM_SEARCH;
+import static android.media.MediaSession2.COMMAND_CODE_PREPARE_FROM_URI;
+import static android.media.MediaSession2.ControllerInfo;
+import static android.media.MediaSession2.PlaylistParams;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.media.MediaSession2;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.SessionCallback;
@@ -41,8 +60,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mockito;
+
+import java.util.List;
/**
* Tests whether {@link MediaSession2} receives commands that hasn't allowed.
@@ -154,13 +173,13 @@
public void testSkipToNext() throws InterruptedException {
createSessionWithAllowedActions(
createCommandGroupWith(COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM));
- createController(mSession.getToken()).skipToNext();
+ createController(mSession.getToken()).skipToNextItem();
verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(
matchesSession(), matchesCaller(), matches(COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM));
createSessionWithAllowedActions(
createCommandGroupWithout(COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM));
- createController(mSession.getToken()).skipToNext();
+ createController(mSession.getToken()).skipToNextItem();
verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any(), any());
}
@@ -168,13 +187,13 @@
public void testSkipToPrevious() throws InterruptedException {
createSessionWithAllowedActions(
createCommandGroupWith(COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM));
- createController(mSession.getToken()).skipToPrevious();
+ createController(mSession.getToken()).skipToPreviousItem();
verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(
matchesSession(), matchesCaller(), matches(COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM));
createSessionWithAllowedActions(
createCommandGroupWithout(COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM));
- createController(mSession.getToken()).skipToPrevious();
+ createController(mSession.getToken()).skipToPreviousItem();
verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any(), any());
}
@@ -260,12 +279,13 @@
@Test
public void testSetVolume() throws InterruptedException {
- createSessionWithAllowedActions(createCommandGroupWith(COMMAND_CODE_SET_VOLUME));
+ createSessionWithAllowedActions(createCommandGroupWith(COMMAND_CODE_PLAYBACK_SET_VOLUME));
createController(mSession.getToken()).setVolumeTo(0, 0);
verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(
- matchesSession(), matchesCaller(), matches(COMMAND_CODE_SET_VOLUME));
+ matchesSession(), matchesCaller(), matches(COMMAND_CODE_PLAYBACK_SET_VOLUME));
- createSessionWithAllowedActions(createCommandGroupWithout(COMMAND_CODE_SET_VOLUME));
+ createSessionWithAllowedActions(
+ createCommandGroupWithout(COMMAND_CODE_PLAYBACK_SET_VOLUME));
createController(mSession.getToken()).setVolumeTo(0, 0);
verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any(), any());
}
@@ -362,4 +382,43 @@
verify(mCallback, after(WAIT_TIME_MS).never()).onPrepareFromSearch(
any(), any(), any(), any());
}
+
+ @Test
+ public void testChangingPermissionWithSetAllowedCommands() throws InterruptedException {
+ final String query = "testChangingPermissionWithSetAllowedCommands";
+ createSessionWithAllowedActions(
+ createCommandGroupWith(COMMAND_CODE_PREPARE_FROM_SEARCH));
+
+ TestControllerCallbackInterface controllerCallback =
+ mock(TestControllerCallbackInterface.class);
+ MediaController2 controller =
+ createController(mSession.getToken(), true, controllerCallback);
+
+ controller.prepareFromSearch(query, null);
+ verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onPrepareFromSearch(
+ matchesSession(), matchesCaller(), eq(query), isNull());
+ clearInvocations(mCallback);
+
+ // Change allowed commands.
+ mSession.setAllowedCommands(getTestControllerInfo(),
+ createCommandGroupWithout(COMMAND_CODE_PREPARE_FROM_SEARCH));
+ verify(controllerCallback, timeout(TIMEOUT_MS).atLeastOnce())
+ .onAllowedCommandsChanged(any());
+
+ controller.prepareFromSearch(query, null);
+ verify(mCallback, after(WAIT_TIME_MS).never()).onPrepareFromSearch(
+ any(), any(), any(), any());
+ }
+
+ private ControllerInfo getTestControllerInfo() {
+ List<ControllerInfo> controllers = mSession.getConnectedControllers();
+ assertNotNull(controllers);
+ for (int i = 0; i < controllers.size(); i++) {
+ if (Process.myUid() == controllers.get(i).getUid()) {
+ return controllers.get(i);
+ }
+ }
+ fail("Failed to get test controller info");
+ return null;
+ }
}
diff --git a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
index fb02f7a..df516c5 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
@@ -27,6 +27,8 @@
import android.media.TestServiceRegistry.SessionCallbackProxy;
import android.media.TestUtils.SyncHandler;
import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.util.Log;
import java.io.FileDescriptor;
@@ -96,8 +98,8 @@
@Override
public void onCreate() {
- super.onCreate();
TestServiceRegistry.getInstance().setServiceInstance(this);
+ super.onCreate();
}
@Override
@@ -144,9 +146,9 @@
}
@Override
- public CommandGroup onConnect(MediaSession2 session,
- ControllerInfo controller) {
- return mCallbackProxy.onConnect(controller);
+ public CommandGroup onConnect(@NonNull MediaSession2 session,
+ @NonNull ControllerInfo controller) {
+ return mCallbackProxy.onConnect(session, controller);
}
@Override
@@ -211,15 +213,16 @@
}
@Override
- public void onSubscribe(MediaLibrarySession session, ControllerInfo controller,
- String parentId, Bundle extras) {
- mCallbackProxy.onSubscribe(controller, parentId, extras);
+ public void onSubscribe(@NonNull MediaLibrarySession session,
+ @NonNull ControllerInfo controller, @NonNull String parentId,
+ @Nullable Bundle extras) {
+ mCallbackProxy.onSubscribe(session, controller, parentId, extras);
}
@Override
- public void onUnsubscribe(MediaLibrarySession session, ControllerInfo controller,
- String parentId) {
- mCallbackProxy.onUnsubscribe(controller, parentId);
+ public void onUnsubscribe(@NonNull MediaLibrarySession session,
+ @NonNull ControllerInfo controller, String parentId) {
+ mCallbackProxy.onUnsubscribe(session, controller, parentId);
}
}
diff --git a/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java b/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
index ce7ce8b..64b1acd 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
@@ -27,6 +27,7 @@
import android.media.MediaSession2.SessionCallback;
import android.media.TestServiceRegistry.SessionCallbackProxy;
import android.media.TestUtils.SyncHandler;
+import android.support.annotation.NonNull;
import java.util.concurrent.Executor;
@@ -46,8 +47,8 @@
@Override
public void onCreate() {
- super.onCreate();
TestServiceRegistry.getInstance().setServiceInstance(this);
+ super.onCreate();
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
}
@@ -103,9 +104,9 @@
}
@Override
- public CommandGroup onConnect(MediaSession2 session,
- ControllerInfo controller) {
- return mCallbackProxy.onConnect(controller);
+ public CommandGroup onConnect(@NonNull MediaSession2 session,
+ @NonNull ControllerInfo controller) {
+ return mCallbackProxy.onConnect(session, controller);
}
}
}
diff --git a/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java b/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
index 08e0cf0..27b6f89 100644
--- a/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
+++ b/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.fail;
import android.content.Context;
+import android.media.MediaLibraryService2.MediaLibrarySession;
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
import android.media.TestUtils.SyncHandler;
@@ -26,6 +27,8 @@
import android.os.Handler;
import android.os.Process;
import android.support.annotation.GuardedBy;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
/**
* Keeps the instance of currently running {@link MockMediaSessionService2}. And also provides
@@ -36,7 +39,7 @@
public class TestServiceRegistry {
/**
* Proxy for both {@link MediaSession2.SessionCallback} and
- * {@link MediaLibraryService2.MediaLibrarySessionCallback}.
+ * {@link MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback}.
*/
public static abstract class SessionCallbackProxy {
private final Context mContext;
@@ -56,7 +59,8 @@
* @param controller
* @return
*/
- public CommandGroup onConnect(ControllerInfo controller) {
+ public CommandGroup onConnect(@NonNull MediaSession2 session,
+ @NonNull ControllerInfo controller) {
if (Process.myUid() == controller.getUid()) {
CommandGroup commands = new CommandGroup(mContext);
commands.addAllPredefinedCommands();
@@ -75,8 +79,10 @@
*/
public void onServiceDestroyed() { }
- public void onSubscribe(ControllerInfo info, String parentId, Bundle extra) { }
- public void onUnsubscribe(ControllerInfo info, String parentId) { }
+ public void onSubscribe(@NonNull MediaLibrarySession session, @NonNull ControllerInfo info,
+ @NonNull String parentId, @Nullable Bundle extra) { }
+ public void onUnsubscribe(@NonNull MediaLibrarySession session,
+ @NonNull ControllerInfo info, @NonNull String parentId) { }
}
@GuardedBy("TestServiceRegistry.class")
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 1301998..3134323 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1837,6 +1837,10 @@
void AudioFlinger::PlaybackThread::preExit()
{
ALOGV(" preExit()");
+ // FIXME this is using hard-coded strings but in the future, this functionality will be
+ // converted to use audio HAL extensions required to support tunneling
+ status_t result = mOutput->stream->setParameters(String8("exiting=1"));
+ ALOGE_IF(result != OK, "Error when setting parameters on exit: %d", result);
}
// PlaybackThread::createTrack_l() must be called with AudioFlinger::mLock held