diff --git a/meta-oe/recipes-devtools/android-tools/android-tools/0021-liblog-optionally-read-ro.debuggable-from-build.prop.patch b/meta-oe/recipes-devtools/android-tools/android-tools/0021-liblog-optionally-read-ro.debuggable-from-build.prop.patch
new file mode 100644
index 000000000000..6cf4f9c8583e
--- /dev/null
+++ b/meta-oe/recipes-devtools/android-tools/android-tools/0021-liblog-optionally-read-ro.debuggable-from-build.prop.patch
@@ -0,0 +1,117 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Alex Kiernan <alex.kiernan@gmail.com>
+Date: Fri, 6 Nov 2026 09:38:18 +0000
+Subject: [PATCH] liblog: optionally read ro.debuggable from build.prop-style
+ files
+
+The non-Android stub of __android_log_is_debuggable() returns 1, so on a
+Linux adbd every build is debuggable: `adb root` and the root: service are
+always allowed, and nothing can tell a development image from a production
+one.
+
+When LIBLOG_DEBUGGABLE_PROP_FILES is passed to make - a colon-separated
+list of build.prop-style files - read ro.debuggable from them instead. The build is
+debuggable if any of them has "ro.debuggable=1", and not otherwise,
+including when none of them exists. The files are read on each call rather
+than latched, so one written after the process started (a runtime unlock on
+tmpfs, say) takes effect without a restart.
+
+Without the define nothing changes.
+
+Upstream-Status: Pending
+
+AI-Generated: Claude Opus 5.5 (Claude Code)
+Signed-off-by: Alex Kiernan <alex.kiernan@gmail.com>
+---
+ debian/system/liblog.mk              |  4 +++
+ system/logging/liblog/properties.cpp | 52 ++++++++++++++++++++++++++++
+ 2 files changed, 56 insertions(+)
+
+diff --git a/debian/system/liblog.mk b/debian/system/liblog.mk
+index 091ccad19c3a..89027bcebacf 100644
+--- a/debian/system/liblog.mk
++++ b/debian/system/liblog.mk
+@@ -26,6 +26,10 @@ CPPFLAGS += \
+   -Isystem/libbase/include \
+   -Isystem/logging/liblog/include \
+ 
++ifneq ($(LIBLOG_DEBUGGABLE_PROP_FILES),)
++CPPFLAGS += -DLIBLOG_DEBUGGABLE_PROP_FILES='"$(LIBLOG_DEBUGGABLE_PROP_FILES)"'
++endif
++
+ LDFLAGS += \
+   -Wl,-rpath='$$ORIGIN' \
+   -Wl,-soname,$(NAME).so.0 \
+diff --git a/system/logging/liblog/properties.cpp b/system/logging/liblog/properties.cpp
+index 657901a34e44..f069ebc4eb22 100644
+--- a/system/logging/liblog/properties.cpp
++++ b/system/logging/liblog/properties.cpp
+@@ -17,7 +17,9 @@
+ #include <log/log_properties.h>
+ 
+ #include <ctype.h>
++#include <limits.h>
+ #include <pthread.h>
++#include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <unistd.h>
+@@ -311,8 +313,58 @@ int __android_log_is_loggable_len(int prio, const char*, size_t, int def) {
+   return __android_log_is_loggable(prio, nullptr, def);
+ }
+ 
++#if defined(LIBLOG_DEBUGGABLE_PROP_FILES)
++// True only if the build.prop-style file at path has the line "<key>=1".
++// Anything else, including the file being absent or unreadable, is false.
++static bool prop_file_is_true(const char* path, size_t pathlen, const char* key) {
++  char buf[PATH_MAX];
++  if (pathlen == 0 || pathlen >= sizeof(buf)) {
++    return false;
++  }
++  memcpy(buf, path, pathlen);
++  buf[pathlen] = '\0';
++  FILE* f = fopen(buf, "re");
++  if (f == nullptr) {
++    return false;
++  }
++  const size_t keylen = strlen(key);
++  char line[256];
++  bool value = false;
++  while (fgets(line, sizeof(line), f) != nullptr) {
++    if (strncmp(line, key, keylen) == 0 && line[keylen] == '=') {
++      const char* v = line + keylen + 1;
++      value = v[0] == '1' && (v[1] == '\0' || v[1] == '\n' || v[1] == '\r');
++      break;
++    }
++  }
++  fclose(f);
++  return value;
++}
++#endif
++
+ int __android_log_is_debuggable() {
++#if defined(LIBLOG_DEBUGGABLE_PROP_FILES)
++  // There is no property service off Android. Read ro.debuggable from a
++  // colon-separated list of build.prop-style files instead, and treat the
++  // build as debuggable if any of them sets it to 1; absent all of them, it is
++  // not. Read on every call, not latched: a file may be written after the
++  // process starts, and the callers are occasional.
++  const char* paths = LIBLOG_DEBUGGABLE_PROP_FILES;
++  while (*paths != '\0') {
++    const char* end = strchr(paths, ':');
++    const size_t len = end != nullptr ? static_cast<size_t>(end - paths) : strlen(paths);
++    if (prop_file_is_true(paths, len, "ro.debuggable")) {
++      return 1;
++    }
++    if (end == nullptr) {
++      break;
++    }
++    paths = end + 1;
++  }
++  return 0;
++#else
+   return 1;
++#endif
+ }
+ 
+ int __android_log_security() {
diff --git a/meta-oe/recipes-devtools/android-tools/android-tools_35.0.2.bb b/meta-oe/recipes-devtools/android-tools/android-tools_35.0.2.bb
index f5ab21262ce5..d3d60b514721 100644
--- a/meta-oe/recipes-devtools/android-tools/android-tools_35.0.2.bb
+++ b/meta-oe/recipes-devtools/android-tools/android-tools_35.0.2.bb
@@ -65,6 +65,7 @@ SRC_URI = "https://deb.debian.org/debian/pool/main/a/android-platform-tools/andr
            file://0018-adbd-do-not-use-pthread_kill-as-a-thread-liveness-pr.patch \
            file://0019-adb-daemon-exit-after-adb-root-unroot-so-an-init-sup.patch \
            file://0020-adbd-signal-readiness-once-functionfs-is-serviced.patch \
+           file://0021-liblog-optionally-read-ro.debuggable-from-build.prop.patch \
            "
 
 SRC_URI[orig.md5sum] = "352376965cdef7bd7505d8fefdd43d50"
@@ -208,7 +209,8 @@ do_compile() {
     # Debian replaces Android's Soong build system with hand-written GNU make
     # fragments under debian/system/ and debian/external/.
     for tool in ${PREREQUISITE_core}; do
-        oe_runmake -f ${S}/debian/system/${tool}.mk -C ${S}
+        oe_runmake -f ${S}/debian/system/${tool}.mk -C ${S} \
+            ${@'LIBLOG_DEBUGGABLE_PROP_FILES=' + d.getVar('ADB_DEBUGGABLE_PROP_FILES') if bb.utils.contains('PACKAGECONFIG', 'debuggable-prop', True, False, d) else ''}
     done
 
     if echo " ${TOOLS_TO_BUILD} " | grep -q " fastboot "; then
@@ -266,8 +268,13 @@ FILES:${PN}-adbd = " \
 BBCLASSEXTEND = "native"
 
 PACKAGECONFIG ??= "${@bb.utils.filter('DISTRO_FEATURES', 'systemd', d)}"
+ADB_DEBUGGABLE_PROP_FILES ?= "${sysconfdir}/build.prop"
 # Apply Android's fs_config ownership/mode tables to files pushed by adbd to
 # /system, /vendor and /oem. Off by default: the tables describe Android
 # partition images and are meaningless on a generic Linux rootfs.
 PACKAGECONFIG[fs-config] = ""
+# Read ro.debuggable from ADB_DEBUGGABLE_PROP_FILES (colon-separated,
+# build.prop format), so `adb root` is refused unless one of them sets
+# ro.debuggable=1. Off by default: every build is debuggable.
+PACKAGECONFIG[debuggable-prop] = ""
 PACKAGECONFIG[systemd] = ",,systemd"
