Add emulated device ports

Bug: 137005763
Test: boot emulator, make a phone call
Signed-off-by: Roman Kiryanov <rkir@google.com>
Change-Id: Ibbb2574cf2e0d72a0bba6933a2425e7c153e9c38
Merged-In: Ibbb2574cf2e0d72a0bba6933a2425e7c153e9c38
diff --git a/audio/Android.bp b/audio/Android.bp
index b4bea6b..668a1e7 100644
--- a/audio/Android.bp
+++ b/audio/Android.bp
@@ -36,6 +36,7 @@
         "android.hardware.audio@6.0",
         "android.hardware.audio.common@6.0",
         "android.hardware.audio.common@6.0-util",
+        "libaudioutils",
         "libbase",
         "libcutils",
         "libhidlbase",
diff --git a/audio/device_port_sink.cpp b/audio/device_port_sink.cpp
index 4adde3c..8c17b6d 100644
--- a/audio/device_port_sink.cpp
+++ b/audio/device_port_sink.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <log/log.h>
 #include "device_port_sink.h"
 #include "talsa.h"
 #include "util.h"
@@ -67,20 +68,63 @@
         }
     }
 
+private:
     const uint32_t mSampleRateHz;
     util::StreamPosition mPos;
     talsa::PcmPtr mPcm;
 };
 
+struct NullSink : public DevicePortSink {
+    explicit NullSink(const AudioConfig &cfg)
+            : mSampleRateHz(cfg.sampleRateHz)
+            , mNChannels(util::countChannels(cfg.channelMask)) {}
+
+    Result getPresentationPosition(uint64_t &frames, TimeSpec &ts) const override {
+        nsecs_t t = 0;
+        mPos.now(mSampleRateHz, frames, t);
+        ts.tvSec = ns2s(t);
+        ts.tvNSec = t - s2ns(ts.tvSec);
+        return Result::OK;
+    }
+
+    int write(const void *, size_t nBytes) override {
+        mPos.addFrames(nBytes / mNChannels / sizeof(int16_t));
+        return nBytes;
+    }
+
+    static std::unique_ptr<NullSink> create(const AudioConfig &cfg) {
+        return std::make_unique<NullSink>(cfg);
+    }
+
+private:
+    const unsigned mSampleRateHz;
+    const unsigned mNChannels;
+    util::StreamPosition mPos;
+};
+
 }  // namespace
 
 std::unique_ptr<DevicePortSink>
 DevicePortSink::create(const DeviceAddress &address,
                        const AudioConfig &cfg,
                        const hidl_bitfield<AudioOutputFlag> &flags) {
-    (void)address;
     (void)flags;
-    return TinyalsaSink::create(talsa::kPcmCard, talsa::kPcmDevice, cfg);
+
+    if (cfg.format != AudioFormat::PCM_16_BIT) {
+        ALOGE("%s:%d Only PCM_16_BIT is supported", __func__, __LINE__);
+        return nullptr;
+    }
+
+    switch (address.device) {
+    case AudioDevice::OUT_SPEAKER:
+        return TinyalsaSink::create(talsa::kPcmCard, talsa::kPcmDevice, cfg);
+
+    case AudioDevice::OUT_TELEPHONY_TX:
+        return NullSink::create(cfg);
+
+    default:
+        return nullptr;
+    }
 }
 
 }  // namespace implementation
diff --git a/audio/device_port_source.cpp b/audio/device_port_source.cpp
index f3350e3..b084db6 100644
--- a/audio/device_port_source.cpp
+++ b/audio/device_port_source.cpp
@@ -14,10 +14,16 @@
  * limitations under the License.
  */
 
+#include <cmath>
+#include <chrono>
+#include <thread>
+#include <audio_utils/channels.h>
+#include <audio_utils/format.h>
+#include <log/log.h>
+#include <utils/Timers.h>
 #include "device_port_source.h"
 #include "talsa.h"
 #include "util.h"
-#include <cmath>
 
 namespace android {
 namespace hardware {
@@ -27,31 +33,43 @@
 
 namespace {
 
-struct TinyalsaSource : public DevicePortSource {
+struct CapturePositionDevicePortSource : public DevicePortSource {
+    explicit CapturePositionDevicePortSource(unsigned sampleRateHz)
+            : mSampleRateHz(sampleRateHz)
+            , mStartNs(systemTime(SYSTEM_TIME_MONOTONIC)) {}
+
+    Result getCapturePosition(uint64_t &frames, uint64_t &time) const override {
+        const nsecs_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
+        frames = getNowFrames(nowNs);
+        time = uint64_t(nowNs);
+        return Result::OK;
+    }
+
+    uint64_t getNowFrames(const nsecs_t nowNs) const {
+        return uint64_t(mSampleRateHz) * ns2ms(nowNs - mStartNs) / 1000;
+    }
+
+protected:
+    const unsigned mSampleRateHz;
+    const nsecs_t mStartNs;
+};
+
+struct TinyalsaSource : public CapturePositionDevicePortSource {
     TinyalsaSource(unsigned pcmCard, unsigned pcmDevice, const AudioConfig &cfg)
-            : sampleRateHz(cfg.sampleRateHz)
+            : CapturePositionDevicePortSource(cfg.sampleRateHz)
             , pcm(talsa::pcmOpen(pcmCard, pcmDevice,
                                  util::countChannels(cfg.channelMask),
                                  cfg.sampleRateHz,
                                  cfg.frameCount,
                                  false /* isOut */)) {}
 
-    Result getCapturePosition(uint64_t &frames, uint64_t &time) const override {
-        nsecs_t t = 0;
-        pos.now(sampleRateHz, frames, t);
-        time = t;
-        return Result::OK;
-    }
-
     int read(void *data, size_t toReadBytes) override {
         const int res = ::pcm_read(pcm.get(), data, toReadBytes);
         if (res < 0) {
             return res;
         } else if (res == 0) {
-            pos.addFrames(::pcm_bytes_to_frames(pcm.get(), toReadBytes));
             return toReadBytes;
         } else {
-            pos.addFrames(::pcm_bytes_to_frames(pcm.get(), res));
             return res;
         }
     }
@@ -67,20 +85,177 @@
         }
     }
 
-    const uint32_t sampleRateHz;
-    util::StreamPosition pos;
+private:
     talsa::PcmPtr pcm;
 };
 
+template <class G> struct GeneratedSource : public CapturePositionDevicePortSource {
+    GeneratedSource(const AudioConfig &cfg, G generator)
+            : CapturePositionDevicePortSource(cfg.sampleRateHz)
+            , mNChannels(util::countChannels(cfg.channelMask))
+            , mGenerator(std::move(generator)) {}
+
+    int read(void *data, size_t toReadBytes) override {
+        int16_t *samples = static_cast<int16_t *>(data);
+        const unsigned nChannels = mNChannels;
+        const unsigned requestedFrames = toReadBytes / nChannels / sizeof(*samples);
+
+        unsigned availableFrames;
+        while (true) {
+            const nsecs_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
+            availableFrames = getNowFrames(nowNs) - mSentFrames;
+            if (availableFrames < requestedFrames / 2) {
+                const unsigned neededMoreFrames = requestedFrames / 2 - availableFrames;
+
+                using namespace std::chrono_literals;
+                std::this_thread::sleep_for(1s * neededMoreFrames / mSampleRateHz);
+            } else {
+                break;
+            }
+        }
+
+        const unsigned nFrames = std::min(requestedFrames, availableFrames);
+        mGenerator(samples, nFrames);
+        const size_t sizeBytes = nFrames * nChannels * sizeof(*samples);
+        if (nChannels > 1) {
+            adjust_channels(samples, 1, samples, nChannels,
+                            sizeof(*samples), sizeBytes);
+        }
+
+        mSentFrames += nFrames;
+        return sizeBytes;
+    }
+
+private:
+    const unsigned mNChannels;
+    uint64_t mSentFrames = 0;
+    G mGenerator;
+};
+
+std::vector<int16_t> convertFloatsToInt16(const std::vector<float> &pcmFloat) {
+    std::vector<int16_t> pcmI16(pcmFloat.size());
+
+    memcpy_by_audio_format(pcmI16.data(),   AUDIO_FORMAT_PCM_16_BIT,
+                           pcmFloat.data(), AUDIO_FORMAT_PCM_FLOAT,
+                           pcmFloat.size());
+
+    return pcmI16;
+}
+
+// https://en.wikipedia.org/wiki/Busy_signal
+struct BusySignalGenerator {
+    explicit BusySignalGenerator(const uint32_t sampleRateHz) : mSampleRateHz(sampleRateHz) {
+        // 24/480 = 31/620, mValues must contain 50ms of audio samples
+        const size_t sz = sampleRateHz / 20;
+        std::vector<float> pcm(sz);
+        for (unsigned i = 0; i < sz; ++i) {
+            const double a = double(i) * M_PI * 2 / sampleRateHz;
+            pcm[i] = .5 * (sin(480 * a) + sin(620 * a));
+        }
+        mValues = convertFloatsToInt16(pcm);
+    }
+
+    void operator()(int16_t* s, size_t n) {
+        const unsigned rate = mSampleRateHz;
+        const unsigned rateHalf = rate / 2;
+        const int16_t *const vals = mValues.data();
+        const size_t valsSz = mValues.size();
+        size_t i = mI;
+
+        while (n > 0) {
+            size_t len;
+            if (i < rateHalf) {
+                const size_t valsOff = i % valsSz;
+                len = std::min(n, std::min(rateHalf - i, valsSz - valsOff));
+                memcpy(s, vals + valsOff, len * sizeof(*s));
+            } else {
+                len = std::min(n, rate - i);
+                memset(s, 0, len * sizeof(*s));
+            }
+            s += len;
+            i = (i + len) % rate;
+            n -= len;
+        }
+
+        mI = i;
+    }
+
+private:
+    const unsigned mSampleRateHz;
+    std::vector<int16_t> mValues;
+    size_t mI = 0;
+};
+
+struct RepeatGenerator {
+    explicit RepeatGenerator(const std::vector<float> &pcm)
+            : mValues(convertFloatsToInt16(pcm)) {}
+
+    void operator()(int16_t* s, size_t n) {
+        const int16_t *const vals = mValues.data();
+        const size_t valsSz = mValues.size();
+        size_t i = mI;
+
+        while (n > 0) {
+            const size_t len = std::min(n, valsSz - i);
+            memcpy(s, vals + i, len * sizeof(*s));
+            s += len;
+            i = (i + len) % valsSz;
+            n -= len;
+        }
+
+        mI = i;
+    }
+
+private:
+    const std::vector<int16_t> mValues;
+    size_t mI = 0;
+};
+
+std::vector<float> generateSinePattern(uint32_t sampleRateHz,
+                                       double freq,
+                                       double amp) {
+    std::vector<float> result(3 * sampleRateHz / freq + .5);
+
+    for (size_t i = 0; i < result.size(); ++i) {
+        const double a = double(i) * M_PI * 2 / sampleRateHz;
+        result[i] = amp * sin(a * freq);
+    }
+
+    return result;
+}
+
+template <class G> std::unique_ptr<GeneratedSource<G>>
+createGeneratedSource(const AudioConfig &cfg, G generator) {
+    return std::make_unique<GeneratedSource<G>>(cfg, std::move(generator));
+}
+
 }  // namespace
 
 std::unique_ptr<DevicePortSource>
 DevicePortSource::create(const DeviceAddress &address,
                          const AudioConfig &cfg,
                          const hidl_bitfield<AudioOutputFlag> &flags) {
-    (void)address;
     (void)flags;
-    return TinyalsaSource::create(talsa::kPcmCard, talsa::kPcmDevice, cfg);
+
+    if (cfg.format != AudioFormat::PCM_16_BIT) {
+        ALOGE("%s:%d Only PCM_16_BIT is supported", __func__, __LINE__);
+        return nullptr;
+    }
+
+    switch (address.device) {
+    case AudioDevice::IN_BUILTIN_MIC:
+        return TinyalsaSource::create(talsa::kPcmCard, talsa::kPcmDevice, cfg);
+
+    case AudioDevice::IN_TELEPHONY_RX:
+        return createGeneratedSource(cfg, BusySignalGenerator(cfg.sampleRateHz));
+
+    case AudioDevice::IN_FM_TUNER:
+        return createGeneratedSource(
+            cfg, RepeatGenerator(generateSinePattern(cfg.sampleRateHz, 440.0, 1.0)));
+
+    default:
+        return nullptr;
+    }
 }
 
 }  // namespace implementation
diff --git a/audio/policy/primary_audio_policy_configuration.xml b/audio/policy/primary_audio_policy_configuration.xml
index f9ac2c3..33a47d7 100644
--- a/audio/policy/primary_audio_policy_configuration.xml
+++ b/audio/policy/primary_audio_policy_configuration.xml
@@ -4,6 +4,9 @@
     <attachedDevices>
         <item>Speaker</item>
         <item>Built-In Mic</item>
+        <item>Telephony Tx</item>
+        <item>Telephony Rx</item>
+        <item>FM Tuner</item>
     </attachedDevices>
     <defaultOutputDevice>Speaker</defaultOutputDevice>
     <mixPorts>
@@ -15,18 +18,47 @@
             <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                      samplingRates="44100" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
         </mixPort>
+
+        <mixPort name="telephony_tx" role="source">
+            <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                     samplingRates="44100" channelMasks="AUDIO_CHANNEL_OUT_MONO,AUDIO_CHANNEL_OUT_STEREO"/>
+        </mixPort>
+        <mixPort name="telephony_rx" role="sink">
+            <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                     samplingRates="44100" channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO"/>
+        </mixPort>
+
+        <mixPort name="fm_tuner" role="sink">
+            <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                     samplingRates="44100" channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO"/>
+        </mixPort>
    </mixPorts>
    <devicePorts>
         <devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" role="sink">
         </devicePort>
+        <devicePort tagName="Telephony Tx" type="AUDIO_DEVICE_OUT_TELEPHONY_TX" role="sink">
+        </devicePort>
 
         <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
         </devicePort>
+        <devicePort tagName="Telephony Rx" type="AUDIO_DEVICE_IN_TELEPHONY_RX" role="source">
+        </devicePort>
+
+        <devicePort tagName="FM Tuner" type="AUDIO_DEVICE_IN_FM_TUNER" role="source">
+        </devicePort>
     </devicePorts>
     <routes>
         <route type="mix" sink="Speaker"
                sources="primary output"/>
         <route type="mix" sink="primary input"
                sources="Built-In Mic"/>
+
+        <route type="mix" sink="telephony_rx"
+               sources="Telephony Rx"/>
+        <route type="mix" sink="Telephony Tx"
+               sources="telephony_tx"/>
+
+        <route type="mix" sink="fm_tuner"
+               sources="FM Tuner"/>
     </routes>
 </module>