diff --git a/CMakeLists.txt b/CMakeLists.txt
index 18b8f7967d..9dfc06ac37 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -142,6 +142,9 @@ if (YUZU_USE_BUNDLED_VCPKG)
     if (ENABLE_WEB_SERVICE)
         list(APPEND VCPKG_MANIFEST_FEATURES "web-service")
     endif()
+    if (ANDROID)
+        list(APPEND VCPKG_MANIFEST_FEATURES "android")
+    endif()
 
     include(${CMAKE_SOURCE_DIR}/externals/vcpkg/scripts/buildsystems/vcpkg.cmake)
 elseif(NOT "$ENV{VCPKG_TOOLCHAIN_FILE}" STREQUAL "")
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index ab435dce99..e3915ef4f7 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -256,11 +256,13 @@
 
     <string-array name="outputEngineEntries">
         <item>@string/auto</item>
+        <item>@string/oboe</item>
         <item>@string/cubeb</item>
         <item>@string/string_null</item>
     </string-array>
     <integer-array name="outputEngineValues">
         <item>0</item>
+        <item>4</item>
         <item>1</item>
         <item>3</item>
     </integer-array>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index c86c43df24..0b80b04a49 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -503,6 +503,7 @@
     <string name="theme_mode_dark">Dark</string>
 
     <!-- Audio output engines -->
+    <string name="oboe">oboe</string>
     <string name="cubeb">cubeb</string>
 
     <!-- Black backgrounds theme -->
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 400988c5fb..e982d03bef 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -253,6 +253,17 @@ if (ENABLE_SDL2)
     target_compile_definitions(audio_core PRIVATE HAVE_SDL2)
 endif()
 
+if (ANDROID)
+    target_sources(audio_core PRIVATE
+        sink/oboe_sink.cpp
+        sink/oboe_sink.h
+    )
+
+    # FIXME: this port seems broken, it cannot be imported with find_package(oboe REQUIRED)
+    target_link_libraries(audio_core PRIVATE "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/lib/liboboe.a")
+    target_compile_definitions(audio_core PRIVATE HAVE_OBOE)
+endif()
+
 if (YUZU_USE_PRECOMPILED_HEADERS)
     target_precompile_headers(audio_core PRIVATE precompiled_headers.h)
 endif()
diff --git a/src/audio_core/sink/oboe_sink.cpp b/src/audio_core/sink/oboe_sink.cpp
new file mode 100644
index 0000000000..e618411722
--- /dev/null
+++ b/src/audio_core/sink/oboe_sink.cpp
@@ -0,0 +1,223 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <span>
+#include <vector>
+
+#include <oboe/Oboe.h>
+
+#include "audio_core/common/common.h"
+#include "audio_core/sink/oboe_sink.h"
+#include "audio_core/sink/sink_stream.h"
+#include "common/logging/log.h"
+#include "common/scope_exit.h"
+#include "core/core.h"
+
+namespace AudioCore::Sink {
+
+class OboeSinkStream final : public SinkStream,
+                             public oboe::AudioStreamDataCallback,
+                             public oboe::AudioStreamErrorCallback {
+public:
+    explicit OboeSinkStream(Core::System& system_, StreamType type_, const std::string& name_,
+                            u32 system_channels_)
+        : SinkStream(system_, type_) {
+        name = name_;
+        system_channels = system_channels_;
+
+        this->OpenStream();
+    }
+
+    ~OboeSinkStream() override {
+        LOG_INFO(Audio_Sink, "Destroyed Oboe stream");
+    }
+
+    void Finalize() override {
+        this->Stop();
+        m_stream.reset();
+    }
+
+    void Start(bool resume = false) override {
+        if (!m_stream || !paused) {
+            return;
+        }
+
+        paused = false;
+
+        if (m_stream->start() != oboe::Result::OK) {
+            LOG_CRITICAL(Audio_Sink, "Error starting Oboe stream");
+        }
+    }
+
+    void Stop() override {
+        if (!m_stream || paused) {
+            return;
+        }
+
+        this->SignalPause();
+
+        if (m_stream->stop() != oboe::Result::OK) {
+            LOG_CRITICAL(Audio_Sink, "Error stopping Oboe stream");
+        }
+    }
+
+public:
+    static s32 QueryChannelCount(oboe::Direction direction) {
+        std::shared_ptr<oboe::AudioStream> temp_stream;
+        oboe::AudioStreamBuilder builder;
+
+        const auto result = ConfigureBuilder(builder, direction)->openStream(temp_stream);
+        ASSERT(result == oboe::Result::OK);
+
+        return temp_stream->getChannelCount() >= 6 ? 6 : 2;
+    }
+
+protected:
+    oboe::DataCallbackResult onAudioReady(oboe::AudioStream*, void* audio_data,
+                                          s32 num_buffer_frames) override {
+        const size_t num_channels = this->GetDeviceChannels();
+        const size_t frame_size = num_channels;
+        const size_t num_frames = static_cast<size_t>(num_buffer_frames);
+
+        if (type == StreamType::In) {
+            std::span<const s16> input_buffer{reinterpret_cast<const s16*>(audio_data),
+                                              num_frames * frame_size};
+            this->ProcessAudioIn(input_buffer, num_frames);
+        } else {
+            std::span<s16> output_buffer{reinterpret_cast<s16*>(audio_data),
+                                         num_frames * frame_size};
+            this->ProcessAudioOutAndRender(output_buffer, num_frames);
+        }
+
+        return oboe::DataCallbackResult::Continue;
+    }
+
+    void onErrorAfterClose(oboe::AudioStream*, oboe::Result) override {
+        LOG_INFO(Audio_Sink, "Audio stream closed, reinitializing");
+
+        if (this->OpenStream()) {
+            m_stream->start();
+        }
+    }
+
+private:
+    static oboe::AudioStreamBuilder* ConfigureBuilder(oboe::AudioStreamBuilder& builder,
+                                                      oboe::Direction direction) {
+        // TODO: investigate callback delay issues when using AAudio
+        return builder.setPerformanceMode(oboe::PerformanceMode::LowLatency)
+            ->setAudioApi(oboe::AudioApi::OpenSLES)
+            ->setDirection(direction)
+            ->setSampleRate(TargetSampleRate)
+            ->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::High)
+            ->setFormat(oboe::AudioFormat::I16)
+            ->setFormatConversionAllowed(true)
+            ->setUsage(oboe::Usage::Game)
+            ->setBufferCapacityInFrames(TargetSampleCount * 2);
+    }
+
+    bool OpenStream() {
+        const auto direction = [&]() {
+            switch (type) {
+            case StreamType::In:
+                return oboe::Direction::Input;
+            case StreamType::Out:
+            case StreamType::Render:
+                return oboe::Direction::Output;
+            default:
+                ASSERT(false);
+                return oboe::Direction::Output;
+            }
+        }();
+
+        const auto expected_channels = QueryChannelCount(direction);
+        const auto expected_mask = [&]() {
+            switch (expected_channels) {
+            case 1:
+                return oboe::ChannelMask::Mono;
+            case 2:
+                return oboe::ChannelMask::Stereo;
+            case 6:
+                return oboe::ChannelMask::CM5Point1;
+            default:
+                ASSERT(false);
+                return oboe::ChannelMask::Unspecified;
+            }
+        }();
+
+        oboe::AudioStreamBuilder builder;
+        const auto result = ConfigureBuilder(builder, direction)
+                                ->setChannelCount(expected_channels)
+                                ->setChannelMask(expected_mask)
+                                ->setChannelConversionAllowed(true)
+                                ->setDataCallback(this)
+                                ->setErrorCallback(this)
+                                ->openStream(m_stream);
+        ASSERT(result == oboe::Result::OK);
+        return result == oboe::Result::OK && this->SetStreamProperties();
+    }
+
+    bool SetStreamProperties() {
+        ASSERT(m_stream);
+
+        m_stream->setBufferSizeInFrames(TargetSampleCount * 2);
+        device_channels = m_stream->getChannelCount();
+
+        const auto sample_rate = m_stream->getSampleRate();
+        const auto buffer_capacity = m_stream->getBufferCapacityInFrames();
+        const auto stream_backend =
+            m_stream->getAudioApi() == oboe::AudioApi::AAudio ? "AAudio" : "OpenSLES";
+
+        LOG_INFO(Audio_Sink, "Opened Oboe {} stream with {} channels sample rate {} capacity {}",
+                 stream_backend, device_channels, sample_rate, buffer_capacity);
+
+        return true;
+    }
+
+    std::shared_ptr<oboe::AudioStream> m_stream{};
+};
+
+OboeSink::OboeSink() {
+    // TODO: This is not generally knowable
+    // The channel count is distinct based on direction and can change
+    device_channels = OboeSinkStream::QueryChannelCount(oboe::Direction::Output);
+}
+
+OboeSink::~OboeSink() = default;
+
+SinkStream* OboeSink::AcquireSinkStream(Core::System& system, u32 system_channels,
+                                        const std::string& name, StreamType type) {
+    SinkStreamPtr& stream = sink_streams.emplace_back(
+        std::make_unique<OboeSinkStream>(system, type, name, system_channels));
+
+    return stream.get();
+}
+
+void OboeSink::CloseStream(SinkStream* to_remove) {
+    sink_streams.remove_if([&](auto& stream) { return stream.get() == to_remove; });
+}
+
+void OboeSink::CloseStreams() {
+    sink_streams.clear();
+}
+
+f32 OboeSink::GetDeviceVolume() const {
+    if (sink_streams.empty()) {
+        return 1.0f;
+    }
+
+    return sink_streams.front()->GetDeviceVolume();
+}
+
+void OboeSink::SetDeviceVolume(f32 volume) {
+    for (auto& stream : sink_streams) {
+        stream->SetDeviceVolume(volume);
+    }
+}
+
+void OboeSink::SetSystemVolume(f32 volume) {
+    for (auto& stream : sink_streams) {
+        stream->SetSystemVolume(volume);
+    }
+}
+
+} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/oboe_sink.h b/src/audio_core/sink/oboe_sink.h
new file mode 100644
index 0000000000..8f6f54ab53
--- /dev/null
+++ b/src/audio_core/sink/oboe_sink.h
@@ -0,0 +1,75 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <list>
+#include <string>
+
+#include "audio_core/sink/sink.h"
+
+namespace Core {
+class System;
+}
+
+namespace AudioCore::Sink {
+class SinkStream;
+
+class OboeSink final : public Sink {
+public:
+    explicit OboeSink();
+    ~OboeSink() override;
+
+    /**
+     * Create a new sink stream.
+     *
+     * @param system          - Core system.
+     * @param system_channels - Number of channels the audio system expects.
+     *                          May differ from the device's channel count.
+     * @param name            - Name of this stream.
+     * @param type            - Type of this stream, render/in/out.
+     *
+     * @return A pointer to the created SinkStream
+     */
+    SinkStream* AcquireSinkStream(Core::System& system, u32 system_channels,
+                                  const std::string& name, StreamType type) override;
+
+    /**
+     * Close a given stream.
+     *
+     * @param stream - The stream to close.
+     */
+    void CloseStream(SinkStream* stream) override;
+
+    /**
+     * Close all streams.
+     */
+    void CloseStreams() override;
+
+    /**
+     * Get the device volume. Set from calls to the IAudioDevice service.
+     *
+     * @return Volume of the device.
+     */
+    f32 GetDeviceVolume() const override;
+
+    /**
+     * Set the device volume. Set from calls to the IAudioDevice service.
+     *
+     * @param volume - New volume of the device.
+     */
+    void SetDeviceVolume(f32 volume) override;
+
+    /**
+     * Set the system volume. Comes from the audio system using this stream.
+     *
+     * @param volume - New volume of the system.
+     */
+    void SetSystemVolume(f32 volume) override;
+
+private:
+    /// List of streams managed by this sink
+    std::list<SinkStreamPtr> sink_streams{};
+};
+
+} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/sink_details.cpp b/src/audio_core/sink/sink_details.cpp
index 7c9a4e3acf..449af659d0 100644
--- a/src/audio_core/sink/sink_details.cpp
+++ b/src/audio_core/sink/sink_details.cpp
@@ -7,6 +7,9 @@
 #include <vector>
 
 #include "audio_core/sink/sink_details.h"
+#ifdef HAVE_OBOE
+#include "audio_core/sink/oboe_sink.h"
+#endif
 #ifdef HAVE_CUBEB
 #include "audio_core/sink/cubeb_sink.h"
 #endif
@@ -36,6 +39,16 @@ struct SinkDetails {
 
 // sink_details is ordered in terms of desirability, with the best choice at the top.
 constexpr SinkDetails sink_details[] = {
+#ifdef HAVE_OBOE
+    SinkDetails{
+        Settings::AudioEngine::Oboe,
+        [](std::string_view device_id) -> std::unique_ptr<Sink> {
+            return std::make_unique<OboeSink>();
+        },
+        [](bool capture) { return std::vector<std::string>{"Default"}; },
+        []() { return true; },
+    },
+#endif
 #ifdef HAVE_CUBEB
     SinkDetails{
         Settings::AudioEngine::Cubeb,
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h
index d6351e57e9..6170365886 100644
--- a/src/common/settings_enums.h
+++ b/src/common/settings_enums.h
@@ -82,16 +82,15 @@ enum class AudioEngine : u32 {
     Cubeb,
     Sdl2,
     Null,
+    Oboe,
 };
 
 template <>
 inline std::vector<std::pair<std::string, AudioEngine>>
 EnumMetadata<AudioEngine>::Canonicalizations() {
     return {
-        {"auto", AudioEngine::Auto},
-        {"cubeb", AudioEngine::Cubeb},
-        {"sdl2", AudioEngine::Sdl2},
-        {"null", AudioEngine::Null},
+        {"auto", AudioEngine::Auto}, {"cubeb", AudioEngine::Cubeb}, {"sdl2", AudioEngine::Sdl2},
+        {"null", AudioEngine::Null}, {"oboe", AudioEngine::Oboe},
     };
 }
 
diff --git a/vcpkg.json b/vcpkg.json
index 01a4657d41..a9e895153e 100644
--- a/vcpkg.json
+++ b/vcpkg.json
@@ -41,6 +41,15 @@
                     "platform": "windows"
                 }
             ]
+        },
+        "android": {
+            "description": "Enable Android dependencies",
+            "dependencies": [
+                {
+                    "name": "oboe",
+                    "platform": "android"
+                }
+            ]
         }
     },
     "overrides": [