diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index d2fbea488..d0a71a15b 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -2,6 +2,8 @@
 # SPDX-License-Identifier: GPL-2.0-or-later
 
 add_library(input_common STATIC
+    drivers/android.cpp
+    drivers/android.h
     drivers/camera.cpp
     drivers/camera.h
     drivers/keyboard.cpp
diff --git a/src/input_common/drivers/android.cpp b/src/input_common/drivers/android.cpp
new file mode 100644
index 000000000..b6a03fdc0
--- /dev/null
+++ b/src/input_common/drivers/android.cpp
@@ -0,0 +1,48 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "input_common/drivers/android.h"
+
+namespace InputCommon {
+
+Android::Android(std::string input_engine_) : InputEngine(std::move(input_engine_)) {}
+
+void Android::RegisterController(std::size_t controller_number) {
+    PreSetController(GetIdentifier(controller_number));
+}
+
+void Android::SetButtonState(std::size_t controller_number, int button_id, bool value) {
+    const auto identifier = GetIdentifier(controller_number);
+    SetButton(identifier, button_id, value);
+}
+
+void Android::SetAxisState(std::size_t controller_number, int axis_id, float value) {
+    const auto identifier = GetIdentifier(controller_number);
+    SetAxis(identifier, axis_id, value);
+}
+
+void Android::SetMotionState(std::size_t controller_number, u64 delta_timestamp, float gyro_x,
+                             float gyro_y, float gyro_z, float accel_x, float accel_y,
+                             float accel_z) {
+    const auto identifier = GetIdentifier(controller_number);
+    const BasicMotion motion_data{
+        .gyro_x = gyro_x,
+        .gyro_y = gyro_y,
+        .gyro_z = gyro_z,
+        .accel_x = accel_x,
+        .accel_y = accel_y,
+        .accel_z = accel_z,
+        .delta_timestamp = delta_timestamp,
+    };
+    SetMotion(identifier, 0, motion_data);
+}
+
+PadIdentifier Android::GetIdentifier(std::size_t controller_number) const {
+    return {
+        .guid = Common::UUID{},
+        .port = controller_number,
+        .pad = 0,
+    };
+}
+
+} // namespace InputCommon
diff --git a/src/input_common/drivers/android.h b/src/input_common/drivers/android.h
new file mode 100644
index 000000000..3f01817f6
--- /dev/null
+++ b/src/input_common/drivers/android.h
@@ -0,0 +1,54 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "input_common/input_engine.h"
+
+namespace InputCommon {
+
+/**
+ * A virtual controller that is always assigned to the game input
+ */
+class Android final : public InputEngine {
+public:
+    explicit Android(std::string input_engine_);
+
+    /**
+     * Registers controller number to accept new inputs
+     * @param controller_number the controller number that will take this action
+     */
+    void RegisterController(std::size_t controller_number);
+
+    /**
+     * Sets the status of all buttons bound with the key to pressed
+     * @param controller_number the controller number that will take this action
+     * @param button_id the id of the button
+     * @param value indicates if the button is pressed or not
+     */
+    void SetButtonState(std::size_t controller_number, int button_id, bool value);
+
+    /**
+     * Sets the status of a analog input to a specific player index
+     * @param controller_number the controller number that will take this action
+     * @param axis_id the id of the axis to move
+     * @param value the analog position of the axis
+     */
+    void SetAxisState(std::size_t controller_number, int axis_id, float value);
+
+    /**
+     * Sets the status of the motion sensor to a specific player index
+     * @param controller_number the controller number that will take this action
+     * @param delta_timestamp time passed since last reading
+     * @param gyro_x,gyro_y,gyro_z the gyro sensor readings
+     * @param accel_x,accel_y,accel_z the accelerometer reading
+     */
+    void SetMotionState(std::size_t controller_number, u64 delta_timestamp, float gyro_x,
+                        float gyro_y, float gyro_z, float accel_x, float accel_y, float accel_z);
+
+private:
+    /// Returns the correct identifier corresponding to the player index
+    PadIdentifier GetIdentifier(std::size_t controller_number) const;
+};
+
+} // namespace InputCommon
diff --git a/src/input_common/input_mapping.cpp b/src/input_common/input_mapping.cpp
index 8c2ee4eb3..f1a1d7398 100644
--- a/src/input_common/input_mapping.cpp
+++ b/src/input_common/input_mapping.cpp
@@ -210,6 +210,9 @@ bool MappingFactory::IsDriverValid(const MappingData& data) const {
     if (data.engine == "analog_from_button") {
         return false;
     }
+    if (data.engine == "virtual_gamepad") {
+        return false;
+    }
     return true;
 }
 
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index c77fc04ee..f8749ebbf 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -4,6 +4,7 @@
 #include <memory>
 #include "common/input.h"
 #include "common/param_package.h"
+#include "input_common/drivers/android.h"
 #include "input_common/drivers/camera.h"
 #include "input_common/drivers/keyboard.h"
 #include "input_common/drivers/mouse.h"
@@ -78,6 +79,7 @@ struct InputSubsystem::Impl {
         RegisterEngine("cemuhookudp", udp_client);
         RegisterEngine("tas", tas_input);
         RegisterEngine("camera", camera);
+        RegisterEngine("android", android);
         RegisterEngine("virtual_amiibo", virtual_amiibo);
         RegisterEngine("virtual_gamepad", virtual_gamepad);
 #ifdef HAVE_SDL2
@@ -109,6 +111,7 @@ struct InputSubsystem::Impl {
         UnregisterEngine(udp_client);
         UnregisterEngine(tas_input);
         UnregisterEngine(camera);
+        UnregisterEngine(android);
         UnregisterEngine(virtual_amiibo);
         UnregisterEngine(virtual_gamepad);
 #ifdef HAVE_SDL2
@@ -129,6 +132,8 @@ struct InputSubsystem::Impl {
         devices.insert(devices.end(), keyboard_devices.begin(), keyboard_devices.end());
         auto mouse_devices = mouse->GetInputDevices();
         devices.insert(devices.end(), mouse_devices.begin(), mouse_devices.end());
+        auto android_devices = android->GetInputDevices();
+        devices.insert(devices.end(), android_devices.begin(), android_devices.end());
 #ifdef HAVE_LIBUSB
         auto gcadapter_devices = gcadapter->GetInputDevices();
         devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end());
@@ -157,6 +162,9 @@ struct InputSubsystem::Impl {
         if (engine == mouse->GetEngineName()) {
             return mouse;
         }
+        if (engine == android->GetEngineName()) {
+            return android;
+        }
 #ifdef HAVE_LIBUSB
         if (engine == gcadapter->GetEngineName()) {
             return gcadapter;
@@ -237,6 +245,9 @@ struct InputSubsystem::Impl {
         if (engine == mouse->GetEngineName()) {
             return true;
         }
+        if (engine == android->GetEngineName()) {
+            return true;
+        }
 #ifdef HAVE_LIBUSB
         if (engine == gcadapter->GetEngineName()) {
             return true;
@@ -265,6 +276,7 @@ struct InputSubsystem::Impl {
     void BeginConfiguration() {
         keyboard->BeginConfiguration();
         mouse->BeginConfiguration();
+        android->BeginConfiguration();
 #ifdef HAVE_LIBUSB
         gcadapter->BeginConfiguration();
 #endif
@@ -278,6 +290,7 @@ struct InputSubsystem::Impl {
     void EndConfiguration() {
         keyboard->EndConfiguration();
         mouse->EndConfiguration();
+        android->EndConfiguration();
 #ifdef HAVE_LIBUSB
         gcadapter->EndConfiguration();
 #endif
@@ -308,6 +321,7 @@ struct InputSubsystem::Impl {
     std::shared_ptr<TasInput::Tas> tas_input;
     std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
     std::shared_ptr<Camera> camera;
+    std::shared_ptr<Android> android;
     std::shared_ptr<VirtualAmiibo> virtual_amiibo;
     std::shared_ptr<VirtualGamepad> virtual_gamepad;
 
@@ -373,6 +387,14 @@ const Camera* InputSubsystem::GetCamera() const {
     return impl->camera.get();
 }
 
+Android* InputSubsystem::GetAndroid() {
+    return impl->android.get();
+}
+
+const Android* InputSubsystem::GetAndroid() const {
+    return impl->android.get();
+}
+
 VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() {
     return impl->virtual_amiibo.get();
 }
diff --git a/src/input_common/main.h b/src/input_common/main.h
index d64a6cb4c..1d19019ee 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -29,6 +29,7 @@ enum Values : int;
 }
 
 namespace InputCommon {
+class Android;
 class Camera;
 class Keyboard;
 class Mouse;
@@ -103,6 +104,12 @@ public:
     /// Retrieves the underlying camera input device.
     [[nodiscard]] const Camera* GetCamera() const;
 
+    /// Retrieves the underlying android input device.
+    [[nodiscard]] Android* GetAndroid();
+
+    /// Retrieves the underlying android input device.
+    [[nodiscard]] const Android* GetAndroid() const;
+
     /// Retrieves the underlying virtual amiibo input device.
     [[nodiscard]] VirtualAmiibo* GetVirtualAmiibo();