Add linux audio PAL

Bug: 144189870

Test: `atest --host chre_unit_tests`,
      `run_pal_impl_tests.sh`
Change-Id: I350a4922fa01157a87738286dd9c2fb9d7bdb811
diff --git a/Android.bp b/Android.bp
index 793fc39..f9882ea 100644
--- a/Android.bp
+++ b/Android.bp
@@ -506,6 +506,7 @@
         "platform/linux/host_link.cc",
         "platform/linux/memory_manager.cc",
         "platform/linux/memory.cc",
+        "platform/linux/pal_audio.cc",
         "platform/linux/pal_ble.cc",
         "platform/linux/pal_gnss.cc",
         "platform/linux/pal_nan.cc",
diff --git a/pal/pal.mk b/pal/pal.mk
index 5bc7c9c..16c546a 100644
--- a/pal/pal.mk
+++ b/pal/pal.mk
@@ -10,8 +10,11 @@
 # GoogleTest Source Files ######################################################
 
 GOOGLETEST_CFLAGS += -I$(CHRE_PREFIX)/pal/tests/include
+
 GOOGLETEST_SRCS += $(CHRE_PREFIX)/pal/tests/src/version_test.cc
 GOOGLETEST_SRCS += $(CHRE_PREFIX)/pal/tests/src/wwan_test.cc
+
+GOOGLETEST_PAL_IMPL_SRCS += $(CHRE_PREFIX)/pal/tests/src/audio_pal_impl_test.cc
 GOOGLETEST_PAL_IMPL_SRCS += $(CHRE_PREFIX)/pal/tests/src/gnss_pal_impl_test.cc
+GOOGLETEST_PAL_IMPL_SRCS += $(CHRE_PREFIX)/pal/tests/src/sensor_pal_impl_test.cc
 GOOGLETEST_PAL_IMPL_SRCS += $(CHRE_PREFIX)/pal/tests/src/wifi_pal_impl_test.cc
-GOOGLETEST_PAL_IMPL_SRCS += $(CHRE_PREFIX)/pal/tests/src/sensor_pal_impl_test.cc
\ No newline at end of file
diff --git a/pal/tests/src/audio_pal_impl_test.cc b/pal/tests/src/audio_pal_impl_test.cc
new file mode 100644
index 0000000..2e7c67a
--- /dev/null
+++ b/pal/tests/src/audio_pal_impl_test.cc
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <cstdint>
+
+#include "chre/pal/audio.h"
+#include "chre/platform/condition_variable.h"
+#include "chre/platform/mutex.h"
+#include "chre/platform/shared/pal_system_api.h"
+#include "chre/util/lock_guard.h"
+#include "chre/util/macros.h"
+#include "chre/util/optional.h"
+#include "chre/util/time.h"
+#include "chre/util/unique_ptr.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+using ::chre::ConditionVariable;
+using ::chre::gChrePalSystemApi;
+using ::chre::kOneMillisecondInNanoseconds;
+using ::chre::LockGuard;
+using ::chre::MakeUnique;
+using ::chre::Mutex;
+using ::chre::Nanoseconds;
+using ::chre::Optional;
+using ::chre::UniquePtr;
+
+class Callbacks {
+ public:
+  void audioDataEventCallback(struct chreAudioDataEvent *event) {
+    LockGuard<Mutex> lock(mMutex);
+    if (!mDataEvent.has_value()) {
+      mDataEvent = event;
+      mCondVarDataEvents.notify_one();
+    }
+  }
+
+  void audioAvailabilityCallback(uint32_t handle, bool available) {
+    UNUSED_VAR(handle);
+    UNUSED_VAR(available);
+  }
+
+  Optional<struct chreAudioDataEvent *> mDataEvent;
+
+  //! Synchronize access to class members.
+  Mutex mMutex;
+  ConditionVariable mCondVarDataEvents;
+};
+
+UniquePtr<Callbacks> gCallbacks = nullptr;
+
+void audioDataEventCallback(struct chreAudioDataEvent *event) {
+  if (gCallbacks != nullptr) {
+    gCallbacks->audioDataEventCallback(event);
+  }
+}
+
+void audioAvailabilityCallback(uint32_t handle, bool available) {
+  if (gCallbacks != nullptr) {
+    gCallbacks->audioAvailabilityCallback(handle, available);
+  }
+}
+
+class PalAudioTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    gCallbacks = MakeUnique<Callbacks>();
+    mApi = chrePalAudioGetApi(CHRE_PAL_AUDIO_API_CURRENT_VERSION);
+    ASSERT_NE(mApi, nullptr);
+    EXPECT_EQ(mApi->moduleVersion, CHRE_PAL_AUDIO_API_CURRENT_VERSION);
+    ASSERT_TRUE(mApi->open(&gChrePalSystemApi, &mPalCallbacks));
+  }
+
+  void TearDown() override {
+    gCallbacks = nullptr;
+    if (mApi != nullptr) {
+      mApi->close();
+    }
+  }
+
+  //! CHRE PAL implementation API.
+  const struct chrePalAudioApi *mApi;
+
+  const struct chrePalAudioCallbacks mPalCallbacks = {
+      .audioDataEventCallback = audioDataEventCallback,
+      .audioAvailabilityCallback = audioAvailabilityCallback,
+  };
+};
+
+TEST_F(PalAudioTest, GetAudioSourceInfoForExistingSource) {
+  struct chreAudioSource audioSource;
+
+  EXPECT_EQ(mApi->getSourceCount(), 1);
+  EXPECT_TRUE(mApi->getAudioSource(0, &audioSource));
+  EXPECT_STREQ(audioSource.name, "Test Source");
+}
+
+TEST_F(PalAudioTest, GetAudioSourceInfoForNonExistingSource) {
+  struct chreAudioSource audioSource;
+
+  EXPECT_EQ(mApi->getSourceCount(), 1);
+  EXPECT_FALSE(mApi->getAudioSource(10, &audioSource));
+}
+
+TEST_F(PalAudioTest, GetDataEvent) {
+  EXPECT_TRUE(mApi->requestAudioDataEvent(0 /*handle*/, 1000 /*numSamples*/,
+                                          100 /*eventDelaysNs*/));
+
+  LockGuard<Mutex> lock(gCallbacks->mMutex);
+  gCallbacks->mCondVarDataEvents.wait_for(
+      gCallbacks->mMutex, Nanoseconds(kOneMillisecondInNanoseconds));
+  EXPECT_TRUE(gCallbacks->mDataEvent.has_value());
+  struct chreAudioDataEvent *event = gCallbacks->mDataEvent.value();
+  EXPECT_EQ(event->handle, 0);
+  EXPECT_EQ(event->sampleCount, 1000);
+
+  mApi->releaseAudioDataEvent(event);
+}
+
+}  // namespace
\ No newline at end of file
diff --git a/platform/linux/pal_audio.cc b/platform/linux/pal_audio.cc
new file mode 100644
index 0000000..b143522
--- /dev/null
+++ b/platform/linux/pal_audio.cc
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "chre/pal/audio.h"
+
+#include "chre/platform/memory.h"
+#include "chre/util/macros.h"
+#include "chre/util/memory.h"
+#include "chre/util/unique_ptr.h"
+
+#include <chrono>
+#include <cinttypes>
+#include <cstdint>
+#include <future>
+#include <thread>
+
+/**
+ * A simulated implementation of the audio PAL for the linux platform.
+ */
+namespace {
+const struct chrePalSystemApi *gSystemApi = nullptr;
+const struct chrePalAudioCallbacks *gCallbacks = nullptr;
+
+//! Thread to deliver asynchronous audio data after a CHRE request.
+std::thread gHandle0Thread;
+std::promise<void> gStopHandle0Thread;
+constexpr uint32_t kHandle0SampleRate = 16000;
+
+void stopHandle0Thread() {
+  if (gHandle0Thread.joinable()) {
+    gStopHandle0Thread.set_value();
+    gHandle0Thread.join();
+  }
+}
+
+void chrePalAudioApiClose(void) {
+  stopHandle0Thread();
+}
+
+bool chrePalAudioApiOpen(const struct chrePalSystemApi *systemApi,
+                         const struct chrePalAudioCallbacks *callbacks) {
+  chrePalAudioApiClose();
+
+  if (systemApi != nullptr && callbacks != nullptr) {
+    gSystemApi = systemApi;
+    gCallbacks = callbacks;
+    return true;
+  }
+
+  return false;
+}
+
+void sendHandle0Events(uint64_t delayNs, uint32_t numSamples) {
+  std::future<void> signal = gStopHandle0Thread.get_future();
+  if (signal.wait_for(std::chrono::nanoseconds(delayNs)) ==
+      std::future_status::timeout) {
+    auto data = chre::MakeUniqueZeroFill<struct chreAudioDataEvent>();
+
+    data->version = CHRE_AUDIO_DATA_EVENT_VERSION;
+    data->handle = 0;
+    data->timestamp = gSystemApi->getCurrentTime();
+    data->sampleRate = kHandle0SampleRate;
+    data->sampleCount = numSamples;
+    data->format = CHRE_AUDIO_DATA_FORMAT_8_BIT_U_LAW;
+    data->samplesULaw8 =
+        static_cast<const uint8_t *>(chre::memoryAlloc(numSamples));
+
+    gCallbacks->audioDataEventCallback(data.release());
+  }
+}
+
+bool chrePalAudioApiRequestAudioDataEvent(uint32_t handle, uint32_t numSamples,
+                                          uint64_t eventDelayNs) {
+  if (handle != 0) {
+    return false;
+  }
+
+  stopHandle0Thread();
+  if (numSamples > 0) {
+    gStopHandle0Thread = std::promise<void>();
+    gHandle0Thread = std::thread(sendHandle0Events, eventDelayNs, numSamples);
+  }
+
+  return true;
+}
+
+void chrePalAudioApiCancelAudioDataEvent(uint32_t handle) {
+  if (handle == 0) {
+    stopHandle0Thread();
+  }
+}
+
+void chrePalAudioApiReleaseAudioDataEvent(struct chreAudioDataEvent *event) {
+  if (event->format == CHRE_AUDIO_DATA_FORMAT_8_BIT_U_LAW) {
+    chre::memoryFree((void *)event->samplesULaw8);
+  } else if (event->format == CHRE_AUDIO_DATA_FORMAT_16_BIT_SIGNED_PCM) {
+    chre::memoryFree((void *)event->samplesS16);
+  }
+  chre::memoryFree(event);
+}
+
+uint32_t chrePalAudioApiGetSourceCount(void) {
+  return 1;
+}
+
+bool chrePalAudioApiGetAudioSource(uint32_t handle,
+                                   struct chreAudioSource *audioSource) {
+  if (handle != 0) {
+    return false;
+  }
+
+  *audioSource = {
+      .name = "Test Source",
+      .sampleRate = kHandle0SampleRate,
+      .minBufferDuration = 1,
+      .maxBufferDuration = 1000,
+      .format = CHRE_AUDIO_DATA_FORMAT_8_BIT_U_LAW,
+  };
+
+  return true;
+}
+
+}  // namespace
+
+const chrePalAudioApi *chrePalAudioGetApi(uint32_t requestedApiVersion) {
+  static const struct chrePalAudioApi kApi = {
+      .moduleVersion = CHRE_PAL_AUDIO_API_CURRENT_VERSION,
+      .open = chrePalAudioApiOpen,
+      .close = chrePalAudioApiClose,
+      .requestAudioDataEvent = chrePalAudioApiRequestAudioDataEvent,
+      .cancelAudioDataEvent = chrePalAudioApiCancelAudioDataEvent,
+      .releaseAudioDataEvent = chrePalAudioApiReleaseAudioDataEvent,
+      .getSourceCount = chrePalAudioApiGetSourceCount,
+      .getAudioSource = chrePalAudioApiGetAudioSource,
+  };
+
+  if (!CHRE_PAL_VERSIONS_ARE_COMPATIBLE(kApi.moduleVersion,
+                                        requestedApiVersion)) {
+    return nullptr;
+  } else {
+    return &kApi;
+  }
+}
\ No newline at end of file
diff --git a/platform/platform.mk b/platform/platform.mk
index 91ee4c2..3d14788 100644
--- a/platform/platform.mk
+++ b/platform/platform.mk
@@ -236,6 +236,12 @@
 SIM_SRCS += platform/shared/platform_gnss.cc
 endif
 
+# Optional sensor support.
+ifeq ($(CHRE_SENSORS_SUPPORT_ENABLED), true)
+SIM_SRCS += platform/linux/pal_sensor.cc
+SIM_SRCS += platform/shared/platform_sensor_manager.cc
+endif
+
 # Optional Wi-Fi support.
 ifeq ($(CHRE_WIFI_SUPPORT_ENABLED), true)
 ifeq ($(CHRE_WIFI_NAN_SUPPORT_ENABLED), true)
@@ -251,12 +257,6 @@
 SIM_SRCS += platform/shared/platform_wwan.cc
 endif
 
-# Optional sensor support.
-ifeq ($(CHRE_SENSORS_SUPPORT_ENABLED), true)
-SIM_SRCS += platform/linux/pal_sensor.cc
-SIM_SRCS += platform/shared/platform_sensor_manager.cc
-endif
-
 # Linux-specific Compiler Flags ################################################
 
 GOOGLE_X86_LINUX_CFLAGS += -Iplatform/linux/include