Set config_maxUiWidth to 1920 am: 92b6197e64 am: 28aaa2c7bc am: 1f7a9b37d5
Original change: https://googleplex-android-review.googlesource.com/c/device/google/atv/+/11177215
Change-Id: Ie9df65a9f27a04dda61a703cb49d0d238047cd07
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
new file mode 100644
index 0000000..17691a9
--- /dev/null
+++ b/PREUPLOAD.cfg
@@ -0,0 +1,8 @@
+[Builtin Hooks]
+clang_format = true
+
+[Builtin Hooks Options]
+# Only turn on clang-format check for the following subfolders.
+clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
+ audio_proxy/
+
diff --git a/TvProvision/Android.bp b/TvProvision/Android.bp
index 71e0ee9..ed00008 100644
--- a/TvProvision/Android.bp
+++ b/TvProvision/Android.bp
@@ -1,6 +1,6 @@
prebuilt_etc {
name: "privapp_whitelist_com.android.tv.provision",
- system_ext_specific: true,
+ product_specific: true,
sub_dir: "permissions",
src: "com.android.tv.provision.xml",
filename_from_src: true,
@@ -9,8 +9,8 @@
android_app {
name: "TvProvision",
srcs: ["**/*.java"],
- platform_apis: true,
- system_ext_specific: true,
+ product_specific: true,
+ sdk_version: "system_current",
certificate: "platform",
privileged: true,
overrides: ["SdkSetup"],
diff --git a/TvProvision/src/com/android/tv/provision/DefaultActivity.java b/TvProvision/src/com/android/tv/provision/DefaultActivity.java
index 97a7ff9..0a7debf 100644
--- a/TvProvision/src/com/android/tv/provision/DefaultActivity.java
+++ b/TvProvision/src/com/android/tv/provision/DefaultActivity.java
@@ -20,7 +20,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
@@ -31,6 +30,8 @@
*/
public class DefaultActivity extends Activity {
+ private static final String TV_USER_SETUP_COMPLETE = "tv_user_setup_complete";
+
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
@@ -40,7 +41,7 @@
Settings.Global.putInt(getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1);
}
Settings.Secure.putInt(getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 1);
- Settings.Secure.putInt(getContentResolver(), Settings.Secure.TV_USER_SETUP_COMPLETE, 1);
+ Settings.Secure.putInt(getContentResolver(), TV_USER_SETUP_COMPLETE, 1);
// remove this activity from the package manager.
PackageManager pm = getPackageManager();
@@ -54,8 +55,7 @@
private boolean isRestrictedUser() {
UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
- UserInfo userInfo = userManager.getUserInfo(UserHandle.myUserId());
- return userInfo.isRestricted();
+ return userManager.isRestrictedProfile();
}
}
diff --git a/audio_proxy/Android.bp b/audio_proxy/Android.bp
new file mode 100644
index 0000000..3c926bd
--- /dev/null
+++ b/audio_proxy/Android.bp
@@ -0,0 +1,74 @@
+cc_library {
+ name: "libaudio_proxy.google",
+
+ product_specific: true,
+
+ srcs: [
+ "AudioProxy.cpp",
+ ],
+
+ shared_libs: [
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+
+ // HAL version 5.0
+ "android.hardware.audio@5.0",
+ "android.hardware.audio.common@5.0",
+ "android.hardware.audio.common@5.0-util",
+ "device.google.atv.audio_proxy@5.0",
+ ],
+
+ static_libs: [
+ "libaudio_proxy_client@5.0",
+ ],
+}
+
+cc_defaults {
+ name: "libaudio_proxy_client_default",
+
+ product_specific: true,
+
+ srcs: [
+ "AudioProxyDevice.cpp",
+ "AudioProxyManager.cpp",
+ "AudioProxyStreamOut.cpp",
+ "BusDeviceImpl.cpp",
+ "HidlTypeUtil.cpp",
+ "StreamOutImpl.cpp",
+ ],
+
+ header_libs: [
+ "android.hardware.audio.common.util@all-versions",
+ "libaudio_system_headers",
+ ],
+
+ shared_libs: [
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ ]
+}
+
+cc_library_static {
+ name: "libaudio_proxy_client@5.0",
+
+ defaults: [ "libaudio_proxy_client_default" ],
+
+ shared_libs: [
+ "android.hardware.audio@5.0",
+ "android.hardware.audio.common@5.0",
+ "android.hardware.audio.common@5.0-util",
+ "device.google.atv.audio_proxy@5.0",
+ ],
+
+ cflags: [
+ "-DMAJOR_VERSION=5",
+ "-DMINOR_VERSION=0",
+ "-include common/all-versions/VersionMacro.h",
+ ],
+}
diff --git a/audio_proxy/AudioProxy.cpp b/audio_proxy/AudioProxy.cpp
new file mode 100644
index 0000000..a215299
--- /dev/null
+++ b/audio_proxy/AudioProxy.cpp
@@ -0,0 +1,55 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#define LOG_TAG "audio_proxy_client"
+
+#include <utils/Log.h>
+
+#include "AudioProxyManager.h"
+#include "public/audio_proxy.h"
+
+namespace {
+class AudioProxyImpl {
+ public:
+ static AudioProxyImpl* getInstance();
+
+ bool registerDevice(audio_proxy_device_t* device);
+
+ private:
+ AudioProxyImpl();
+ ~AudioProxyImpl() = default;
+
+ std::unique_ptr<audio_proxy::AudioProxyManager> mManager;
+};
+
+AudioProxyImpl::AudioProxyImpl() {
+ mManager = audio_proxy::V5_0::createAudioProxyManager();
+ ALOGE_IF(!mManager, "Failed to create audio proxy manager");
+}
+
+bool AudioProxyImpl::registerDevice(audio_proxy_device_t* device) {
+ return mManager && mManager->registerDevice(device);
+}
+
+// static
+AudioProxyImpl* AudioProxyImpl::getInstance() {
+ static AudioProxyImpl instance;
+ return &instance;
+}
+
+} // namespace
+
+extern "C" int audio_proxy_register_device(audio_proxy_device_t* device) {
+ return AudioProxyImpl::getInstance()->registerDevice(device) ? 0 : -1;
+}
diff --git a/audio_proxy/AudioProxyDevice.cpp b/audio_proxy/AudioProxyDevice.cpp
new file mode 100644
index 0000000..16cdcf4
--- /dev/null
+++ b/audio_proxy/AudioProxyDevice.cpp
@@ -0,0 +1,95 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#define LOG_TAG "audio_proxy_client"
+
+#include "AudioProxyDevice.h"
+
+#include <utils/Log.h>
+
+#include "AudioProxyStreamOut.h"
+#include "HidlTypeUtil.h"
+
+#define CHECK_API(func) \
+ do { \
+ if (!stream->func) { \
+ ALOGD("Undefined API %s", #func); \
+ return false; \
+ } \
+ } while (0)
+
+namespace audio_proxy {
+namespace CPP_VERSION {
+namespace {
+bool isValidStreamOut(const audio_proxy_stream_out_t* stream) {
+ CHECK_API(get_buffer_size);
+ CHECK_API(get_frame_count);
+ CHECK_API(get_supported_sample_rates);
+ CHECK_API(get_sample_rate);
+ CHECK_API(get_supported_channel_masks);
+ CHECK_API(get_channel_mask);
+ CHECK_API(get_supported_formats);
+ CHECK_API(get_format);
+ CHECK_API(get_latency);
+ CHECK_API(standby);
+ CHECK_API(pause);
+ CHECK_API(resume);
+ CHECK_API(flush);
+ CHECK_API(write);
+ CHECK_API(get_presentation_position);
+ CHECK_API(set_parameters);
+ CHECK_API(get_parameters);
+
+ return true;
+}
+} // namespace
+
+AudioProxyDevice::AudioProxyDevice(audio_proxy_device_t* device)
+ : mDevice(device) {}
+
+AudioProxyDevice::~AudioProxyDevice() = default;
+
+const char* AudioProxyDevice::getAddress() {
+ return mDevice->get_address(mDevice);
+}
+
+Result AudioProxyDevice::openOutputStream(
+ hidl_bitfield<AudioOutputFlag> flags, const AudioConfig& hidlConfig,
+ std::unique_ptr<AudioProxyStreamOut>* streamOut,
+ AudioConfig* hidlConfigOut) {
+ audio_proxy_config_t config = toAudioProxyConfig(hidlConfig);
+
+ audio_proxy_stream_out_t* stream = nullptr;
+ int ret = mDevice->open_output_stream(
+ mDevice, static_cast<audio_proxy_output_flags_t>(flags), &config,
+ &stream);
+
+ if (stream) {
+ if (!isValidStreamOut(stream)) {
+ mDevice->close_output_stream(mDevice, stream);
+ return Result::NOT_SUPPORTED;
+ }
+
+ *streamOut = std::make_unique<AudioProxyStreamOut>(stream, mDevice);
+ }
+
+ // Pass the config out even if open_output_stream returns error, as the
+ // suggested config, so that audio service can re-open the stream with
+ // suggested config.
+ *hidlConfigOut = toHidlAudioConfig(config);
+ return toResult(ret);
+}
+
+} // namespace CPP_VERSION
+} // namespace audio_proxy
diff --git a/audio_proxy/AudioProxyDevice.h b/audio_proxy/AudioProxyDevice.h
new file mode 100644
index 0000000..d801fc9
--- /dev/null
+++ b/audio_proxy/AudioProxyDevice.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <memory>
+
+#include "public/audio_proxy.h"
+
+// clang-format off
+#include PATH(android/hardware/audio/FILE_VERSION/types.h)
+#include PATH(android/hardware/audio/common/FILE_VERSION/types.h)
+// clang-format on
+
+namespace audio_proxy {
+namespace CPP_VERSION {
+
+using ::android::hardware::hidl_bitfield;
+using namespace ::android::hardware::audio::CPP_VERSION;
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+
+class AudioProxyStreamOut;
+
+// C++ friendly wrapper of audio_proxy_device.
+class AudioProxyDevice final {
+ public:
+ explicit AudioProxyDevice(audio_proxy_device_t* device);
+ ~AudioProxyDevice();
+
+ const char* getAddress();
+
+ Result openOutputStream(hidl_bitfield<AudioOutputFlag> flags,
+ const AudioConfig& config,
+ std::unique_ptr<AudioProxyStreamOut>* streamOut,
+ AudioConfig* configOut);
+
+ private:
+ audio_proxy_device_t* const mDevice;
+};
+
+} // namespace CPP_VERSION
+} // namespace audio_proxy
diff --git a/audio_proxy/AudioProxyManager.cpp b/audio_proxy/AudioProxyManager.cpp
new file mode 100644
index 0000000..b84a206
--- /dev/null
+++ b/audio_proxy/AudioProxyManager.cpp
@@ -0,0 +1,136 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#define LOG_TAG "audio_proxy_client"
+
+#include "AudioProxyManager.h"
+
+#include <mutex>
+
+// clang-format off
+#include PATH(device/google/atv/audio_proxy/FILE_VERSION/IAudioProxyDevicesManager.h)
+// clang-format on
+
+#include <hidl/HidlTransportSupport.h>
+#include <utils/Log.h>
+
+#include "AudioProxyDevice.h"
+#include "BusDeviceImpl.h"
+
+#define QUOTE(s) #s
+#define TO_STR(s) QUOTE(s)
+
+using ::android::sp;
+using ::android::status_t;
+using ::android::hardware::hidl_death_recipient;
+using ::android::hardware::Return;
+using ::device::google::atv::audio_proxy::CPP_VERSION::
+ IAudioProxyDevicesManager;
+
+namespace audio_proxy {
+namespace CPP_VERSION {
+namespace {
+
+bool checkDevice(audio_proxy_device_t* device) {
+ return device && device->get_address && device->open_output_stream &&
+ device->close_output_stream;
+}
+
+class DeathRecipient;
+
+class AudioProxyManagerImpl : public AudioProxyManager {
+ public:
+ explicit AudioProxyManagerImpl(const sp<IAudioProxyDevicesManager>& manager);
+ ~AudioProxyManagerImpl() override = default;
+
+ bool registerDevice(audio_proxy_device_t* device) override;
+
+ void reconnectService();
+
+ private:
+ std::mutex mLock;
+ sp<IAudioProxyDevicesManager> mService;
+ std::unique_ptr<AudioProxyDevice> mDevice;
+
+ sp<DeathRecipient> mDeathRecipient;
+};
+
+class DeathRecipient : public hidl_death_recipient {
+ public:
+ explicit DeathRecipient(AudioProxyManagerImpl& manager) : mManager(manager) {}
+ ~DeathRecipient() override = default;
+
+ void serviceDied(
+ uint64_t cookie,
+ const android::wp<::android::hidl::base::V1_0::IBase>& who) override {
+ mManager.reconnectService();
+ }
+
+ private:
+ AudioProxyManagerImpl& mManager;
+};
+
+AudioProxyManagerImpl::AudioProxyManagerImpl(
+ const sp<IAudioProxyDevicesManager>& manager)
+ : mService(manager), mDeathRecipient(new DeathRecipient(*this)) {
+ mService->linkToDeath(mDeathRecipient, 1234);
+}
+
+bool AudioProxyManagerImpl::registerDevice(audio_proxy_device_t* device) {
+ if (!checkDevice(device)) {
+ ALOGE("Invalid device.");
+ return false;
+ }
+
+ std::lock_guard<std::mutex> guard(mLock);
+ if (mDevice) {
+ ALOGE("Device already registered!");
+ return false;
+ }
+
+ mDevice = std::make_unique<AudioProxyDevice>(device);
+
+ const char* address = mDevice->getAddress();
+ return mService->registerDevice(address, new BusDeviceImpl(mDevice.get()));
+}
+
+void AudioProxyManagerImpl::reconnectService() {
+ std::lock_guard<std::mutex> guard(mLock);
+ mService = IAudioProxyDevicesManager::getService();
+ if (!mService) {
+ ALOGE("Failed to reconnect service");
+ return;
+ }
+
+ if (mDevice) {
+ bool success = mService->registerDevice(mDevice->getAddress(),
+ new BusDeviceImpl(mDevice.get()));
+ ALOGE_IF(!success, "fail to register device after reconnect.");
+ }
+}
+
+} // namespace
+
+std::unique_ptr<AudioProxyManager> createAudioProxyManager() {
+ auto service = IAudioProxyDevicesManager::getService();
+ if (!service) {
+ return nullptr;
+ }
+
+ ALOGI("Connect to audio proxy service %s", TO_STR(FILE_VERSION));
+ return std::make_unique<AudioProxyManagerImpl>(service);
+}
+
+} // namespace CPP_VERSION
+} // namespace audio_proxy
diff --git a/audio_proxy/AudioProxyManager.h b/audio_proxy/AudioProxyManager.h
new file mode 100644
index 0000000..d23f5dd
--- /dev/null
+++ b/audio_proxy/AudioProxyManager.h
@@ -0,0 +1,33 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <memory>
+
+#include "public/audio_proxy.h"
+
+namespace audio_proxy {
+
+class AudioProxyManager {
+ public:
+ virtual ~AudioProxyManager() = default;
+
+ virtual bool registerDevice(audio_proxy_device_t* device) = 0;
+};
+
+namespace V5_0 {
+std::unique_ptr<AudioProxyManager> createAudioProxyManager();
+} // namespace V5_0
+} // namespace audio_proxy
diff --git a/audio_proxy/AudioProxyStreamOut.cpp b/audio_proxy/AudioProxyStreamOut.cpp
new file mode 100644
index 0000000..97a40dd
--- /dev/null
+++ b/audio_proxy/AudioProxyStreamOut.cpp
@@ -0,0 +1,248 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "AudioProxyStreamOut.h"
+
+#include "HidlTypeUtil.h"
+
+#define CHECK_OPT_API(func) \
+ do { \
+ if (!mStream->func) return Result::NOT_SUPPORTED; \
+ } while (0)
+
+namespace audio_proxy {
+namespace CPP_VERSION {
+namespace {
+
+template <typename T>
+size_t getArraySize(const T* const arr, T terminator) {
+ if (!arr) {
+ return 0;
+ }
+
+ const T* ptr = arr;
+ while (*ptr != terminator) {
+ ptr++;
+ }
+
+ return ptr - arr;
+}
+
+template <typename T, typename H>
+hidl_vec<H> convertToHidlVec(const T* const arr, T terminator) {
+ const size_t size = getArraySize(arr, terminator);
+ hidl_vec<H> vec(size);
+
+ for (size_t i = 0; i < size; i++) {
+ vec[i] = H(arr[i]);
+ }
+
+ return vec;
+}
+
+std::vector<audio_proxy_key_val_t> buildKeyValVec(
+ const hidl_vec<ParameterValue>& parameters) {
+ std::vector<audio_proxy_key_val_t> kvVec(parameters.size() + 1);
+
+ for (size_t i = 0; i < parameters.size(); i++) {
+ kvVec[i] = {parameters[i].key.c_str(), parameters[i].value.c_str()};
+ }
+
+ // Terminator.
+ kvVec.back() = {};
+ return kvVec;
+}
+
+std::vector<const char*> buildKeyVec(const hidl_vec<hidl_string>& keys) {
+ std::vector<const char*> keyVec(keys.size() + 1);
+ for (size_t i = 0; i < keys.size(); i++) {
+ keyVec[i] = keys[i].c_str();
+ }
+
+ // Terminator.
+ keyVec.back() = nullptr;
+ return keyVec;
+}
+
+void onParametersAvailable(void* obj, const audio_proxy_key_val_t* params) {
+ std::vector<ParameterValue>* results =
+ static_cast<std::vector<ParameterValue>*>(obj);
+ while (params->key != nullptr) {
+ ParameterValue result;
+ result.key = params->key;
+ result.value = params->val;
+ results->push_back(result);
+ params++;
+ }
+}
+} // namespace
+
+AudioProxyStreamOut::AudioProxyStreamOut(audio_proxy_stream_out_t* stream,
+ audio_proxy_device_t* device)
+ : mStream(stream), mDevice(device) {}
+
+AudioProxyStreamOut::~AudioProxyStreamOut() {
+ mDevice->close_output_stream(mDevice, mStream);
+}
+
+uint64_t AudioProxyStreamOut::getFrameCount() const {
+ return mStream->get_frame_count(mStream);
+}
+
+uint32_t AudioProxyStreamOut::getSampleRate() const {
+ return mStream->get_sample_rate(mStream);
+}
+
+Result AudioProxyStreamOut::setSampleRate(uint32_t rate) {
+ CHECK_OPT_API(set_sample_rate);
+ return toResult(mStream->set_sample_rate(mStream, rate));
+}
+
+hidl_vec<uint32_t> AudioProxyStreamOut::getSupportedSampleRates(
+ AudioFormat format) const {
+ return convertToHidlVec<uint32_t, uint32_t>(
+ mStream->get_supported_sample_rates(
+ mStream, static_cast<audio_proxy_format_t>(format)),
+ 0);
+}
+
+size_t AudioProxyStreamOut::getBufferSize() const {
+ return mStream->get_buffer_size(mStream);
+}
+
+hidl_bitfield<AudioChannelMask> AudioProxyStreamOut::getChannelMask() const {
+ return hidl_bitfield<AudioChannelMask>(mStream->get_channel_mask(mStream));
+}
+
+Result AudioProxyStreamOut::setChannelMask(
+ hidl_bitfield<AudioChannelMask> mask) {
+ CHECK_OPT_API(set_channel_mask);
+ return toResult(mStream->set_channel_mask(
+ mStream, static_cast<audio_proxy_channel_mask_t>(mask)));
+}
+
+hidl_vec<hidl_bitfield<AudioChannelMask>>
+AudioProxyStreamOut::getSupportedChannelMasks(AudioFormat format) const {
+ const audio_proxy_channel_mask_t* channelMasks =
+ mStream->get_supported_channel_masks(
+ mStream, static_cast<audio_proxy_format_t>(format));
+
+ return convertToHidlVec<audio_proxy_channel_mask_t,
+ hidl_bitfield<AudioChannelMask>>(
+ channelMasks, AUDIO_PROXY_CHANNEL_INVALID);
+}
+
+AudioFormat AudioProxyStreamOut::getFormat() const {
+ return AudioFormat(mStream->get_format(mStream));
+}
+
+hidl_vec<AudioFormat> AudioProxyStreamOut::getSupportedFormats() const {
+ return convertToHidlVec<audio_proxy_format_t, AudioFormat>(
+ mStream->get_supported_formats(mStream), AUDIO_PROXY_FORMAT_INVALID);
+}
+
+Result AudioProxyStreamOut::setFormat(AudioFormat format) {
+ CHECK_OPT_API(set_format);
+ return toResult(
+ mStream->set_format(mStream, static_cast<audio_proxy_format_t>(format)));
+}
+
+Result AudioProxyStreamOut::standby() {
+ return toResult(mStream->standby(mStream));
+}
+
+Result AudioProxyStreamOut::setParameters(
+ const hidl_vec<ParameterValue>& context,
+ const hidl_vec<ParameterValue>& parameters) {
+ std::vector<audio_proxy_key_val_t> contextKvVec = buildKeyValVec(context);
+ std::vector<audio_proxy_key_val_t> parameterKvVec =
+ buildKeyValVec(parameters);
+ return toResult(mStream->set_parameters(mStream, contextKvVec.data(),
+ parameterKvVec.data()));
+}
+
+hidl_vec<ParameterValue> AudioProxyStreamOut::getParameters(
+ const hidl_vec<ParameterValue>& context,
+ const hidl_vec<hidl_string>& keys) const {
+ std::vector<audio_proxy_key_val_t> contextKvVec = buildKeyValVec(context);
+ std::vector<const char*> keyVec = buildKeyVec(keys);
+
+ std::vector<ParameterValue> results;
+ results.reserve(keys.size());
+
+ mStream->get_parameters(mStream, contextKvVec.data(), keyVec.data(),
+ onParametersAvailable, &results);
+
+ return hidl_vec<ParameterValue>(results);
+}
+
+ssize_t AudioProxyStreamOut::write(const void* buffer, size_t bytes) {
+ return mStream->write(mStream, buffer, bytes);
+}
+
+uint32_t AudioProxyStreamOut::getLatency() const {
+ return mStream->get_latency(mStream);
+}
+
+Result AudioProxyStreamOut::getRenderPosition(uint32_t* dsp_frames) const {
+ CHECK_OPT_API(get_render_position);
+ return toResult(mStream->get_render_position(mStream, dsp_frames));
+}
+
+Result AudioProxyStreamOut::getNextWriteTimestamp(int64_t* timestamp) const {
+ CHECK_OPT_API(get_next_write_timestamp);
+ return toResult(mStream->get_next_write_timestamp(mStream, timestamp));
+}
+
+Result AudioProxyStreamOut::getPresentationPosition(uint64_t* frames,
+ TimeSpec* timestamp) const {
+ struct timespec ts;
+ int ret = mStream->get_presentation_position(mStream, frames, &ts);
+ if (ret != 0) {
+ return toResult(ret);
+ }
+
+ timestamp->tvSec = ts.tv_sec;
+ timestamp->tvNSec = ts.tv_nsec;
+ return Result::OK;
+}
+
+Result AudioProxyStreamOut::pause() {
+ return toResult(mStream->pause(mStream));
+}
+
+Result AudioProxyStreamOut::resume() {
+ return toResult(mStream->resume(mStream));
+}
+
+bool AudioProxyStreamOut::supportsDrain() const {
+ return mStream->drain != nullptr;
+}
+
+Result AudioProxyStreamOut::drain(AudioDrain type) {
+ return toResult(
+ mStream->drain(mStream, static_cast<audio_proxy_drain_type_t>(type)));
+}
+
+Result AudioProxyStreamOut::flush() {
+ return toResult(mStream->flush(mStream));
+}
+
+Result AudioProxyStreamOut::setVolume(float left, float right) {
+ CHECK_OPT_API(set_volume);
+ return toResult(mStream->set_volume(mStream, left, right));
+}
+
+} // namespace CPP_VERSION
+} // namespace audio_proxy
diff --git a/audio_proxy/AudioProxyStreamOut.h b/audio_proxy/AudioProxyStreamOut.h
new file mode 100644
index 0000000..56ad6a2
--- /dev/null
+++ b/audio_proxy/AudioProxyStreamOut.h
@@ -0,0 +1,90 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <memory>
+
+// clang-format off
+#include PATH(android/hardware/audio/FILE_VERSION/types.h)
+#include PATH(android/hardware/audio/common/FILE_VERSION/types.h)
+// clang-format on
+
+#include "public/audio_proxy.h"
+
+namespace audio_proxy {
+namespace CPP_VERSION {
+
+using ::android::hardware::hidl_bitfield;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using namespace ::android::hardware::audio::CPP_VERSION;
+
+// C++ friendly wrapper of audio_proxy_stream_out. It handles type conversion
+// between C type and hidl type.
+class AudioProxyStreamOut final {
+ public:
+ AudioProxyStreamOut(audio_proxy_stream_out_t* stream,
+ audio_proxy_device_t* device);
+ ~AudioProxyStreamOut();
+
+ size_t getBufferSize() const;
+ uint64_t getFrameCount() const;
+
+ hidl_vec<uint32_t> getSupportedSampleRates(AudioFormat format) const;
+ uint32_t getSampleRate() const;
+ Result setSampleRate(uint32_t rate);
+
+ hidl_vec<hidl_bitfield<AudioChannelMask>> getSupportedChannelMasks(
+ AudioFormat format) const;
+ hidl_bitfield<AudioChannelMask> getChannelMask() const;
+ Result setChannelMask(hidl_bitfield<AudioChannelMask> mask);
+
+ hidl_vec<AudioFormat> getSupportedFormats() const;
+ AudioFormat getFormat() const;
+ Result setFormat(AudioFormat format);
+
+ Result standby();
+
+ Result setParameters(const hidl_vec<ParameterValue>& context,
+ const hidl_vec<ParameterValue>& parameters);
+ hidl_vec<ParameterValue> getParameters(
+ const hidl_vec<ParameterValue>& context,
+ const hidl_vec<hidl_string>& keys) const;
+
+ uint32_t getLatency() const;
+ ssize_t write(const void* buffer, size_t bytes);
+ Result getRenderPosition(uint32_t* dsp_frames) const;
+ Result getNextWriteTimestamp(int64_t* timestamp) const;
+ Result getPresentationPosition(uint64_t* frames, TimeSpec* timestamp) const;
+
+ Result pause();
+ Result resume();
+
+ bool supportsDrain() const;
+ Result drain(AudioDrain type);
+
+ Result flush();
+
+ Result setVolume(float left, float right);
+
+ private:
+ audio_proxy_stream_out_t* const mStream;
+ audio_proxy_device_t* const mDevice;
+};
+
+} // namespace CPP_VERSION
+} // namespace audio_proxy
diff --git a/audio_proxy/BusDeviceImpl.cpp b/audio_proxy/BusDeviceImpl.cpp
new file mode 100644
index 0000000..ee53baf
--- /dev/null
+++ b/audio_proxy/BusDeviceImpl.cpp
@@ -0,0 +1,52 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#define LOG_TAG "audio_proxy_client"
+
+#include "BusDeviceImpl.h"
+
+#include <utils/Log.h>
+
+#include "AudioProxyDevice.h"
+#include "AudioProxyStreamOut.h"
+#include "StreamOutImpl.h"
+
+using ::android::hardware::Void;
+
+namespace audio_proxy {
+namespace CPP_VERSION {
+
+BusDeviceImpl::BusDeviceImpl(AudioProxyDevice* device) : mDevice(device) {}
+BusDeviceImpl::~BusDeviceImpl() = default;
+
+Return<void> BusDeviceImpl::openOutputStream(
+ int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config,
+ hidl_bitfield<AudioOutputFlag> flags, const SourceMetadata& sourceMetadata,
+ openOutputStream_cb _hidl_cb) {
+ std::unique_ptr<AudioProxyStreamOut> stream;
+ AudioConfig suggestedConfig;
+
+ Result res =
+ mDevice->openOutputStream(flags, config, &stream, &suggestedConfig);
+ ALOGE_IF(res != Result::OK, "Open output stream error.");
+
+ // Still pass `suggestedConfig` back when `openOutputStream` returns error,
+ // so that audio service can re-open the stream with a new config.
+ _hidl_cb(res, stream ? new StreamOutImpl(std::move(stream)) : nullptr,
+ suggestedConfig);
+ return Void();
+}
+
+} // namespace CPP_VERSION
+} // namespace audio_proxy
diff --git a/audio_proxy/BusDeviceImpl.h b/audio_proxy/BusDeviceImpl.h
new file mode 100644
index 0000000..a445815
--- /dev/null
+++ b/audio_proxy/BusDeviceImpl.h
@@ -0,0 +1,51 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+// clang-format off
+#include PATH(device/google/atv/audio_proxy/FILE_VERSION/IBusDevice.h)
+// clang-format on
+
+#include "public/audio_proxy.h"
+
+namespace audio_proxy {
+namespace CPP_VERSION {
+
+using ::android::hardware::hidl_bitfield;
+using ::android::hardware::Return;
+using ::device::google::atv::audio_proxy::CPP_VERSION::IBusDevice;
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using namespace ::android::hardware::audio::CPP_VERSION;
+
+class AudioProxyDevice;
+
+class BusDeviceImpl : public IBusDevice {
+ public:
+ explicit BusDeviceImpl(AudioProxyDevice* device);
+ ~BusDeviceImpl() override;
+
+ // Methods from ::device::google::atv::audio_proxy::CPP_VERSION::IBusDevice:
+ Return<void> openOutputStream(int32_t ioHandle, const DeviceAddress& device,
+ const AudioConfig& config,
+ hidl_bitfield<AudioOutputFlag> flags,
+ const SourceMetadata& sourceMetadata,
+ openOutputStream_cb _hidl_cb) override;
+
+ private:
+ AudioProxyDevice* const mDevice;
+};
+
+} // namespace CPP_VERSION
+} // namespace audio_proxy
diff --git a/audio_proxy/HidlTypeUtil.cpp b/audio_proxy/HidlTypeUtil.cpp
new file mode 100644
index 0000000..3eb3dcb
--- /dev/null
+++ b/audio_proxy/HidlTypeUtil.cpp
@@ -0,0 +1,57 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "HidlTypeUtil.h"
+
+#include <error.h>
+
+using ::android::hardware::hidl_bitfield;
+
+namespace audio_proxy {
+namespace CPP_VERSION {
+
+Result toResult(int res) {
+ switch (res) {
+ case 0:
+ return Result::OK;
+ case EINVAL:
+ return Result::INVALID_ARGUMENTS;
+ case ENOSYS:
+ return Result::NOT_SUPPORTED;
+ default:
+ return Result::INVALID_STATE;
+ }
+}
+
+AudioConfig toHidlAudioConfig(const audio_proxy_config_t& config) {
+ AudioConfig hidlConfig;
+ hidlConfig.sampleRateHz = config.sample_rate;
+ hidlConfig.channelMask = hidl_bitfield<AudioChannelMask>(config.channel_mask);
+ hidlConfig.format = AudioFormat(config.format);
+ hidlConfig.frameCount = config.frame_count;
+ return hidlConfig;
+}
+
+audio_proxy_config_t toAudioProxyConfig(const AudioConfig& hidlConfig) {
+ audio_proxy_config_t config = {};
+ config.sample_rate = hidlConfig.sampleRateHz;
+ config.channel_mask =
+ static_cast<audio_proxy_channel_mask_t>(hidlConfig.channelMask);
+ config.format = static_cast<audio_proxy_format_t>(hidlConfig.format);
+ config.frame_count = hidlConfig.frameCount;
+ return config;
+}
+
+} // namespace CPP_VERSION
+} // namespace audio_proxy
\ No newline at end of file
diff --git a/audio_proxy/HidlTypeUtil.h b/audio_proxy/HidlTypeUtil.h
new file mode 100644
index 0000000..138fd5d
--- /dev/null
+++ b/audio_proxy/HidlTypeUtil.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include "public/audio_proxy.h"
+
+// clang-format off
+#include PATH(android/hardware/audio/FILE_VERSION/types.h)
+#include PATH(android/hardware/audio/common/FILE_VERSION/types.h)
+// clang-format on
+
+namespace audio_proxy {
+namespace CPP_VERSION {
+
+using namespace ::android::hardware::audio::CPP_VERSION;
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+
+// Convert from C type to HIDL type.
+Result toResult(int res);
+AudioConfig toHidlAudioConfig(const audio_proxy_config_t& config);
+
+// Convert from HIDL type to C type.
+audio_proxy_config_t toAudioProxyConfig(const AudioConfig& hidlConfig);
+
+} // namespace CPP_VERSION
+} // namespace audio_proxy
\ No newline at end of file
diff --git a/audio_proxy/StreamOutImpl.cpp b/audio_proxy/StreamOutImpl.cpp
new file mode 100644
index 0000000..eabc1e3
--- /dev/null
+++ b/audio_proxy/StreamOutImpl.cpp
@@ -0,0 +1,460 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#define LOG_TAG "audio_proxy_client"
+
+#include "StreamOutImpl.h"
+
+#include <system/audio.h>
+#include <time.h>
+#include <utils/Log.h>
+
+#include <cstring>
+
+#include "AudioProxyStreamOut.h"
+
+using ::android::status_t;
+
+namespace audio_proxy {
+namespace CPP_VERSION {
+namespace {
+// 1GB
+constexpr uint32_t kMaxBufferSize = 1 << 30;
+
+void deleteEventFlag(EventFlag* obj) {
+ if (!obj) {
+ return;
+ }
+
+ status_t status = EventFlag::deleteEventFlag(&obj);
+ ALOGE_IF(status, "write MQ event flag deletion error: %s", strerror(-status));
+}
+
+class WriteThread : public Thread {
+ public:
+ // WriteThread's lifespan never exceeds StreamOut's lifespan.
+ WriteThread(std::atomic<bool>* stop, AudioProxyStreamOut* stream,
+ StreamOutImpl::CommandMQ* commandMQ,
+ StreamOutImpl::DataMQ* dataMQ, StreamOutImpl::StatusMQ* statusMQ,
+ EventFlag* eventFlag);
+
+ ~WriteThread() override;
+
+ private:
+ bool threadLoop() override;
+
+ IStreamOut::WriteStatus doGetLatency();
+ IStreamOut::WriteStatus doGetPresentationPosition();
+ IStreamOut::WriteStatus doWrite();
+
+ std::atomic<bool>* const mStop;
+ AudioProxyStreamOut* mStream;
+ StreamOutImpl::CommandMQ* const mCommandMQ;
+ StreamOutImpl::DataMQ* const mDataMQ;
+ StreamOutImpl::StatusMQ* const mStatusMQ;
+ EventFlag* const mEventFlag;
+ const std::unique_ptr<uint8_t[]> mBuffer;
+};
+
+WriteThread::WriteThread(std::atomic<bool>* stop, AudioProxyStreamOut* stream,
+ StreamOutImpl::CommandMQ* commandMQ,
+ StreamOutImpl::DataMQ* dataMQ,
+ StreamOutImpl::StatusMQ* statusMQ,
+ EventFlag* eventFlag)
+ : Thread(false /*canCallJava*/),
+ mStop(stop),
+ mStream(stream),
+ mCommandMQ(commandMQ),
+ mDataMQ(dataMQ),
+ mStatusMQ(statusMQ),
+ mEventFlag(eventFlag),
+ mBuffer(new uint8_t[mDataMQ->getQuantumCount()]) {}
+
+WriteThread::~WriteThread() = default;
+
+IStreamOut::WriteStatus WriteThread::doWrite() {
+ const size_t availToRead = mDataMQ->availableToRead();
+ IStreamOut::WriteStatus status;
+ status.replyTo = IStreamOut::WriteCommand::WRITE;
+ status.retval = Result::OK;
+ status.reply.written = 0;
+ if (mDataMQ->read(&mBuffer[0], availToRead)) {
+ status.reply.written = availToRead;
+ ssize_t writeResult = mStream->write(&mBuffer[0], availToRead);
+ if (writeResult >= 0) {
+ status.reply.written = writeResult;
+ ALOGW_IF(writeResult < availToRead,
+ "Stream doesn't write all the bytes. Drop the unwritten bytes.");
+ } else {
+ status.retval = Result::INVALID_STATE;
+ }
+ }
+
+ return status;
+}
+
+IStreamOut::WriteStatus WriteThread::doGetPresentationPosition() {
+ IStreamOut::WriteStatus status;
+ status.replyTo = IStreamOut::WriteCommand::GET_PRESENTATION_POSITION;
+ status.retval = mStream->getPresentationPosition(
+ &status.reply.presentationPosition.frames,
+ &status.reply.presentationPosition.timeStamp);
+ return status;
+}
+
+IStreamOut::WriteStatus WriteThread::doGetLatency() {
+ IStreamOut::WriteStatus status;
+ status.replyTo = IStreamOut::WriteCommand::GET_LATENCY;
+ status.retval = Result::OK;
+ status.reply.latencyMs = mStream->getLatency();
+ return status;
+}
+
+bool WriteThread::threadLoop() {
+ // This implementation doesn't return control back to the Thread until the
+ // parent thread decides to stop, as the Thread uses mutexes, and this can
+ // lead to priority inversion.
+ while (!std::atomic_load_explicit(mStop, std::memory_order_acquire)) {
+ uint32_t efState = 0;
+ mEventFlag->wait(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY),
+ &efState);
+ if (!(efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY))) {
+ continue; // Nothing to do.
+ }
+
+ IStreamOut::WriteCommand replyTo;
+ if (!mCommandMQ->read(&replyTo)) {
+ continue; // Nothing to do.
+ }
+
+ IStreamOut::WriteStatus status;
+ switch (replyTo) {
+ case IStreamOut::WriteCommand::WRITE:
+ status = doWrite();
+ break;
+ case IStreamOut::WriteCommand::GET_PRESENTATION_POSITION:
+ status = doGetPresentationPosition();
+ break;
+ case IStreamOut::WriteCommand::GET_LATENCY:
+ status = doGetLatency();
+ break;
+ default:
+ ALOGE("Unknown write thread command code %d", replyTo);
+ status.retval = Result::NOT_SUPPORTED;
+ break;
+ }
+ if (!mStatusMQ->write(&status)) {
+ ALOGE("status message queue write failed");
+ }
+ mEventFlag->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL));
+ }
+
+ return false;
+}
+
+} // namespace
+
+StreamOutImpl::StreamOutImpl(std::unique_ptr<AudioProxyStreamOut> stream)
+ : mStream(std::move(stream)), mEventFlag(nullptr, deleteEventFlag) {}
+
+StreamOutImpl::~StreamOutImpl() {
+ closeImpl();
+
+ if (mWriteThread) {
+ status_t status = mWriteThread->join();
+ ALOGE_IF(status, "write thread exit error: %s", strerror(-status));
+ }
+
+ mEventFlag.reset();
+}
+
+Return<uint64_t> StreamOutImpl::getFrameSize() {
+ audio_format_t format = static_cast<audio_format_t>(mStream->getFormat());
+
+ if (!audio_has_proportional_frames(format)) {
+ return sizeof(int8_t);
+ }
+
+ size_t channel_sample_size = audio_bytes_per_sample(format);
+ return audio_channel_count_from_out_mask(
+ static_cast<audio_channel_mask_t>(mStream->getChannelMask())) *
+ channel_sample_size;
+}
+
+Return<uint64_t> StreamOutImpl::getFrameCount() {
+ return mStream->getFrameCount();
+}
+
+Return<uint64_t> StreamOutImpl::getBufferSize() {
+ return mStream->getBufferSize();
+}
+
+Return<uint32_t> StreamOutImpl::getSampleRate() {
+ return mStream->getSampleRate();
+}
+
+Return<void> StreamOutImpl::getSupportedSampleRates(
+ AudioFormat format, getSupportedSampleRates_cb _hidl_cb) {
+ _hidl_cb(Result::OK, mStream->getSupportedSampleRates(format));
+ return Void();
+}
+
+Return<void> StreamOutImpl::getSupportedChannelMasks(
+ AudioFormat format, getSupportedChannelMasks_cb _hidl_cb) {
+ _hidl_cb(Result::OK, mStream->getSupportedChannelMasks(format));
+ return Void();
+}
+
+Return<Result> StreamOutImpl::setSampleRate(uint32_t sampleRateHz) {
+ return mStream->setSampleRate(sampleRateHz);
+}
+
+Return<hidl_bitfield<AudioChannelMask>> StreamOutImpl::getChannelMask() {
+ return hidl_bitfield<AudioChannelMask>(mStream->getChannelMask());
+}
+
+Return<Result> StreamOutImpl::setChannelMask(
+ hidl_bitfield<AudioChannelMask> mask) {
+ return mStream->setChannelMask(mask);
+}
+
+Return<AudioFormat> StreamOutImpl::getFormat() { return mStream->getFormat(); }
+
+Return<void> StreamOutImpl::getSupportedFormats(
+ getSupportedFormats_cb _hidl_cb) {
+ _hidl_cb(mStream->getSupportedFormats());
+ return Void();
+}
+
+Return<Result> StreamOutImpl::setFormat(AudioFormat format) {
+ return mStream->setFormat(format);
+}
+
+Return<void> StreamOutImpl::getAudioProperties(getAudioProperties_cb _hidl_cb) {
+ _hidl_cb(mStream->getSampleRate(), mStream->getChannelMask(),
+ mStream->getFormat());
+ return Void();
+}
+
+Return<Result> StreamOutImpl::addEffect(uint64_t effectId) {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamOutImpl::removeEffect(uint64_t effectId) {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamOutImpl::standby() { return mStream->standby(); }
+
+Return<void> StreamOutImpl::getDevices(getDevices_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, {});
+ return Void();
+}
+
+Return<Result> StreamOutImpl::setDevices(
+ const hidl_vec<DeviceAddress>& devices) {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> StreamOutImpl::getParameters(
+ const hidl_vec<ParameterValue>& context, const hidl_vec<hidl_string>& keys,
+ getParameters_cb _hidl_cb) {
+ _hidl_cb(Result::OK, mStream->getParameters(context, keys));
+ return Void();
+}
+
+Return<Result> StreamOutImpl::setParameters(
+ const hidl_vec<ParameterValue>& context,
+ const hidl_vec<ParameterValue>& parameters) {
+ return mStream->setParameters(context, parameters);
+}
+
+Return<Result> StreamOutImpl::setHwAvSync(uint32_t hwAvSync) {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamOutImpl::close() { return closeImpl(); }
+
+Result StreamOutImpl::closeImpl() {
+ if (mStopWriteThread.load(
+ std::memory_order_relaxed)) { // only this thread writes
+ return Result::INVALID_STATE;
+ }
+ mStopWriteThread.store(true, std::memory_order_release);
+ if (mEventFlag) {
+ mEventFlag->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY));
+ }
+ return Result::OK;
+}
+
+Return<uint32_t> StreamOutImpl::getLatency() { return mStream->getLatency(); }
+
+Return<Result> StreamOutImpl::setVolume(float left, float right) {
+ return mStream->setVolume(left, right);
+}
+
+Return<void> StreamOutImpl::prepareForWriting(uint32_t frameSize,
+ uint32_t framesCount,
+ prepareForWriting_cb _hidl_cb) {
+ ThreadInfo threadInfo = {0, 0};
+
+ // Wrap the _hidl_cb to return an error
+ auto sendError = [&threadInfo, &_hidl_cb](Result result) -> Return<void> {
+ _hidl_cb(result, CommandMQ::Descriptor(), DataMQ::Descriptor(),
+ StatusMQ::Descriptor(), threadInfo);
+ return Void();
+ };
+
+ if (mDataMQ) {
+ ALOGE("the client attempted to call prepareForWriting twice");
+ return sendError(Result::INVALID_STATE);
+ }
+
+ if (frameSize == 0 || framesCount == 0) {
+ ALOGE("Invalid frameSize (%u) or framesCount (%u)", frameSize, framesCount);
+ return sendError(Result::INVALID_ARGUMENTS);
+ }
+
+ if (frameSize > kMaxBufferSize / framesCount) {
+ ALOGE("Buffer too big: %u*%u bytes > MAX_BUFFER_SIZE (%u)", frameSize,
+ framesCount, kMaxBufferSize);
+ return sendError(Result::INVALID_ARGUMENTS);
+ }
+
+ auto commandMQ = std::make_unique<CommandMQ>(1);
+ if (!commandMQ->isValid()) {
+ ALOGE("command MQ is invalid");
+ return sendError(Result::INVALID_ARGUMENTS);
+ }
+
+ auto dataMQ =
+ std::make_unique<DataMQ>(frameSize * framesCount, true /* EventFlag */);
+ if (!dataMQ->isValid()) {
+ ALOGE("data MQ is invalid");
+ return sendError(Result::INVALID_ARGUMENTS);
+ }
+
+ auto statusMQ = std::make_unique<StatusMQ>(1);
+ if (!statusMQ->isValid()) {
+ ALOGE("status MQ is invalid");
+ return sendError(Result::INVALID_ARGUMENTS);
+ }
+
+ EventFlag* rawEventFlag = nullptr;
+ status_t status =
+ EventFlag::createEventFlag(dataMQ->getEventFlagWord(), &rawEventFlag);
+ std::unique_ptr<EventFlag, EventFlagDeleter> eventFlag(rawEventFlag,
+ deleteEventFlag);
+ if (status != ::android::OK || !eventFlag) {
+ ALOGE("failed creating event flag for data MQ: %s", strerror(-status));
+ return sendError(Result::INVALID_ARGUMENTS);
+ }
+
+ sp<WriteThread> writeThread =
+ new WriteThread(&mStopWriteThread, mStream.get(), commandMQ.get(),
+ dataMQ.get(), statusMQ.get(), eventFlag.get());
+ status = writeThread->run("writer", ::android::PRIORITY_URGENT_AUDIO);
+ if (status != ::android::OK) {
+ ALOGW("failed to start writer thread: %s", strerror(-status));
+ return sendError(Result::INVALID_ARGUMENTS);
+ }
+
+ mCommandMQ = std::move(commandMQ);
+ mDataMQ = std::move(dataMQ);
+ mStatusMQ = std::move(statusMQ);
+ mEventFlag = std::move(eventFlag);
+ mWriteThread = std::move(writeThread);
+ threadInfo.pid = getpid();
+ threadInfo.tid = mWriteThread->getTid();
+ _hidl_cb(Result::OK, *mCommandMQ->getDesc(), *mDataMQ->getDesc(),
+ *mStatusMQ->getDesc(), threadInfo);
+ return Void();
+}
+
+Return<void> StreamOutImpl::getRenderPosition(getRenderPosition_cb _hidl_cb) {
+ uint32_t dspFrames = 0;
+ Result res = mStream->getRenderPosition(&dspFrames);
+ _hidl_cb(res, dspFrames);
+ return Void();
+}
+
+Return<void> StreamOutImpl::getNextWriteTimestamp(
+ getNextWriteTimestamp_cb _hidl_cb) {
+ int64_t timestamp = 0;
+ Result res = mStream->getNextWriteTimestamp(×tamp);
+ _hidl_cb(res, timestamp);
+ return Void();
+}
+
+Return<Result> StreamOutImpl::setCallback(
+ const sp<IStreamOutCallback>& callback) {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamOutImpl::clearCallback() { return Result::NOT_SUPPORTED; }
+
+Return<void> StreamOutImpl::supportsPauseAndResume(
+ supportsPauseAndResume_cb _hidl_cb) {
+ _hidl_cb(true, true);
+ return Void();
+}
+
+Return<Result> StreamOutImpl::pause() { return mStream->pause(); }
+
+Return<Result> StreamOutImpl::resume() { return mStream->resume(); }
+
+Return<bool> StreamOutImpl::supportsDrain() { return mStream->supportsDrain(); }
+
+Return<Result> StreamOutImpl::drain(AudioDrain type) {
+ return mStream->drain(type);
+}
+
+Return<Result> StreamOutImpl::flush() { return mStream->flush(); }
+
+Return<void> StreamOutImpl::getPresentationPosition(
+ getPresentationPosition_cb _hidl_cb) {
+ uint64_t frames = 0;
+ TimeSpec ts = {0, 0};
+ Result result = mStream->getPresentationPosition(&frames, &ts);
+ _hidl_cb(result, frames, ts);
+ return Void();
+}
+
+Return<Result> StreamOutImpl::start() { return Result::NOT_SUPPORTED; }
+
+Return<Result> StreamOutImpl::stop() { return Result::NOT_SUPPORTED; }
+
+Return<void> StreamOutImpl::createMmapBuffer(int32_t minSizeFrames,
+ createMmapBuffer_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, MmapBufferInfo());
+ return Void();
+}
+
+Return<void> StreamOutImpl::getMmapPosition(getMmapPosition_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, MmapPosition());
+ return Void();
+}
+
+Return<void> StreamOutImpl::updateSourceMetadata(
+ const SourceMetadata& sourceMetadata) {
+ return Void();
+}
+
+Return<Result> StreamOutImpl::selectPresentation(int32_t presentationId,
+ int32_t programId) {
+ return Result::NOT_SUPPORTED;
+}
+
+} // namespace CPP_VERSION
+} // namespace audio_proxy
diff --git a/audio_proxy/StreamOutImpl.h b/audio_proxy/StreamOutImpl.h
new file mode 100644
index 0000000..435e0f8
--- /dev/null
+++ b/audio_proxy/StreamOutImpl.h
@@ -0,0 +1,128 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+// clang-format off
+#include PATH(android/hardware/audio/FILE_VERSION/IStreamOut.h)
+// clang-format on
+
+#include <fmq/EventFlag.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <utils/Thread.h>
+
+using ::android::sp;
+using ::android::Thread;
+using ::android::hardware::EventFlag;
+using ::android::hardware::hidl_bitfield;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using namespace ::android::hardware::audio::CPP_VERSION;
+
+namespace audio_proxy {
+namespace CPP_VERSION {
+class AudioProxyStreamOut;
+
+class StreamOutImpl : public IStreamOut {
+ public:
+ using CommandMQ = MessageQueue<WriteCommand, kSynchronizedReadWrite>;
+ using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+ using StatusMQ = MessageQueue<WriteStatus, kSynchronizedReadWrite>;
+
+ explicit StreamOutImpl(std::unique_ptr<AudioProxyStreamOut> stream);
+ ~StreamOutImpl() override;
+
+ // Methods from ::android::hardware::audio::CPP_VERSION::IStream follow.
+ Return<uint64_t> getFrameSize() override;
+ Return<uint64_t> getFrameCount() override;
+ Return<uint64_t> getBufferSize() override;
+ Return<uint32_t> getSampleRate() override;
+ Return<void> getSupportedSampleRates(
+ AudioFormat format, getSupportedSampleRates_cb _hidl_cb) override;
+ Return<void> getSupportedChannelMasks(
+ AudioFormat format, getSupportedChannelMasks_cb _hidl_cb) override;
+ Return<Result> setSampleRate(uint32_t sampleRateHz) override;
+ Return<hidl_bitfield<AudioChannelMask>> getChannelMask() override;
+ Return<Result> setChannelMask(hidl_bitfield<AudioChannelMask> mask) override;
+ Return<AudioFormat> getFormat() override;
+ Return<void> getSupportedFormats(getSupportedFormats_cb _hidl_cb) override;
+ Return<Result> setFormat(AudioFormat format) override;
+ Return<void> getAudioProperties(getAudioProperties_cb _hidl_cb) override;
+ Return<Result> addEffect(uint64_t effectId) override;
+ Return<Result> removeEffect(uint64_t effectId) override;
+ Return<Result> standby() override;
+ Return<void> getDevices(getDevices_cb _hidl_cb) override;
+ Return<Result> setDevices(const hidl_vec<DeviceAddress>& devices) override;
+ Return<void> getParameters(const hidl_vec<ParameterValue>& context,
+ const hidl_vec<hidl_string>& keys,
+ getParameters_cb _hidl_cb) override;
+ Return<Result> setParameters(
+ const hidl_vec<ParameterValue>& context,
+ const hidl_vec<ParameterValue>& parameters) override;
+ Return<Result> setHwAvSync(uint32_t hwAvSync) override;
+ Return<Result> close() override;
+
+ // Methods from ::android::hardware::audio::CPP_VERSION::IStreamOut follow.
+ Return<uint32_t> getLatency() override;
+ Return<Result> setVolume(float left, float right) override;
+ Return<void> prepareForWriting(uint32_t frameSize, uint32_t framesCount,
+ prepareForWriting_cb _hidl_cb) override;
+ Return<void> getRenderPosition(getRenderPosition_cb _hidl_cb) override;
+ Return<void> getNextWriteTimestamp(
+ getNextWriteTimestamp_cb _hidl_cb) override;
+ Return<Result> setCallback(const sp<IStreamOutCallback>& callback) override;
+ Return<Result> clearCallback() override;
+ Return<void> supportsPauseAndResume(
+ supportsPauseAndResume_cb _hidl_cb) override;
+ Return<Result> pause() override;
+ Return<Result> resume() override;
+ Return<bool> supportsDrain() override;
+ Return<Result> drain(AudioDrain type) override;
+ Return<Result> flush() override;
+ Return<void> getPresentationPosition(
+ getPresentationPosition_cb _hidl_cb) override;
+ Return<Result> start() override;
+ Return<Result> stop() override;
+ Return<void> createMmapBuffer(int32_t minSizeFrames,
+ createMmapBuffer_cb _hidl_cb) override;
+ Return<void> getMmapPosition(getMmapPosition_cb _hidl_cb) override;
+ Return<void> updateSourceMetadata(
+ const SourceMetadata& sourceMetadata) override;
+ Return<Result> selectPresentation(int32_t presentationId,
+ int32_t programId) override;
+
+ private:
+ typedef void (*EventFlagDeleter)(EventFlag*);
+
+ Result closeImpl();
+
+ std::unique_ptr<AudioProxyStreamOut> mStream;
+
+ std::unique_ptr<CommandMQ> mCommandMQ;
+ std::unique_ptr<DataMQ> mDataMQ;
+ std::unique_ptr<StatusMQ> mStatusMQ;
+ std::unique_ptr<EventFlag, EventFlagDeleter> mEventFlag;
+ std::atomic<bool> mStopWriteThread = false;
+ sp<Thread> mWriteThread;
+};
+
+} // namespace CPP_VERSION
+} // namespace audio_proxy
diff --git a/audio_proxy/interfaces/5.0/Android.bp b/audio_proxy/interfaces/5.0/Android.bp
new file mode 100644
index 0000000..85ca5a3
--- /dev/null
+++ b/audio_proxy/interfaces/5.0/Android.bp
@@ -0,0 +1,19 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "device.google.atv.audio_proxy@5.0",
+ root: "device.google.atv.audio_proxy",
+ product_specific: true,
+ srcs: [
+ "IAudioProxyDevicesManager.hal",
+ "IBusDevice.hal",
+ ],
+ interfaces: [
+ "android.hardware.audio.common@5.0",
+ "android.hardware.audio@5.0",
+ "android.hidl.base@1.0",
+ "android.hidl.safe_union@1.0",
+ ],
+ gen_java: false,
+}
+
diff --git a/audio_proxy/interfaces/5.0/IAudioProxyDevicesManager.hal b/audio_proxy/interfaces/5.0/IAudioProxyDevicesManager.hal
new file mode 100644
index 0000000..b2a6771
--- /dev/null
+++ b/audio_proxy/interfaces/5.0/IAudioProxyDevicesManager.hal
@@ -0,0 +1,28 @@
+/* Copyright 2020 Google Inc. All Rights Reserved. */
+
+package device.google.atv.audio_proxy@5.0;
+
+import IBusDevice;
+
+/**
+ * Main entrance for audio proxy service. Client should use this interface to
+ * register IBusDevice. Service also implements audio HAL IDevicesFactory. When
+ * needed, service will use registered IBusDevice to open output stream. This
+ * allows the client to behave like an audio HAL and read audio from audio
+ * server, if permitted.
+ *
+ * Note, the implementation only supports one version of audio HAL. To avoid
+ * confusion, this interface shares the same version as the supported audio HAL
+ * version.
+ */
+interface IAudioProxyDevicesManager {
+ /**
+ * Registers IBusDevice at `address`. IBusService impl should live as long
+ * as its process, after registered.
+ *
+ * @param address The address associated with the device.
+ * @param device The audio bus device.
+ * @return success True if the device is registered successfully.
+ */
+ registerDevice(string address, IBusDevice device) generates (bool success);
+};
diff --git a/audio_proxy/interfaces/5.0/IBusDevice.hal b/audio_proxy/interfaces/5.0/IBusDevice.hal
new file mode 100644
index 0000000..0044a48
--- /dev/null
+++ b/audio_proxy/interfaces/5.0/IBusDevice.hal
@@ -0,0 +1,27 @@
+/* Copyright 2020 Google Inc. All Rights Reserved. */
+
+package device.google.atv.audio_proxy@5.0;
+
+import android.hardware.audio.common@5.0;
+import android.hardware.audio@5.0::IStreamOut;
+import android.hardware.audio@5.0::Result;
+
+/**
+ * Represents a bus device in audio HAL. Check Java AudioDeviceInfo.TYPE_BUS for
+ * more details.
+ */
+interface IBusDevice {
+ /**
+ * Opens an audio stream for output. This function has the same requirement
+ * as audio HAL IDevice.openOutputStream.
+ */
+ openOutputStream(
+ AudioIoHandle ioHandle,
+ DeviceAddress device,
+ AudioConfig config,
+ bitfield<AudioOutputFlag> flags,
+ SourceMetadata sourceMetadata) generates (
+ Result retval,
+ IStreamOut outStream,
+ AudioConfig suggestedConfig);
+};
diff --git a/audio_proxy/interfaces/Android.bp b/audio_proxy/interfaces/Android.bp
new file mode 100644
index 0000000..89d436a
--- /dev/null
+++ b/audio_proxy/interfaces/Android.bp
@@ -0,0 +1,4 @@
+hidl_package_root {
+ name: "device.google.atv.audio_proxy",
+ path: "device/google/atv/audio_proxy/interfaces",
+}
diff --git a/audio_proxy/interfaces/update-makefiles.sh b/audio_proxy/interfaces/update-makefiles.sh
new file mode 100755
index 0000000..d3f832e
--- /dev/null
+++ b/audio_proxy/interfaces/update-makefiles.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+# Run from Android root, e.g.:
+#
+# device/google/atv/audio_proxy/interfaces/update-makefiles.sh
+
+source $ANDROID_BUILD_TOP/system/tools/hidl/update-makefiles-helper.sh
+
+do_makefiles_update \
+ "device.google.atv.audio_proxy:device/google/atv/audio_proxy/interfaces"
diff --git a/audio_proxy/public/audio_proxy.h b/audio_proxy/public/audio_proxy.h
new file mode 100644
index 0000000..6ffca0d
--- /dev/null
+++ b/audio_proxy/public/audio_proxy.h
@@ -0,0 +1,233 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef DEVICE_GOOGLE_ATV_AUDIO_PROXY_PUBLIC_AUDIO_PROXY_H_
+#define DEVICE_GOOGLE_ATV_AUDIO_PROXY_PUBLIC_AUDIO_PROXY_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// audio proxy allows the application to implement an audio HAL. It contains two
+// components, a client library and a service.
+// The client library is defined by this header file. Applications should
+// integrate this library to provide audio HAL components. Currently it's only
+// IStreamOut.
+// The service implements IDevicesFactory and IDevice. It will register itself
+// to audio server and forward function calls to client.
+
+// Most of the struct/functions just converts the HIDL definitions into C
+// definitions.
+
+// The following enum and typedef are subset of those defined in
+// hardware/interfaces/audio/common/$VERSION/types.hal, or
+// hardware/interfaces/audio/$VERSION/types.hal.
+// The selected subsets are those commonly supported by a normal audio HAL. The
+// library won't check the validation of these enums. In other words, Audio
+// server can still pass value not defined here to the application.
+
+// AudioFormat
+enum {
+ AUDIO_PROXY_FORMAT_INVALID = 0xFFFFFFFFu,
+ AUDIO_PROXY_FORMAT_PCM_16_BIT = 0x1u,
+ AUDIO_PROXY_FORMAT_PCM_8_BIT = 0x2u,
+ AUDIO_PROXY_FORMAT_PCM_FLOAT = 0x5u,
+};
+typedef uint32_t audio_proxy_format_t;
+
+// AudioChannelMask
+enum {
+ AUDIO_PROXY_CHANNEL_INVALID = 0xC0000000u,
+ AUDIO_PROXY_CHANNEL_OUT_MONO = 0x1u,
+ AUDIO_PROXY_CHANNEL_OUT_STEREO = 0x3u,
+};
+typedef uint32_t audio_proxy_channel_mask_t;
+
+// AudioDrain
+enum {
+ AUDIO_PROXY_DRAIN_ALL,
+ AUDIO_PROXY_DRAIN_EARLY_NOTIFY,
+};
+typedef int32_t audio_proxy_drain_type_t;
+
+// AudioOutputFlag
+enum {
+ AUDIO_PROXY_OUTPUT_FLAG_NONE = 0x0,
+ AUDIO_PROXY_OUTPUT_FLAG_DIRECT = 0x1,
+};
+typedef int32_t audio_proxy_output_flags_t;
+
+// AudioConfig
+typedef struct {
+ uint32_t sample_rate;
+ audio_proxy_channel_mask_t channel_mask;
+ audio_proxy_format_t format;
+ uint32_t frame_count;
+
+ // Points to extra fields defined in the future versions.
+ void* extension;
+} audio_proxy_config_t;
+
+// Util structure for key value pair.
+typedef struct {
+ const char* key;
+ const char* val;
+} audio_proxy_key_val_t;
+
+typedef void (*audio_proxy_get_parameters_callback_t)(
+ void*, const audio_proxy_key_val_t*);
+
+// The following struct/functions mirror those definitions in audio HAL. They
+// should have the same requirement as audio HAL interfaces, unless specified.
+
+// IStreamOut.
+struct audio_proxy_stream_out {
+ size_t (*get_buffer_size)(const struct audio_proxy_stream_out* stream);
+ uint64_t (*get_frame_count)(const struct audio_proxy_stream_out* stream);
+
+ // Gets all the sample rate supported by the stream. The list is terminated
+ // by 0. The returned list should have the same life cycle of |stream|.
+ const uint32_t* (*get_supported_sample_rates)(
+ const struct audio_proxy_stream_out* stream, audio_proxy_format_t format);
+ uint32_t (*get_sample_rate)(const struct audio_proxy_stream_out* stream);
+
+ // optional.
+ int (*set_sample_rate)(struct audio_proxy_stream_out* stream, uint32_t rate);
+
+ // Gets all the channel mask supported by the stream. The list is terminated
+ // by AUDIO_PROXY_CHANNEL_INVALID. The returned list should have the same life
+ // cycle of |stream|.
+ const audio_proxy_channel_mask_t* (*get_supported_channel_masks)(
+ const struct audio_proxy_stream_out* stream, audio_proxy_format_t format);
+ audio_proxy_channel_mask_t (*get_channel_mask)(
+ const struct audio_proxy_stream_out* stream);
+
+ // optional.
+ int (*set_channel_mask)(struct audio_proxy_stream_out* stream,
+ audio_proxy_channel_mask_t mask);
+
+ // Gets all the audio formats supported by the stream. The list is terminated
+ // by AUDIO_PROXY_FORMAT_INVALID. The returned list should have the same life
+ // cycle of |stream|.
+ const audio_proxy_format_t* (*get_supported_formats)(
+ const struct audio_proxy_stream_out* stream);
+ audio_proxy_format_t (*get_format)(
+ const struct audio_proxy_stream_out* stream);
+
+ // optional.
+ int (*set_format)(struct audio_proxy_stream_out* stream,
+ audio_proxy_format_t format);
+
+ uint32_t (*get_latency)(const struct audio_proxy_stream_out* stream);
+
+ int (*standby)(struct audio_proxy_stream_out* stream);
+
+ int (*pause)(struct audio_proxy_stream_out* stream);
+ int (*resume)(struct audio_proxy_stream_out* stream);
+
+ // optional.
+ int (*drain)(struct audio_proxy_stream_out* stream,
+ audio_proxy_drain_type_t type);
+
+ int (*flush)(struct audio_proxy_stream_out* stream);
+
+ // Writes |buffer| into |stream|. This is called on an internal thread of this
+ // library.
+ ssize_t (*write)(struct audio_proxy_stream_out* self, const void* buffer,
+ size_t bytes);
+
+ // optional.
+ int (*get_render_position)(const struct audio_proxy_stream_out* stream,
+ uint32_t* dsp_frames);
+
+ // optional.
+ int (*get_next_write_timestamp)(const struct audio_proxy_stream_out* stream,
+ int64_t* timestamp);
+
+ int (*get_presentation_position)(const struct audio_proxy_stream_out* stream,
+ uint64_t* frames,
+ struct timespec* timestamp);
+
+ // opional.
+ int (*set_volume)(struct audio_proxy_stream_out* stream, float left,
+ float right);
+
+ // Sets parameters on |stream|. Both |context| and |param| are terminated
+ // by key_val_t whose key is null. They are only valid during the function
+ // call.
+ int (*set_parameters)(struct audio_proxy_stream_out* stream,
+ const audio_proxy_key_val_t* context,
+ const audio_proxy_key_val_t* param);
+
+ // Gets parameters from |stream|.
+ // |context| is key val pairs array terminated by null key
+ // audio_proxy_key_val_t. |keys| is C string array, terminated by nullptr.
+ // |on_result| is the callback to deliver the result. It must be called before
+ // this function returns, with |obj| as the first argument, and the list of
+ // caller owned list of key value pairs as the second argument.
+ // |obj| opaque object. Implementation should not touch it.
+ void (*get_parameters)(const struct audio_proxy_stream_out* stream,
+ const audio_proxy_key_val_t* context,
+ const char** keys,
+ audio_proxy_get_parameters_callback_t on_result,
+ void* obj);
+
+ // optional.
+ int (*dump)(const struct audio_proxy_stream_out* stream, int fd);
+
+ // Pointer to the next version structure, for compatibility.
+ void* extension;
+};
+
+typedef struct audio_proxy_stream_out audio_proxy_stream_out_t;
+
+// Represents an audio HAL bus device.
+struct audio_proxy_device {
+ // Returns the unique address of this device.
+ const char* (*get_address)(struct audio_proxy_device* device);
+
+ // Similar to IDevice::openOutputStream.
+ int (*open_output_stream)(struct audio_proxy_device* device,
+ audio_proxy_output_flags_t flags,
+ audio_proxy_config_t* config,
+ audio_proxy_stream_out_t** stream_out);
+
+ // Close |stream|. No more methods will be called on |stream| after this.
+ void (*close_output_stream)(struct audio_proxy_device* device,
+ struct audio_proxy_stream_out* stream);
+
+ // Points to next version's struct. Implementation should set this field to
+ // null if next version struct is not available.
+ // This allows library to work with applications integrated with older version
+ // header.
+ void* extension;
+};
+
+typedef struct audio_proxy_device audio_proxy_device_t;
+
+// Provides |device| to the library. It returns 0 on success. This function is
+// supposed to be called once per process.
+// The service behind this library will register a new audio HAL to the audio
+// server, on the first call to the service.
+int audio_proxy_register_device(audio_proxy_device_t* device);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // DEVICE_GOOGLE_ATV_AUDIO_PROXY_PUBLIC_AUDIO_PROXY_H_
diff --git a/audio_proxy/sepolicy/file_contexts b/audio_proxy/sepolicy/file_contexts
new file mode 100644
index 0000000..6e6e729
--- /dev/null
+++ b/audio_proxy/sepolicy/file_contexts
@@ -0,0 +1,3 @@
+# audio proxy service
+/(vendor|system/vendor)/bin/hw/device\.google\.atv\.audio_proxy@5.0-service u:object_r:hal_audio_proxy_default_exec:s0
+
diff --git a/audio_proxy/sepolicy/hal_audio_proxy.te b/audio_proxy/sepolicy/hal_audio_proxy.te
new file mode 100644
index 0000000..134e24b
--- /dev/null
+++ b/audio_proxy/sepolicy/hal_audio_proxy.te
@@ -0,0 +1,8 @@
+# This could be moved to attributes
+hal_attribute(audio_proxy)
+
+binder_call(hal_audio_proxy_client, hal_audio_proxy_server)
+binder_call(hal_audio_proxy_server, hal_audio_proxy_client)
+
+type hal_audio_proxy_hwservice, hwservice_manager_type;
+hal_attribute_hwservice(hal_audio_proxy, hal_audio_proxy_hwservice)
diff --git a/audio_proxy/sepolicy/hal_audio_proxy_default.te b/audio_proxy/sepolicy/hal_audio_proxy_default.te
new file mode 100644
index 0000000..e162130
--- /dev/null
+++ b/audio_proxy/sepolicy/hal_audio_proxy_default.te
@@ -0,0 +1,14 @@
+type hal_audio_proxy_default, domain;
+type hal_audio_proxy_default_exec, exec_type, vendor_file_type, file_type;
+
+# allows transition from init to the daemon _exec domain
+init_daemon_domain(hal_audio_proxy_default);
+
+# AudioProxy HAL incluces Audio as well as AudioProxy HAL interfaces.
+hal_server_domain(hal_audio_proxy_default, hal_audio);
+hal_server_domain(hal_audio_proxy_default, hal_audio_proxy);
+
+# allows audio proxy service access audio HAL interfaces.
+hal_client_domain(hal_audio_proxy_default, hal_audio);
+
+
diff --git a/audio_proxy/sepolicy/hwservice_contexts b/audio_proxy/sepolicy/hwservice_contexts
new file mode 100644
index 0000000..9bfd6e0
--- /dev/null
+++ b/audio_proxy/sepolicy/hwservice_contexts
@@ -0,0 +1,2 @@
+device.google.atv.audio_proxy::IAudioProxyDevicesManager u:object_r:hal_audio_proxy_hwservice:s0
+
diff --git a/audio_proxy/service/Android.bp b/audio_proxy/service/Android.bp
new file mode 100644
index 0000000..fa2bfb6
--- /dev/null
+++ b/audio_proxy/service/Android.bp
@@ -0,0 +1,53 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_binary {
+ name: "device.google.atv.audio_proxy@5.0-service",
+ vendor: true,
+ relative_install_path: "hw",
+
+ srcs: [
+ "AudioProxyDevicesManagerImpl.cpp",
+ "BusDeviceProvider.cpp",
+ "DeviceImpl.cpp",
+ "DevicesFactoryImpl.cpp",
+ "main.cpp",
+ ],
+
+ init_rc: [
+ "device.google.atv.audio_proxy@5.0-service.rc",
+ ],
+
+ vintf_fragments: [ "manifest_audio_proxy.xml" ],
+
+ shared_libs: [
+ "android.hardware.audio@5.0",
+ "android.hardware.audio.common@5.0",
+ "android.hardware.audio.common@5.0-util",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ "device.google.atv.audio_proxy@5.0",
+ ],
+
+ header_libs: [
+ "android.hardware.audio.common.util@all-versions",
+ ],
+
+ cflags: [
+ "-DMAJOR_VERSION=5",
+ "-DMINOR_VERSION=0",
+ "-include common/all-versions/VersionMacro.h",
+ ],
+}
diff --git a/audio_proxy/service/AudioProxyDevicesManagerImpl.cpp b/audio_proxy/service/AudioProxyDevicesManagerImpl.cpp
new file mode 100644
index 0000000..7302eec
--- /dev/null
+++ b/audio_proxy/service/AudioProxyDevicesManagerImpl.cpp
@@ -0,0 +1,65 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "AudioProxyDevicesManagerImpl.h"
+
+#include <utils/Log.h>
+
+using ::android::OK;
+using ::android::status_t;
+
+namespace audio_proxy {
+namespace service {
+
+AudioProxyDevicesManagerImpl::AudioProxyDevicesManagerImpl() = default;
+AudioProxyDevicesManagerImpl::~AudioProxyDevicesManagerImpl() = default;
+
+Return<bool> AudioProxyDevicesManagerImpl::registerDevice(
+ const hidl_string& address, const sp<IBusDevice>& device) {
+ if (address.empty() || !device) {
+ return false;
+ }
+
+ if (!mBusDeviceProvider.add(address, device)) {
+ ALOGE("Failed to register bus device with addr %s", address.c_str());
+ return false;
+ }
+
+ if (!ensureDevicesFactory()) {
+ ALOGE("Failed to register audio devices factory.");
+ mBusDeviceProvider.removeAll();
+ return false;
+ }
+
+ return true;
+}
+
+bool AudioProxyDevicesManagerImpl::ensureDevicesFactory() {
+ sp<DevicesFactoryImpl> factory = mDevicesFactory.promote();
+ if (factory) {
+ return true;
+ }
+
+ factory = new DevicesFactoryImpl(mBusDeviceProvider);
+ status_t status = factory->registerAsService("audio_proxy");
+ if (status != OK) {
+ return false;
+ }
+
+ mDevicesFactory = factory;
+ return true;
+}
+
+} // namespace service
+} // namespace audio_proxy
diff --git a/audio_proxy/service/AudioProxyDevicesManagerImpl.h b/audio_proxy/service/AudioProxyDevicesManagerImpl.h
new file mode 100644
index 0000000..ab2ea29
--- /dev/null
+++ b/audio_proxy/service/AudioProxyDevicesManagerImpl.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include "BusDeviceProvider.h"
+#include "DevicesFactoryImpl.h"
+
+// clang-format off
+#include PATH(device/google/atv/audio_proxy/FILE_VERSION/IAudioProxyDevicesManager.h)
+// clang-format on
+
+using ::android::sp;
+using ::android::wp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::Return;
+using ::device::google::atv::audio_proxy::CPP_VERSION::IAudioProxyDevicesManager;
+using ::device::google::atv::audio_proxy::CPP_VERSION::IBusDevice;
+
+namespace audio_proxy {
+namespace service {
+
+class AudioProxyDevicesManagerImpl : public IAudioProxyDevicesManager {
+ public:
+ AudioProxyDevicesManagerImpl();
+ ~AudioProxyDevicesManagerImpl() override;
+
+ Return<bool> registerDevice(const hidl_string& address,
+ const sp<IBusDevice>& device) override;
+
+ private:
+ bool ensureDevicesFactory();
+
+ BusDeviceProvider mBusDeviceProvider;
+ wp<DevicesFactoryImpl> mDevicesFactory;
+};
+
+} // namespace service
+} // namespace audio_proxy
diff --git a/audio_proxy/service/BusDeviceProvider.cpp b/audio_proxy/service/BusDeviceProvider.cpp
new file mode 100644
index 0000000..adc92ff
--- /dev/null
+++ b/audio_proxy/service/BusDeviceProvider.cpp
@@ -0,0 +1,89 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "BusDeviceProvider.h"
+
+#include <algorithm>
+
+namespace audio_proxy {
+namespace service {
+
+class BusDeviceProvider::DeathRecipient : public hidl_death_recipient {
+ public:
+ explicit DeathRecipient(BusDeviceProvider& owner) : mOwner(owner) {}
+ ~DeathRecipient() override = default;
+
+ void serviceDied(
+ uint64_t token,
+ const android::wp<::android::hidl::base::V1_0::IBase>& who) override {
+ mOwner.removeByToken(token);
+ }
+
+ private:
+ BusDeviceProvider& mOwner;
+};
+
+BusDeviceProvider::BusDeviceProvider()
+ : mDeathRecipient(new DeathRecipient(*this)) {}
+BusDeviceProvider::~BusDeviceProvider() = default;
+
+bool BusDeviceProvider::add(const hidl_string& address, sp<IBusDevice> device) {
+ std::lock_guard<std::mutex> guard(mDevicesLock);
+ auto it = std::find_if(mBusDevices.begin(), mBusDevices.end(),
+ [&address](const BusDeviceHolder& holder) {
+ return holder.address == address;
+ });
+
+ if (it != mBusDevices.end()) {
+ return false;
+ }
+
+ uint64_t token = mNextToken++;
+
+ mBusDevices.push_back({device, address, token});
+
+ device->linkToDeath(mDeathRecipient, token);
+
+ return true;
+}
+
+sp<IBusDevice> BusDeviceProvider::get(const hidl_string& address) {
+ std::lock_guard<std::mutex> guard(mDevicesLock);
+ auto it = std::find_if(mBusDevices.begin(), mBusDevices.end(),
+ [&address](const BusDeviceHolder& holder) {
+ return holder.address == address;
+ });
+
+ if (it == mBusDevices.end()) {
+ return nullptr;
+ }
+
+ return it->device;
+}
+
+void BusDeviceProvider::removeAll() {
+ std::lock_guard<std::mutex> guard(mDevicesLock);
+ mBusDevices.clear();
+}
+
+void BusDeviceProvider::removeByToken(uint64_t token) {
+ std::lock_guard<std::mutex> guard(mDevicesLock);
+ mBusDevices.erase(std::find_if(mBusDevices.begin(), mBusDevices.end(),
+ [token](const BusDeviceHolder& holder) {
+ return holder.token == token;
+ }));
+}
+
+} // namespace service
+} // namespace audio_proxy
\ No newline at end of file
diff --git a/audio_proxy/service/BusDeviceProvider.h b/audio_proxy/service/BusDeviceProvider.h
new file mode 100644
index 0000000..011161a
--- /dev/null
+++ b/audio_proxy/service/BusDeviceProvider.h
@@ -0,0 +1,72 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <mutex>
+#include <vector>
+
+// clang-format off
+#include PATH(device/google/atv/audio_proxy/FILE_VERSION/IBusDevice.h)
+// clang-format on
+
+namespace audio_proxy {
+namespace service {
+
+using ::android::sp;
+using ::android::hardware::hidl_death_recipient;
+using ::android::hardware::hidl_string;
+using ::device::google::atv::audio_proxy::CPP_VERSION::IBusDevice;
+
+class BusDeviceProvider {
+ public:
+ BusDeviceProvider();
+ ~BusDeviceProvider();
+
+ // Register bus device under `address`. The device is unregistered when its
+ // process exits.
+ bool add(const hidl_string& address, sp<IBusDevice> device);
+
+ // Get IBusDevice with `address`. Caller should only keep the strong pointer
+ // shortly, a.k.a caller should release the strong pointer as soon as its
+ // function scope exits.
+ sp<IBusDevice> get(const hidl_string& address);
+
+ // Remove all the added devices.
+ void removeAll();
+
+ private:
+ class DeathRecipient;
+ struct BusDeviceHolder {
+ sp<IBusDevice> device;
+ hidl_string address;
+ uint64_t token;
+ };
+
+ friend class DeathRecipient;
+
+ // Called by DeathRecipient to remove device when the host process exits.
+ void removeByToken(uint64_t token);
+
+ sp<hidl_death_recipient> mDeathRecipient;
+
+ std::mutex mDevicesLock;
+
+ // Use a vector since we don't have too much registered devices.
+ std::vector<BusDeviceHolder> mBusDevices;
+ uint64_t mNextToken = 0;
+};
+
+} // namespace service
+} // namespace audio_proxy
\ No newline at end of file
diff --git a/audio_proxy/service/DeviceImpl.cpp b/audio_proxy/service/DeviceImpl.cpp
new file mode 100644
index 0000000..ac9936d
--- /dev/null
+++ b/audio_proxy/service/DeviceImpl.cpp
@@ -0,0 +1,155 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "DeviceImpl.h"
+
+#include <utils/Log.h>
+
+#include "BusDeviceProvider.h"
+
+#undef LOG_TAG
+#define LOG_TAG "AudioProxyDeviceImpl"
+
+using namespace ::android::hardware::audio::CPP_VERSION;
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+
+namespace audio_proxy {
+namespace service {
+
+DeviceImpl::DeviceImpl(BusDeviceProvider& busDeviceProvider)
+ : mBusDeviceProvider(busDeviceProvider) {}
+
+// Methods from ::android::hardware::audio::V5_0::IDevice follow.
+Return<Result> DeviceImpl::initCheck() { return Result::OK; }
+
+Return<Result> DeviceImpl::setMasterVolume(float volume) {
+ // software mixer will emulate this ability
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> DeviceImpl::getMasterVolume(getMasterVolume_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, 0.f);
+ return Void();
+}
+
+Return<Result> DeviceImpl::setMicMute(bool mute) {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> DeviceImpl::getMicMute(getMicMute_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, false);
+ return Void();
+}
+
+Return<Result> DeviceImpl::setMasterMute(bool mute) {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> DeviceImpl::getMasterMute(getMasterMute_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, false);
+ return Void();
+}
+
+Return<void> DeviceImpl::getInputBufferSize(const AudioConfig& config,
+ getInputBufferSize_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, 0);
+ return Void();
+}
+
+Return<void> DeviceImpl::openOutputStream(int32_t ioHandle,
+ const DeviceAddress& device,
+ const AudioConfig& config,
+ hidl_bitfield<AudioOutputFlag> flags,
+ const SourceMetadata& sourceMetadata,
+ openOutputStream_cb _hidl_cb) {
+ sp<IBusDevice> busDevice = mBusDeviceProvider.get(device.busAddress);
+
+ if (!busDevice) {
+ ALOGE("BusDevice with address %s was not found.",
+ device.busAddress.c_str());
+ _hidl_cb(Result::NOT_SUPPORTED, nullptr, config);
+ return Void();
+ }
+
+ return busDevice->openOutputStream(ioHandle, device, config, flags,
+ sourceMetadata, std::move(_hidl_cb));
+}
+
+Return<void> DeviceImpl::openInputStream(int32_t ioHandle,
+ const DeviceAddress& device,
+ const AudioConfig& config,
+ hidl_bitfield<AudioInputFlag> flags,
+ const SinkMetadata& sinkMetadata,
+ openInputStream_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, sp<IStreamIn>(), config);
+ return Void();
+}
+
+Return<bool> DeviceImpl::supportsAudioPatches() { return true; }
+
+Return<void> DeviceImpl::createAudioPatch(
+ const hidl_vec<AudioPortConfig>& sources,
+ const hidl_vec<AudioPortConfig>& sinks, createAudioPatch_cb _hidl_cb) {
+ _hidl_cb(Result::OK, 0);
+ return Void();
+}
+
+Return<Result> DeviceImpl::releaseAudioPatch(int32_t patch) {
+ return Result::OK;
+}
+
+Return<void> DeviceImpl::getAudioPort(const AudioPort& port,
+ getAudioPort_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, port);
+ return Void();
+}
+
+Return<Result> DeviceImpl::setAudioPortConfig(const AudioPortConfig& config) {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> DeviceImpl::getHwAvSync(getHwAvSync_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, 0);
+ return Void();
+}
+
+Return<Result> DeviceImpl::setScreenState(bool turnedOn) {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> DeviceImpl::getParameters(const hidl_vec<ParameterValue>& context,
+ const hidl_vec<hidl_string>& keys,
+ getParameters_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, hidl_vec<ParameterValue>());
+ return Void();
+}
+
+Return<Result> DeviceImpl::setParameters(
+ const hidl_vec<ParameterValue>& context,
+ const hidl_vec<ParameterValue>& parameters) {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> DeviceImpl::getMicrophones(getMicrophones_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, hidl_vec<MicrophoneInfo>());
+ return Void();
+}
+
+Return<Result> DeviceImpl::setConnectedState(const DeviceAddress& address,
+ bool connected) {
+ return Result::OK;
+}
+
+} // namespace service
+} // namespace audio_proxy
diff --git a/audio_proxy/service/DeviceImpl.h b/audio_proxy/service/DeviceImpl.h
new file mode 100644
index 0000000..3e2b9e4
--- /dev/null
+++ b/audio_proxy/service/DeviceImpl.h
@@ -0,0 +1,98 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+// clang-format off
+#include PATH(android/hardware/audio/FILE_VERSION/IDevice.h)
+// clang-format on
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace audio_proxy {
+namespace service {
+
+using ::android::sp;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_bitfield;
+using ::android::hardware::hidl_death_recipient;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::audio::common::CPP_VERSION::AudioConfig;
+using ::android::hardware::audio::common::CPP_VERSION::AudioInputFlag;
+using ::android::hardware::audio::common::CPP_VERSION::AudioOutputFlag;
+using ::android::hardware::audio::common::CPP_VERSION::AudioPort;
+using ::android::hardware::audio::common::CPP_VERSION::AudioPortConfig;
+using ::android::hardware::audio::common::CPP_VERSION::DeviceAddress;
+using ::android::hardware::audio::common::CPP_VERSION::SinkMetadata;
+using ::android::hardware::audio::common::CPP_VERSION::SourceMetadata;
+using ::android::hardware::audio::CPP_VERSION::IDevice;
+using ::android::hardware::audio::CPP_VERSION::ParameterValue;
+using ::android::hardware::audio::CPP_VERSION::Result;
+
+class BusDeviceProvider;
+
+class DeviceImpl : public IDevice {
+ public:
+ explicit DeviceImpl(BusDeviceProvider& busDeviceProvider);
+
+ // Methods from ::android::hardware::audio::V5_0::IDevice follow.
+ Return<Result> initCheck() override;
+ Return<Result> setMasterVolume(float volume) override;
+ Return<void> getMasterVolume(getMasterVolume_cb _hidl_cb) override;
+ Return<Result> setMicMute(bool mute) override;
+ Return<void> getMicMute(getMicMute_cb _hidl_cb) override;
+ Return<Result> setMasterMute(bool mute) override;
+ Return<void> getMasterMute(getMasterMute_cb _hidl_cb) override;
+ Return<void> getInputBufferSize(const AudioConfig& config,
+ getInputBufferSize_cb _hidl_cb) override;
+ Return<void> openOutputStream(int32_t ioHandle, const DeviceAddress& device,
+ const AudioConfig& config,
+ hidl_bitfield<AudioOutputFlag> flags,
+ const SourceMetadata& sourceMetadata,
+ openOutputStream_cb _hidl_cb) override;
+ Return<void> openInputStream(int32_t ioHandle, const DeviceAddress& device,
+ const AudioConfig& config,
+ hidl_bitfield<AudioInputFlag> flags,
+ const SinkMetadata& sinkMetadata,
+ openInputStream_cb _hidl_cb) override;
+ Return<bool> supportsAudioPatches() override;
+ Return<void> createAudioPatch(const hidl_vec<AudioPortConfig>& sources,
+ const hidl_vec<AudioPortConfig>& sinks,
+ createAudioPatch_cb _hidl_cb) override;
+ Return<Result> releaseAudioPatch(int32_t patch) override;
+ Return<void> getAudioPort(const AudioPort& port,
+ getAudioPort_cb _hidl_cb) override;
+ Return<Result> setAudioPortConfig(const AudioPortConfig& config) override;
+ Return<void> getHwAvSync(getHwAvSync_cb _hidl_cb) override;
+ Return<Result> setScreenState(bool turnedOn) override;
+ Return<void> getParameters(const hidl_vec<ParameterValue>& context,
+ const hidl_vec<hidl_string>& keys,
+ getParameters_cb _hidl_cb) override;
+ Return<Result> setParameters(
+ const hidl_vec<ParameterValue>& context,
+ const hidl_vec<ParameterValue>& parameters) override;
+ Return<void> getMicrophones(getMicrophones_cb _hidl_cb) override;
+ Return<Result> setConnectedState(const DeviceAddress& address,
+ bool connected) override;
+
+ private:
+ BusDeviceProvider& mBusDeviceProvider;
+};
+
+} // namespace service
+} // namespace audio_proxy
diff --git a/audio_proxy/service/DevicesFactoryImpl.cpp b/audio_proxy/service/DevicesFactoryImpl.cpp
new file mode 100644
index 0000000..37963c4
--- /dev/null
+++ b/audio_proxy/service/DevicesFactoryImpl.cpp
@@ -0,0 +1,52 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "DevicesFactoryImpl.h"
+
+#include <utils/Log.h>
+
+#undef LOG_TAG
+#define LOG_TAG "AudioProxyDevicesManagerImpl"
+
+using namespace ::android::hardware::audio::CPP_VERSION;
+
+namespace audio_proxy {
+namespace service {
+
+DevicesFactoryImpl::DevicesFactoryImpl(BusDeviceProvider& busDeviceProvider)
+ : mBusDeviceProvider(busDeviceProvider) {}
+
+// Methods from ::android::hardware::audio::V5_0::IDevicesFactory follow.
+Return<void> DevicesFactoryImpl::openDevice(const hidl_string& device,
+ openDevice_cb _hidl_cb) {
+ ALOGE("openDevice");
+ if (device == "audio_proxy") {
+ ALOGE("Audio Device was opened: %s", device.c_str());
+ _hidl_cb(Result::OK, new DeviceImpl(mBusDeviceProvider));
+ } else {
+ _hidl_cb(Result::NOT_SUPPORTED, nullptr);
+ }
+
+ return Void();
+}
+
+Return<void> DevicesFactoryImpl::openPrimaryDevice(
+ openPrimaryDevice_cb _hidl_cb) {
+ // The AudioProxy HAL does not support a primary device.
+ _hidl_cb(Result::NOT_SUPPORTED, nullptr);
+ return Void();
+}
+
+} // namespace service
+} // namespace audio_proxy
diff --git a/audio_proxy/service/DevicesFactoryImpl.h b/audio_proxy/service/DevicesFactoryImpl.h
new file mode 100644
index 0000000..b8dbf87
--- /dev/null
+++ b/audio_proxy/service/DevicesFactoryImpl.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+// clang-format off
+#include PATH(android/hardware/audio/FILE_VERSION/IDevicesFactory.h)
+// clang-format on
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include "DeviceImpl.h"
+
+namespace audio_proxy {
+namespace service {
+
+using ::android::sp;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::audio::V5_0::IDevicesFactory;
+
+class BusDeviceProvider;
+
+class DevicesFactoryImpl : public IDevicesFactory {
+ public:
+ explicit DevicesFactoryImpl(BusDeviceProvider& busDeviceProvider);
+
+ // Methods from ::android::hardware::audio::V5_0::IDevicesFactory follow.
+ Return<void> openDevice(const hidl_string& device,
+ openDevice_cb _hidl_cb) override;
+ Return<void> openPrimaryDevice(openPrimaryDevice_cb _hidl_cb) override;
+
+ private:
+ BusDeviceProvider& mBusDeviceProvider;
+};
+
+} // namespace service
+} // namespace audio_proxy
diff --git a/audio_proxy/service/device.google.atv.audio_proxy@5.0-service.rc b/audio_proxy/service/device.google.atv.audio_proxy@5.0-service.rc
new file mode 100644
index 0000000..0847c7c
--- /dev/null
+++ b/audio_proxy/service/device.google.atv.audio_proxy@5.0-service.rc
@@ -0,0 +1,4 @@
+service audio_proxy_service /vendor/bin/hw/device.google.atv.audio_proxy@5.0-service
+ class hal
+ user system
+ group system
diff --git a/audio_proxy/service/main.cpp b/audio_proxy/service/main.cpp
new file mode 100644
index 0000000..77eedb7
--- /dev/null
+++ b/audio_proxy/service/main.cpp
@@ -0,0 +1,49 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#define LOG_TAG "audio_proxy_service"
+
+#include <hidl/HidlTransportSupport.h>
+#include <utils/Log.h>
+
+#include "AudioProxyDevicesManagerImpl.h"
+#include "DevicesFactoryImpl.h"
+
+using ::android::sp;
+using ::android::status_t;
+using ::audio_proxy::service::DevicesFactoryImpl;
+
+namespace {
+status_t registerAudioProxyDevicesManager() {
+ sp<audio_proxy::service::AudioProxyDevicesManagerImpl> manager =
+ new audio_proxy::service::AudioProxyDevicesManagerImpl();
+ return manager->registerAsService();
+}
+} // namespace
+
+int main(int argc, char** argv) {
+ android::hardware::configureRpcThreadpool(1, true /* callerWillJoin */);
+
+ status_t status = registerAudioProxyDevicesManager();
+ if (status != android::OK) {
+ ALOGE("fail to register devices factory manager: %x", status);
+ return -1;
+ }
+
+ ::android::hardware::joinRpcThreadpool();
+
+ // `joinRpcThreadpool` should never return. Return -2 here for unexpected
+ // process exit.
+ return -2;
+}
diff --git a/audio_proxy/service/manifest_audio_proxy.xml b/audio_proxy/service/manifest_audio_proxy.xml
new file mode 100644
index 0000000..8259d7e
--- /dev/null
+++ b/audio_proxy/service/manifest_audio_proxy.xml
@@ -0,0 +1,12 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>device.google.atv.audio_proxy</name>
+ <transport>hwbinder</transport>
+ <fqname>@5.0::IAudioProxyDevicesManager/default</fqname>
+ </hal>
+ <hal format="hidl">
+ <name>android.hardware.audio</name>
+ <transport>hwbinder</transport>
+ <fqname>@5.0::IDevicesFactory/audio_proxy</fqname>
+ </hal>
+</manifest>
diff --git a/overlay/frameworks/base/core/res/res/values-mr/strings.xml b/overlay/frameworks/base/core/res/res/values-mr/strings.xml
index dc64789..fdba608 100644
--- a/overlay/frameworks/base/core/res/res/values-mr/strings.xml
+++ b/overlay/frameworks/base/core/res/res/values-mr/strings.xml
@@ -16,5 +16,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_shortcut_toogle_warning" msgid="7758891516165017413">"शॉर्टकट चालू असताना, दोन्ही मागे आणि खाली बटणे 3 सेकंद दाबल्याने प्रवेशसुलभता वैशिष्ट्य सुरू होईल.\n\n सध्याचे प्रवेशसुलभता वैशिष्ट्य:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n तुम्ही सेटिंग्ज > प्रवेशसुलभता मध्ये वैशिष्ट्य बदलू शकता."</string>
+ <string name="accessibility_shortcut_toogle_warning" msgid="7758891516165017413">"शॉर्टकट सुरू असताना, दोन्ही मागे आणि खाली बटणे 3 सेकंद दाबल्याने प्रवेशसुलभता वैशिष्ट्य सुरू होईल.\n\n सध्याचे प्रवेशसुलभता वैशिष्ट्य:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n तुम्ही सेटिंग्ज > प्रवेशसुलभता मध्ये वैशिष्ट्य बदलू शकता."</string>
</resources>
diff --git a/overlay/frameworks/base/core/res/res/values/config.xml b/overlay/frameworks/base/core/res/res/values/config.xml
index 19f31de..d63eaf3 100644
--- a/overlay/frameworks/base/core/res/res/values/config.xml
+++ b/overlay/frameworks/base/core/res/res/values/config.xml
@@ -37,6 +37,10 @@
Any other values will have surprising consequences. -->
<integer name="config_defaultUiModeType">4</integer>
+ <!-- Control whether to lock UI mode to what is selected from config_defaultUiModeType.
+ Once UI mode is locked, applications cannot change it anymore. -->
+ <bool name="config_lockUiMode">true</bool>
+
<!-- default device has recents property -->
<bool name="config_hasRecents">false</bool>
diff --git a/overlay/frameworks/base/packages/SettingsProvider/res/values/defaults.xml b/overlay/frameworks/base/packages/SettingsProvider/res/values/defaults.xml
index be6b533..ca901d3 100644
--- a/overlay/frameworks/base/packages/SettingsProvider/res/values/defaults.xml
+++ b/overlay/frameworks/base/packages/SettingsProvider/res/values/defaults.xml
@@ -33,8 +33,8 @@
to support new developer oriented monitoring tools -->
<bool name="def_netstats_enabled">false</bool>
- <!-- ms until the system goes to sleep, 3 hours default for TV devices -->
- <integer name="def_sleep_timeout">10800000</integer>
+ <!-- ms until the system goes to sleep, 24 hours default for TV devices -->
+ <integer name="def_sleep_timeout">86400000</integer>
<!-- Default screen timeout set when setting screensaver. Currently (15 * 60 * 1000) = 15 min -->
<integer name="def_screen_off_timeout">900000</integer>
diff --git a/products/aosp_tv_arm64.mk b/products/aosp_tv_arm64.mk
index 724a20a..bd5d4da 100644
--- a/products/aosp_tv_arm64.mk
+++ b/products/aosp_tv_arm64.mk
@@ -14,8 +14,6 @@
# limitations under the License.
#
-PRODUCT_USE_DYNAMIC_PARTITIONS := true
-
# The system image of aosp_tv_arm64-userdebug is a GSI for the devices with:
# - ARM 64 bits user space
# - 64 bits binder interface
diff --git a/products/atv_mainline_system.mk b/products/atv_mainline_system.mk
index a28225d..b2148f0 100644
--- a/products/atv_mainline_system.mk
+++ b/products/atv_mainline_system.mk
@@ -21,7 +21,7 @@
$(call inherit-product-if-exists, vendor/google/security/adb/vendor_key.mk)
# Enable updating of APEXes
-#$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk)
+$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk)
# Binaries
PRODUCT_PACKAGES += llkd
diff --git a/products/bootanimations/bootanimation.zip b/products/bootanimations/bootanimation.zip
new file mode 100644
index 0000000..b274f33
--- /dev/null
+++ b/products/bootanimations/bootanimation.zip
Binary files differ
diff --git a/products/sdk_atv_armv7.mk b/products/sdk_atv_armv7.mk
index 0525ac0..1617092 100644
--- a/products/sdk_atv_armv7.mk
+++ b/products/sdk_atv_armv7.mk
@@ -35,3 +35,6 @@
PRODUCT_BRAND := Android
PRODUCT_NAME := sdk_atv_armv7
PRODUCT_DEVICE := generic
+
+PRODUCT_PRODUCT_PROPERTIES += \
+ ro.oem.key1=ATV00100020