{"slug": "nobody-painted-that-rectangle-black", "title": "Nobody Painted That Rectangle Black", "summary": "Maneshwar, a developer, explains the technical mechanism behind black rectangles in screenshots of protected content like Netflix videos and banking apps. On Android, SurfaceFlinger composites separate images per output destination, skipping layers with the FLAG_SECURE flag for screenshots, leaving untouched memory (zeros) that appear black. The protection is a software policy that can be bypassed with root access, unlike Widevine L1 DRM which enforces hardware-level protection via the TEE.", "body_md": "*Hello, I'm Maneshwar. I'm building git-lrc, a Micro AI code reviewer that runs on every commit. It is free and source-available on Github. Star git-lrc to help devs discover the project. Do give it a try and share your feedback.*\n\nYou have done this.\n\nNetflix is playing, something on screen is worth sharing, you hit screenshot, and you get a black rectangle where the video was.\n\nThe play button is there. The scrubber is there.\n\nThe actual show? Gone.\n\nSame deal with screen recording, except weirder.\n\nYou are staring directly at the video while it plays, perfectly visible, in full color, and the saved recording comes out black.\n\nYour eyes say one thing. The file says another.\n\nMost of us file this under \"Netflix blocked it\" and move on with our lives.\n\nBut the mechanism underneath is stranger and honestly more elegant than \"blocked.\"\n\nSo let's go layer by layer.\n\nHere is what I assumed:\n\nReasonable. Also completely wrong.\n\nNothing detects anything. Nothing paints anything.\n\nSit with that, because the rest of this post falls out of it.\n\nHere is the load-bearing idea, and it took me embarrassingly long to arrive at it: **the screen and the screen recorder are not looking at the same image.**\n\nI had assumed there was one framebuffer, one canonical \"what the phone currently looks like,\" and that a screenshot was simply a copy of it.\n\nNope. [SurfaceFlinger](https://source.android.com/docs/core/graphics/surfaceflinger-windowmanager), Android's compositor, does not render one image that everybody shares.\n\nIt renders a **separate composite per output destination.**\n\nThe physical panel is one destination.\n\nThe screenshot service is another. A cast session is a third.\n\nFor each destination, SurfaceFlinger walks the layer list and asks exactly one question per layer: does this layer belong in *this* output? For the panel, the answer is always yes.\n\nFor the screenshot output, any layer carrying `FLAG_SECURE`\n\ngets skipped.\n\nSo the video layer is never *removed* from the screenshot if its is never *added*.\n\nThis is my favorite part.\n\nThe capture buffer gets allocated and zeroed.\n\nNormal layers write into it. The secure layer does not. Then the buffer gets encoded and handed over.\n\nZeros are black.\n\nThat is the whole thing. Nobody chose black.\n\nThere is no `paintItBlack()`\n\nsitting in AOSP.\n\nBlack is simply what untouched memory looks like when you interpret it as pixels. The rectangle is not censored, it is **vacant**.\n\nIt is the absence of a decision.\n\nThis also explains something I had seen a hundred times and never once registered: the black is *exactly* the video's shape, and the UI around it survives just fine.\n\nPlay button, scrubber, title text, all present.\n\nThose are separate layers. Only one of them had the flag.\n\nSo I went and screenshotted my banking app.\n\nSame black frame. Password manager, same.\n\nWhatsapp one view image screenshot? same.\n\nHere is the entire implementation:\n\n```\ngetWindow().setFlags(\n    WindowManager.LayoutParams.FLAG_SECURE,\n    WindowManager.LayoutParams.FLAG_SECURE\n);\n```\n\nOne call. Thirty seconds of work. That is the feature, shipped.\n\nEverything so far is a **policy**.\n\nSurfaceFlinger *chooses* to honor `FLAG_SECURE`\n\n.\n\nThe pixels sit in perfectly ordinary memory the whole time.\n\nThe compositor is just politely declining to copy them.\n\nWhich means: root the phone, patch SurfaceFlinger, flag ignored, banking screenshots work again. The whole protection is a gentleman's agreement enforced by code that the root user owns.\n\nNetflix HD is not that.\n\nWith Widevine L1, decoded frames live inside the TEE, a separate secure world running on the same SoC.\n\nThe decrypted video never lands in memory the application processor can address.\n\nIt goes from the TEE into a protected buffer, gets composited, reaches your eyeballs, and the kernel never touches a single pixel of it.\n\nThere is no code to patch, because it is not code.\n\nIt is the memory controller saying no, in silicon xD\n\n| Mechanism | Enforced by | Defeated by | |\n|---|---|---|---|\n| Banking app | `FLAG_SECURE` |\nThe OS, voluntarily | Root |\n| Netflix HD |\n`FLAG_SECURE` + L1 secure buffers |\nThe chip itself | Nothing on the device |\n\nHere is where I stop explaining and start asking.\n\nFor `FLAG_SECURE`\n\n, I think yes. It is policy, and root owns the policy. Patch SurfaceFlinger, stop honoring the flag, banking screenshots come back.\n\nNetflix HD is where I run out of road. The frames are in the TEE. Root gets you the kernel, and the kernel is not the thing saying no. So what actually comes out the other end?\n\nI have not tried it. No spare handset to brick.\n\nSo has anyone actually done this? Flashaholics, custom ROM people what did you get?\n\nAI agents write code fast. They also silently remove logic, change behavior, and introduce bugs — without telling you. You often find out in production.\n\ngit-lrc fixes this. It hooks into git commit and reviews every diff before it lands. 60-second setup. Completely free.\n\nAny feedback or contributors are welcome! It's online, source-available, and ready for anyone to use.\n\n⭐ Star it on GitHub:\n\n| [🇩🇰 Dansk](https://github.com/HexmosTech/git-lrc/readme/README.da.md) | [🇪🇸 Español](https://github.com/HexmosTech/git-lrc/readme/README.es.md) | [🇮🇷 Farsi](https://github.com/HexmosTech/git-lrc/readme/README.fa.md) | [🇫🇮 Suomi](https://github.com/HexmosTech/git-lrc/readme/README.fi.md) | [🇯🇵 日本語](https://github.com/HexmosTech/git-lrc/readme/README.ja.md) | [🇳🇴 Norsk](https://github.com/HexmosTech/git-lrc/readme/README.nn.md) | [🇵🇹 Português](https://github.com/HexmosTech/git-lrc/readme/README.pt.md) | [🇷🇺 Русский](https://github.com/HexmosTech/git-lrc/readme/README.ru.md) | [🇦🇱 Shqip](https://github.com/HexmosTech/git-lrc/readme/README.sq.md) | [🇨🇳 中文](https://github.com/HexmosTech/git-lrc/readme/README.zh.md) | [🇮🇳 हिन्दी](https://github.com/HexmosTech/git-lrc/readme/README.hi.md) |\n\nGenAI today is a **race car without brakes**. It accelerates fast -- you describe something, and large blocks of code appear instantly. But AI agents *silently break things*: they remove logic, relax constraints, introduce expensive cloud calls, leak credentials, and change behavior -- without telling you. You often find out in production.\n\n** git-lrc is your braking system.** It hooks into\n\n`git commit`\n\nand runs an AI review on every diff In short, git-lrc helps **Prevent Outages, Breaches, and Technical Debt Before They Happen**\n\n**At a glance:** [10 risk categories](https://github.com/HexmosTech/git-lrc#what-git-lrc-checks-for) · [100+ failure patterns tracked](https://github.com/HexmosTech/git-lrc#what-git-lrc-checks-for) · every commit…", "url": "https://wpnews.pro/news/nobody-painted-that-rectangle-black", "canonical_source": "https://dev.to/lovestaco/nobody-painted-that-rectangle-black-a3", "published_at": "2026-07-16 16:54:20+00:00", "updated_at": "2026-07-16 17:05:34.895297+00:00", "lang": "en", "topics": ["artificial-intelligence", "developer-tools"], "entities": ["Maneshwar", "git-lrc", "Netflix", "Android", "SurfaceFlinger", "AOSP", "Widevine L1", "TEE"], "alternates": {"html": "https://wpnews.pro/news/nobody-painted-that-rectangle-black", "markdown": "https://wpnews.pro/news/nobody-painted-that-rectangle-black.md", "text": "https://wpnews.pro/news/nobody-painted-that-rectangle-black.txt", "jsonld": "https://wpnews.pro/news/nobody-painted-that-rectangle-black.jsonld"}}