It is early morning. You’ve just pulled an all-nighter tinkering with an Android project over ADB. The sun is already creeping through the window, tests finally pass, and you unplug the USB cable from your OnePlus to finally go to bed.
You unlock your screen, swipe down to open Settings, and freeze.
Right across your menus is a block of bright-red monospace text:
isOverScrolling: false
X: FlingVX: 0.0, ClickVX: 0.0
Y: FlingVY: 15435.0, ClickVY: 0.0
AbortVX:0.0, AbortVY:15435.0
You stare at it, then touch the screen again. The numbers immediately jump, recalculating in real time with every movement of your thumb. You swipe fast, and FlingVY shoots into the tens of thousands.
Then you notice something even worse: the phone is lagging badly.
When you swipe from the screen edge to go back, the gesture stutters. Pulling out the OnePlus Smart Sidebar hesitates and drops frames. The smooth 120Hz display suddenly feels like a cheap phone struggling to keep up.
Your developer reflexes kick in immediately. You jump straight into Developer Options:
Everything is disabled.
Fine. You hold down the power button and reboot. The phone restarts, you unlock it, open Settings, scroll down—and the red numbers are still right there.
That's when you start to panic. You open your browser and start searching:
"red text on android screen": "OnePlus red text on screen": "OnePlus red text error": "isOverScrolling: false" OnePlus: "AbortVX" / "ClickVX": Zero results. Not a single thread on XDA Forums, not a mention on Reddit, and nothing on the OnePlus Community.
If you've worked with Android long enough, you know what this silence means: when an issue has zero documentation in AOSP and zero community threads, you're usually looking at a full factory reset.
At that point, the morning sun was already up. I had stayed up all night, I was running on zero sleep, completely exhausted, and my brain felt like an OutOfMemory error. I stared at the red numbers on my screen and made the most mature engineering decision possible:
I decided to go to sleep.
"You know what? This is future me's problem."
I put the phone face-down on the nightstand, closed my eyes, and hoped it would just magically disappear by the time I woke up.
Fast forward to later that afternoon: I woke up, tapped the screen, and nope—the red velocity numbers were still right there, tracking every single swipe while I was still half-asleep.
Now awake and ready to deal with it, I decided it was time to bring in serious backup.
I plugged the USB cable back into my laptop. I opened my Antigravity IDE where I had been pair-programming with Gemini, and typed out of pure frustration:
"Hey, you ran some ADB commands on my phone earlier for some app testing and now there's this weird red text on my screen and the edges are lagging. I plugged it in—fix it."
If this were a standard chatbot, you already know what its first response would be:
"Go to Settings > Developer Options and disable 'Pointer Location', 'Show Layout Bounds', or 'Strict Mode'. If those are already off, try backing up your data and performing a factory reset."
Every standard LLM defaults to that exact script because it assumes you're dealing with standard Android toggles. And when you tell it they're already off, it hits a complete dead end.
Instead, Gemini took the wheel. What happened on my terminal over the next few minutes was impressive: rather than giving boilerplate advice, Gemini started systematically diagnosing the operating system from the inside out.
Gemini didn't guess. It queried the device's runtime settings via ADB:
adb shell settings get system pointer_location
adb shell settings get system show_touches
Both came back 0. Standard Android touch telemetry was definitively off.
Then Gemini dumped the entire live UI hierarchy of the Settings app:
adb shell uiautomator dump /sdcard/window_dump.xml
It grepped the XML tree for isOverScrolling. The result? Empty.
That silence told Gemini everything:
"This text does not exist as a TextView or a system overlay window. It is being painted directly onto the hardware Canvas buffer during draw passes by an internal UI component."
Because the text appeared during scrolling in OnePlus system menus, Gemini formed an immediate hypothesis: the code belonged to OnePlus/OPPO’s proprietary UI toolkit—COUI (ColorOS UI).
Because these OEM classes are completely proprietary and unreleased, Gemini couldn't look up documentation. It had to read the machine code directly from the phone.
To inspect the COUI framework without needing root access, Gemini pulled a built-in pre-installed app that uses the same UI components:
adb pull /product/app/Calculator2/Calculator2.apk ./calc.apk
Then, right inside my terminal, Gemini wrote and executed Python scripts to parse the raw binary DEX (Dalvik Executable) structures of the APK.
Gemini parsed the DEX string pool inside calc.apk and found the exact IDs:
#2600 = 'AbortVX:'
#37919 = 'isOverScrolling: '
It mapped those string IDs across every class definition in the binary. In seconds, Gemini had the exact location:
androidx.recyclerview.widget.COUIRecyclerView (OnePlus’s internal extension of the Jetpack RecyclerView)dispatchDraw(Canvas canvas) at bytecode offset 0x1fbea0
Gemini disassembled the opcodes at that offset. The bytecode showed exactly what was going on under the hood:
0x1fbea0: invoke-virtual View->dispatchDraw
0x1fbea6: sget COUIRecyclerView->COUI_DEBUG:Z // Check static boolean flag
0x1fbeaa: if-eqz -> 0x1fc03c // If false, skip entirely!
0x1fbeae: iget COUIRecyclerView->mDebugPaint // Get Paint object
0x1fbec4: invoke-virtual Paint->setColor // Set paint color to RED
0x1fbed4: const-string 'isOverScrolling: ' // Format velocity strings
0x1fbf2a: const-string 'X: FlingVX: '
0x1fbf86: const-string 'Y: FlingVY: '
0x1fbfe4: const-string 'AbortVX:'
0x1fc036: invoke-virtual Canvas->drawText // Draw directly on screen!
There it was: an undocumented, internal scroll-physics debugger built by OEM engineers to measure fling velocities during ROM development.
Gemini now knew what was painting the screen, but why had COUI_DEBUG suddenly turned true across my entire phone?
Gemini inspected the class initializer <clinit> of COUIRecyclerView:
0x1fb9bc: const-string 'COUIRecyclerView'
0x1fb9c2: invoke-static COUILog->isLoggable("COUIRecyclerView", Log.DEBUG)
0x1fb9d6: sput COUIRecyclerView->COUI_DEBUG:Z
COUILog.isLoggable() simply calls Android's built-in android.util.Log.isLoggable(tag, level).
In Android, Log.isLoggable() first checks for a tag-specific system property (log.tag.<TAG>). If that doesn't exist, it checks the global fallback property:
🎯 The Culprit:
persist.log.tag
Gemini queried the phone:
adb shell getprop persist.log.tag
The terminal printed one character:
V
Hours earlier, while debugging an audio service over ADB, a command had set persist.log.tag to V (Verbose) to inspect background audio logs.
That one command triggered an unexpected chain reaction:
persist. are saved directly to flash storage (/data/property). A reboot does not wipe them. COUIRecyclerView checked if debug logging was enabled. Because persist.log.tag was set globally to V, Android replied that everything was in verbose mode. COUI_DEBUG became true, and the red velocity overlay activated.
Once Gemini identified the exact root cause, the fix was quick and clean:
Gemini reset the dangerous persistent global log property to empty:
adb shell setprop persist.log.tag ""
To make sure COUIRecyclerView could never wake up its debug drawing again—even if global verbose logging were accidentally enabled in a future project—Gemini injected a permanent tag-level suppression rule:
adb shell setprop persist.log.tag.COUIRecyclerView SUPPRESS
Gemini killed the Settings process to immediately test the fix on the current window:
adb shell am force-stop com.android.settings
Gemini triggered an automated swipe gesture through ADB, captured a live screenshot, and verified that the red text had vanished from Settings.
While Settings was fixed immediately, Gemini pointed out a crucial Android runtime mechanic:
In the Dalvik/ART virtual machine, static initializers (<clinit>) run only once when a class is first loaded into a process. That meant any app or system service already running in RAM (like the Launcher, SystemUI, or Contacts) still held COUI_DEBUG = true cached in memory.
To guarantee that every background process, system daemon, and service reloaded cleanly from scratch with the new suppressed properties, we performed a fresh device reboot:
adb reboot
Once the phone rebooted, I picked it up, unlocked it, and scrolled through every corner of the system:
As a Computer & Systems Engineering student, I usually take AI hype with a grain of salt. I understand how systems work under the hood, and I know how complex and messy OEM Android frameworks get when low-level state gets corrupted.
If I had posted this on a forum or asked a standard chatbot, the advice would have been the same generic response: back up your data and factory reset.
Watching Gemini handle this live in my terminal was genuinely impressive. It didn't guess, and it didn't give generic tips. It followed a solid systems debugging process:
sget instruction controlling the overlay.
Watching an AI autonomously reverse-engineer compiled bytecode on a live device—and solve an undocumented framework issue in minutes without touching a single byte of my personal data—was amazing to see.
This isn't just about autocompleting syntax or generating boilerplate code anymore. This is a real glimpse into the future of systems engineering—and as an engineering student, it completely blew me away.