My Phone Broke, and Gemini Fixed It
📱 Picture This: An All-Nighter Turns Into a Morning Nightmare 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. 🥶 Zero Results: When Google Goes Completely Silent Your developer reflexes kick in immediately. You jump straight into Developer Options: Pointer Location? Off. Show Layout Bounds? Off. Show Taps? Off. Strict Mode? Off. 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": Generic guides telling you to turn off Developer Options. "OnePlus red text on screen": Threads asking about the red "1" on the lock screen clock (funny, but not helping). "OnePlus red text error": Nothing remotely relevant. "isOverScrolling: false" OnePlus: No results found. "AbortVX" / "ClickVX": Absolute zero. 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. 😴 "That’s Future Me’s Problem" 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. 🤖 Handing the Terminal to Gemini 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. 🎨 Step 1: The Canvas Deduction 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). 🔬 Step 2: Disassembling the Phone on the Fly 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. 1. Hunting the String Table Gemini parsed the DEX string pool inside calc.apk and found the exact IDs: String #2600 = 'AbortVX:' String #37919 = 'isOverScrolling: ' 2. Pinpointing the Method It mapped those string IDs across every class definition in the binary. In seconds, Gemini had the exact location: Class: androidx.recyclerview.widget.COUIRecyclerView (OnePlus’s internal extension of the Jetpack RecyclerView) Method: dispatchDraw(Canvas canvas) at bytecode offset 0x1fbea0 3. Reading the Dalvik Bytecode 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. 💥 Step 3: The Domino Effect — Finding Patient Zero 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 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.). 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 Connecting the Dots 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: Why it survived reboots: Properties starting with persist. are saved directly to flash storage (/data/property). A reboot does not wipe them. Why the red text appeared: Every time any app initialized a scrollable list, 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. Why the edges were lagging: With global verbose logging enabled across the entire OS, every single touch event, swipe coordinate, and edge gesture flooded the system log daemon with hundreds of log lines per second. The CPU was overwhelmed with logging I/O, dropping frames and causing noticeable edge touch lag. ⚡ The Cure: Total Relief in Five Seconds Once Gemini identified the exact root cause, the fix was quick and clean: 1. Resetting the Global Flag Gemini reset the dangerous persistent global log property to empty: adb shell setprop persist.log.tag "" 2. Preventing It from Happening Again 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 3. Killing the Active Process for Instant Testing 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. 4. The Final Device Reboot: Flushing In-Memory State While Settings was fixed immediately, Gemini pointed out a crucial Android runtime mechanic: In the Dalvik/ART virtual machine, static initializers () 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: The red text was 100% gone across all apps. The edges were instantly responsive. Edge gestures, the Smart Sidebar, and the silky 120Hz display were completely restored. Zero personal data touched, zero system partitions modified. All temporary extraction scripts were cleanly wiped from both the phone and PC. 🎓 Final Thoughts: An Engineering Student Left Speechless 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: It systematically eliminated UI layers until only hardware Canvas rendering was left. It pulled compiled OEM packages directly from the phone without needing root access. It wrote Python scripts on the spot to parse binary DEX string pools and find the right class offsets. It analyzed the raw Dalvik bytecode and isolated the exact sget instruction controlling the overlay. It traced an undocumented UI flag back to a persistent Linux system property that had survived reboots. 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.
This is a summary aggregated from Dev.to. Read the complete article on the original site:
Read full article at Dev.to