diff --git a/src/android/camera_buffer.h b/src/android/camera_buffer.h
new file mode 100644
index 000000000000..a1fb97a3c6db
--- /dev/null
+++ b/src/android/camera_buffer.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2021, Google Inc.
+ *
+ * camera_buffer.h - Frame buffer handling interface definition
+ */
+#ifndef __ANDROID_CAMERA_BUFFER_H__
+#define __ANDROID_CAMERA_BUFFER_H__
+
+#include <hardware/camera3.h>
+
+#include <libcamera/internal/buffer.h>
+
+class MappedCamera3Buffer : public libcamera::MappedBuffer
+{
+public:
+	MappedCamera3Buffer(const buffer_handle_t camera3buffer, int flags);
+	~MappedCamera3Buffer();
+};
+
+#endif /* __ANDROID_CAMERA_BUFFER_H__ */
diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
index 16cb8c6d2b84..a7a5b7986aa4 100644
--- a/src/android/camera_device.cpp
+++ b/src/android/camera_device.cpp
@@ -257,36 +257,6 @@ void sortCamera3StreamConfigs(std::vector<Camera3StreamConfig> &unsortedConfigs,
 
 } /* namespace */
 
-MappedCamera3Buffer::MappedCamera3Buffer(const buffer_handle_t camera3buffer,
-					 int flags)
-{
-	maps_.reserve(camera3buffer->numFds);
-	error_ = 0;
-
-	for (int i = 0; i < camera3buffer->numFds; i++) {
-		if (camera3buffer->data[i] == -1)
-			continue;
-
-		off_t length = lseek(camera3buffer->data[i], 0, SEEK_END);
-		if (length < 0) {
-			error_ = -errno;
-			LOG(HAL, Error) << "Failed to query plane length";
-			break;
-		}
-
-		void *address = mmap(nullptr, length, flags, MAP_SHARED,
-				     camera3buffer->data[i], 0);
-		if (address == MAP_FAILED) {
-			error_ = -errno;
-			LOG(HAL, Error) << "Failed to mmap plane";
-			break;
-		}
-
-		maps_.emplace_back(static_cast<uint8_t *>(address),
-				   static_cast<size_t>(length));
-	}
-}
-
 /*
  * \struct Camera3RequestDescriptor
  *
diff --git a/src/android/camera_device.h b/src/android/camera_device.h
index 9cbfcad38433..e6c192c2100b 100644
--- a/src/android/camera_device.h
+++ b/src/android/camera_device.h
@@ -24,17 +24,12 @@
 #include "libcamera/internal/log.h"
 #include "libcamera/internal/message.h"
 
+#include "camera_buffer.h"
 #include "camera_metadata.h"
 #include "camera_stream.h"
 #include "camera_worker.h"
 #include "jpeg/encoder.h"
 
-class MappedCamera3Buffer : public libcamera::MappedBuffer
-{
-public:
-	MappedCamera3Buffer(const buffer_handle_t camera3buffer, int flags);
-};
-
 class CameraDevice : protected libcamera::Loggable
 {
 public:
diff --git a/src/android/meson.build b/src/android/meson.build
index a13ce63b1d58..fd41c74f78ef 100644
--- a/src/android/meson.build
+++ b/src/android/meson.build
@@ -57,6 +57,8 @@ android_hal_sources = files([
     'yuv/post_processor_yuv.cpp'
 ])
 
+subdir('mm')
+
 android_camera_metadata_sources = files([
     'metadata/camera_metadata.c',
 ])
diff --git a/src/android/mm/android_generic_buffer.cpp b/src/android/mm/android_generic_buffer.cpp
new file mode 100644
index 000000000000..2504d9276e9e
--- /dev/null
+++ b/src/android/mm/android_generic_buffer.cpp
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2021, Google Inc.
+ *
+ * android_generic_buffer.cpp - Generic Android frame buffer backend
+ */
+
+#include "../camera_buffer.h"
+
+#include "libcamera/internal/log.h"
+
+using namespace libcamera;
+
+LOG_DECLARE_CATEGORY(HAL)
+
+MappedCamera3Buffer::MappedCamera3Buffer(const buffer_handle_t camera3buffer,
+					 int flags)
+{
+	maps_.reserve(camera3buffer->numFds);
+	error_ = 0;
+
+	for (int i = 0; i < camera3buffer->numFds; i++) {
+		if (camera3buffer->data[i] == -1)
+			continue;
+
+		off_t length = lseek(camera3buffer->data[i], 0, SEEK_END);
+		if (length < 0) {
+			error_ = -errno;
+			LOG(HAL, Error) << "Failed to query plane length";
+			break;
+		}
+
+		void *address = mmap(nullptr, length, flags, MAP_SHARED,
+				     camera3buffer->data[i], 0);
+		if (address == MAP_FAILED) {
+			error_ = -errno;
+			LOG(HAL, Error) << "Failed to mmap plane";
+			break;
+		}
+
+		maps_.emplace_back(static_cast<uint8_t *>(address),
+				   static_cast<size_t>(length));
+	}
+}
+
+MappedCamera3Buffer::~MappedCamera3Buffer()
+{
+}
diff --git a/src/android/mm/meson.build b/src/android/mm/meson.build
new file mode 100644
index 000000000000..39be8fec8567
--- /dev/null
+++ b/src/android/mm/meson.build
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: CC0-1.0
+
+memory_backend = get_option('android_memory_backend')
+if memory_backend == 'android_generic'
+    android_hal_sources += files(['android_generic_buffer.cpp'])
+endif
