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(&timestamp);
+  _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 तुम्‍ही सेटिंग्ज &gt; प्रवेशसुलभता मध्ये वैशिष्ट्य बदलू शकता."</string>
+    <string name="accessibility_shortcut_toogle_warning" msgid="7758891516165017413">"शॉर्टकट सुरू असताना, दोन्ही मागे आणि खाली बटणे 3 सेकंद दाबल्याने प्रवेशसुलभता वैशिष्ट्य सुरू होईल.\n\n सध्‍याचे प्रवेशसुलभता वैशिष्ट्य:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n तुम्‍ही सेटिंग्ज &gt; प्रवेशसुलभता मध्ये वैशिष्ट्य बदलू शकता."</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