u/Imaginary-Storage957

▲ 1.3k r/ActualPublicFreakouts+1 crossposts

Bleacher Brawl at Wrigley Last Night (2nd View)

Alternate angle of the fight last night at the Cubs vs White Sox game

Credit: Twitter - DanteTheDon

u/Imaginary-Storage957 — 3 days ago
▲ 297 r/MorpheApp

Presenting: The Netflix Android TV Patch for Onn 4K TV/Google TV

Hey everyone!

INTRO: I'm a huge fan of this community and all the hard work the developers have put into making Morphe an awesome success. I've been working hard on various patches thanks to being able to learn from the best (RookieEnough, Hoodles, Paresh) and give back to the community.

I'd like to take a moment to acknowledge that I am not a coder. I work in healthcare and have a decent idea of how things work, but I am in no way experienced in bytecode patching. I've learned how to use the expertise of LLM and teach it to understand the complexities of thousands of lines of code and to reverse engineer what companies spend millions of dollars developing. I don't have years to study and learn everything and these patches are in need now.

So fair warning: I've deployed Claude (scary!!!) to help me build my GitHub repo, and I've leaned on AI extensively to problem-solve and write the code — this Netflix patch included. I know an AI-assisted patch isn't what everyone wants, but hey, it's your choice: an AI-generated patch for ad-free Netflix, pay for a Netflix ad-free plan, or watch Netflix with ads. I know which one I'd choose.

And to be clear up front: this is NOT about getting Netflix for free. You need a valid, paid account and you log in normally — bypassing subscriptions, paywalls, or DRM is out of scope. This is purely about removing ads and trimming device telemetry inside an app you're already paying for.

---

TL;DR:

A Morphe patch that installs an ad-free **clone** of the real living-room Netflix app on Android TV. Kills pre-rolls, mid-rolls, and the pause-screen ad overlay, plus an optional privacy toggle. No root, no proxy, no DNS/VPN. **You keep stock Netflix installed** — the patched clone needs it there (explained below). On-device verified on an Onn 4K.

---

What it does

- ✅ **Pre-roll ads** (before playback) — gone

- ✅ **Mid-roll ads** (during a show/movie) — gone

- ✅ **Pause-screen ad overlay** (the full-screen ad when you pause) — gone

- ✅ Playback, resume-to-position, and seeking all work normally

- 🔒 Optional privacy toggle ("Minimize Network Fingerprint") — blanks the local IP / MAC / Wi-Fi SSID the app reports, and stops it from reporting your advertising ID (GAID) in its telemetry. Off by default.

This is the actual TV app (`com.netflix.ninja`), not a phone app running on your TV.

---

Why there are TWO apps (please read — this trips people up)

You end up with stock Netflix & an "ad-free" Netflix clone installed side by side. Both must stay installed. Here's why:

- Netflix on a TV is a system app that's cryptographically signed by Netflix. You CANNOT patch it in place, and you CANNOT uninstall it without root. So the patch installs as a separate app with its own name.

- The clone is protected by a native anti-tamper check that reads Netflix's original signature. The clone passes this check by *pointing at the stock app and borrowing its genuine signature*. WARNING: Remove or disable stock Netflix and the clone will refuse to start.

- The clone reads stock's signature, not its version — so it's totally fine to let stock Netflix auto-update in the background. It won't break the clone.

Bottom line: Keep stock Netflix installed and enabled. You just never open it — you open the clone instead. Log into the clone once and you're set.

---

Install

  1. Add the repo to Morphe: https://morphe.software/add-source?github=ajstrick81/morphe-androidtv-patches
  2. In Morphe, select Netflix and apply the patch set:- Remove Netflix ads — the ad kills (on by default)- Clone Netflix — required; makes it installable alongside stock- Minimize Network Fingerprint — optional privacy toggle (off by default)
  3. Make sure to toggle "off" Optimize for Device Architecture"
  4. Install the patched APK it produces. Do not uninstall stock Netflix.
  5. Use Send Files to TV to transfer the cloned apk to your ATV device
  6. Open the new Netflix Clone app, sign in, and use it like normal.
  7. Enjoy Ad-free Netflix! :)

---

FAQ

Do I need root? No. No root and no proxy. Your ATV device plays it seamlessly without ads.

Do I need a VPN or a DNS blocker (AdGuard/Pi-hole)? No. The ads are removed *inside the app*, not by blocking network requests.

Will this stop the "you're traveling / not in your household" prompt? No. That's based on your public IP address, which Netflix's servers see no matter what the app does. Only a VPN/proxy changes that. This patch is about ads and device fingerprinting, not household enforcement.

Does the privacy toggle make me anonymous? No — be realistic. Netflix still knows who you are (you're logged in) and what you watch (it streams it to you). The toggle trims *granular device analytics* (local network details, advertising ID). It's a reduction, not a cloak.

Will Netflix updating break it? No. Let stock Netflix update freely. If a future Netflix version changes the internals enough that ads come back, the patch fingerprints get refreshed and you rebuild.

Two Netflix icons is annoying — can I hide stock? You can move it out of the way, but don't disable or uninstall it — the clone needs it present to start.

Is my login safe / is this a fake app? It's the real Netflix APK, re-signed and cloned locally by you with Morphe. It talks to Netflix's real servers with your real account. Nothing is proxied through a third party.

Does this get me Netflix for free / bypass the subscription?

**No — and that's not what this is.**

You need a valid, paid Netflix account and you log in normally. This patch only changes how ads and device telemetry are handled *inside* an app you're already entitled to use. Bypassing paywalls, subscription checks, DRM, or any access control is explicitly **out of scope** for these patching projects — don't ask for it and don't expect it here.

---

Verified

Tested on an Onn 4K (Android 14, Netflix 13.0.1). Across repeated runs the Netflix servers did deliver real pre-rolls, mid-rolls and pause ads — and none of them played, with no playback errors and resume position intact. A full technical write-up of how it works is below:

Technical Breakdown

u/Imaginary-Storage957 — 13 days ago
▲ 213 r/MorpheApp

Presenting - Peacock TV Android TV — Full Ad Suppression (v7.6.100) — No DNS Required

Hey everyone!

This one took the longest of any app I've worked on, and the payoff is probably the cleanest result in the whole collection: Peacock, fully ad-free, no DNS filter required, no AdGuard Premium rules, nothing external. Just the patch. Let me walk through why it was hard, because the lesson is useful for everything that follows.

Same disclaimer as always: **I am not a coder.** I work in healthcare and lean on Claude heavily to read bytecode, trace smali, and write the patch code. This was a long collaborative autopsy — dex disassembly across a massive codebase, on-device logcat forensics on my Onn 4K TV, PCAP/GREASE network captures, and a lot of dead ends before the real gap became clear. If AI-assisted patches aren't your thing, totally fair — but it works, and it's ad-free.

---

The Big Discovery — Peacock Has Two Network Stacks & You Have to Block Both

Peacock is built on the **Sky Core Player SDK** — a professional streaming engine from Sky (yes, the UK broadcaster) that Comcast/NBCUniversal licenses for Peacock. It handles playback, DRM, SSAI via MediaTailor, and client-side ad insertion via FreeWheel. When I started digging, I hit what looked like a clean win fast: the SDK wires FreeWheel into the player via a dependency injection module (`AddonInjectorImpl.di$lambda$0()`). Remove those two DI instructions and FreeWheel is never registered. No FreeWheel module → no ad requests → no ads. Right?

Wrong. I installed that build (Layers 1-8, everything except the one I'm about to describe) and ran it on the Onn TV. Logcat showed FreeWheel's full ad lifecycle firing across five consecutive ad pods, each playing to completion over three minutes: `defaultImpression → firstQuartile → midPoint → thirdQuartile → complete → slotEnd`. The beacon pings to `fwmrm.net`, `omtrdc.net`, and `conviva.com` were being intercepted by the WebView layer — but the ad creative itself was already playing. The DI removal should have stopped it. Something was routing around it.

The root cause: Sky SDK has its own OkHttpClient, completely separate from the app's.

The app-level networking lives in `NetworkingKt.getOkHttpClient()` — Layer 6 patches that client and wires in the ad-blocking interceptor. But the Sky SDK has a second network stack: `NativeNetworkApi`, a class deep inside `com.sky.core.player.sdk.addon.networkLayer`. Its constructor derives its own child `OkHttpClient` via `newBuilder()/build()` independently of the app's client — and this is the client that actually carries FreeWheel ad decisioning, creative fetches, Conviva/Comscore/Nielsen measurement, and MediaTailor telemetry. Layer 6's interceptor was never wired into it. The SDK was placing ad requests on a completely invisible channel.

**Layer 9** hooks `NativeNetworkApi.<init>` and injects the same `AdBlockInterceptor` into that second client right before `.build()` is called. With both clients covered, there's nowhere left for ad traffic to go.

---

What the Patch Does

Nine layers total — but I'll describe them by what they actually kill rather than enumerate every hook:

Layers 1-4 — MediaTailor SSAI disabled at three points

`getProxyHost()` returns `""` (no proxy URL = no SSAI), the MediaTailor advertising service factory returns null, and `getSsaiConfigurationProvider()` returns null, forcing `AdvertisingStrategy.None` for all playback types. Belt, suspenders, and a third strap for good measure.

Layer 5 — Ad-break-started handler no-op'd

`PlayerEngineItemImpl.handleAdBreakStarted()` is made a `return-void` at offset 0. With Layers 1/3/4 and 8 in place it's unreachable anyway — but this closes the gap defensively if a stale SSAI manifest ever sneaks through.

Layer 6 — App-level OkHttpClient interceptor

`NetworkingKt.getOkHttpClient()` is replaced with a call to our extension that builds the same OkHttpClient with `AdBlockInterceptor` wired in. Handles all ad/analytics traffic going through the app's own network stack.

Layer 7 — WebView shouldInterceptRequest

PCAP analysis confirmed ad segment delivery and FreeWheel traffic travels through Chromium/WebView, bypassing OkHttp entirely. `XTVWebView` has three constructors and `VirtualDpadXTVWebView` (inflated from `activity_main.xml`) uses the 2-arg `(Context, AttributeSet)` one — not the 1-arg one most people would patch first. All three constructors are now wrapped with `PeacockWebViewHelper.wrapClient()`, which adds `shouldInterceptRequest()` with randomized response codes to avoid FreeWheel's fraud-detection fingerprinting.

Layer 8 — FreeWheel DI module surgically removed (the keystone)

Two instructions in `AddonInjectorImpl.di$lambda$0()` — an `iget-object` and an `import$default` — are all that register FreeWheel into the Sky SDK's DI container. Remove them and the player has no FreeWheel addon at all: no VMAP schedule fetched, no VAST creative requested, no impression/quartile/completion pixels, no ad segments buffered. This is the Sky SDK equivalent of Pluto's `getAdBreaks()` → empty list.

Layer 9 — Sky SDK addon network client interceptor (the gap-closer)

`NativeNetworkApi.<init>` is hooked to inject `AdBlockInterceptor` into the SDK's own OkHttpClient before `.build()` is called. This is what actually stops the ad creatives from loading, even when Layer 8's DI removal should theoretically mean no ad request is ever made — defense in depth, and in practice the layer doing the most work.

---

A Bug We Found Along the Way — and Why It Matters for TV Hardware

During testing we hit an `OutOfMemoryError` within about 2.5 minutes of launch — GC pressure climbing continuously until the process died. The culprit: artificial `Thread.sleep()` calls (50-200ms, ~15% of requests) added to the interceptors to mimic natural network jitter for fraud-detection evasion. On a PC or phone this is harmless. On an Onn TV, `NetworkingKt.getOkHttpClient()` builds a brand-new `OkHttpClient` (with its own `Dispatcher` thread pool) on every call. Those ephemeral thread pools normally get reaped fast because the requests finish quickly. With an artificial sleep holding the threads open longer, the pools piled up faster than they could be reaped on the memory-constrained hardware, and that's your OOM. The randomized status-code mix (the actual anti-detection signal) was kept; only the blocking delay was removed. If you tested an earlier dev build and hit crashes or sluggishness, this is why the released build behaves differently.

---

Testing / Results

Verified on my **Onn 4K Plus** (Android 14), Peacock **v7.6.100**: launched live TV, VOD movies, and shows — **no ads, clean playback, no loading spinner delays, no crashes.** No DNS filter running during testing.

---

Installation

**Add the repo to Morphe:**

https://morphe.software/add-source?github=ajstrick81/morphe-androidtv-patches

**Step by step:**

  1. Download the **Peacock TV Android TV** APK **v7.6.100** from APKMirror to your phone/tablet. Use the **Android TV** listing and download the `.apkm` (app bundle), not a single-arch `.apk`.
  2. Load the `.apkm` into Morphe Manager and select the **Peacock — Skip ads** patch.
  3. Allow patching to complete, then save the patched APK using the save option (bottom-right corner).
  4. Uninstall the existing Peacock app from your Android TV device first (avoids a signature conflict on first install).
  5. Install via one of these methods:- **Option A:** ADB — `adb install <patched_apk>`- **Option B:** "Send Files to TV" → install from a file manager
  6. No DNS setup required — enjoy ad-free Peacock!

---

What This Means Going Forward

Peacock is a useful data point in the emerging map of what's beatable and how. Sky SDK is licensed across multiple apps (it's not Peacock-exclusive), and the `NativeNetworkApi` hidden-client gap is likely to show up anywhere the same SDK is embedded. If you patch an app and the beacons are being blocked but the ads still play, that's the tell: there's a second network client that your layer-6 equivalent isn't reaching.

The deeper lesson — same as Pluto, from the other direction — is that **what layer the ad-break schedule and ad fetching happens in determines removability**. Pluto's ad-break timeline lived in reachable Java, so `getAdBreaks() → empty list` was the kill. Peacock's FreeWheel pipeline lives in the Sky SDK's own DI and networking stack, so you have to go deeper: remove the DI module AND cover both OkHttpClients. Prime Video buries everything in a sealed native engine below the Java line — that's why DNS is the ceiling there. Same disease, different depth.

---

Huge thanks to the Mod Team, to RookieEnough, hoodles, and Paresh (who open-sourced his entire apk patching methods) for building such a great community around Morphe, and to everyone who has tested and given feedback. This one had more dead ends than any other app so far — the community's prior documentation saved a lot of duplicated effort. Every lesson from this build is in the repo. See you in the next one!

reddit.com
u/Imaginary-Storage957 — 1 month ago
▲ 142 r/MorpheApp

Pluto TV Android TV - Patched for Ad-Free Enjoyment!

Hey everyone!

Back with another one — and this might be my favorite result yet, because Pluto TV was "supposed" to be unbeatable. It's SSAI (server-side ad insertion), the same class of stitched-ad problem that walls off Prime Video's mid-rolls. Every instinct said "the ad video is baked into the stream, bytecode can't touch it." Turns out that's only half true — and the other half is a clean one-hook kill. Let me explain, because the lesson here is what separates the apps we CAN beat from the ones we can't.

Same disclaimer as always: **I am not a coder.** I work in healthcare and lean on Claude heavily to read bytecode, trace smali, and write the patch code. This one was a proper collaborative autopsy — dex disassembly across 64,000+ methods, on-device logcat forensics on my Onn 4K TV, and an AdGuard Premium filtering-log capture that cracked the whole thing wide open. If AI-assisted patches aren't your thing, totally fair — but it works, and it's ad-free.

---

The Big Discovery — WHERE the Ad Timeline Lives Decides Everything

Pluto delivers ads via **server-side ad stitching (SSAI)**, with tracking through FreeWheel. The ad video is spliced into the same DASH stream as your content and served from the same CDN. Conventional wisdom — and my own Prime Video scars — says that's unremovable: you can't strip video segments that are physically baked into the stream.

**But not all SSAI apps are the same, and the difference is WHERE the ad-break schedule gets parsed.**

Pluto's stitcher hands the client a `session.json` containing an `adBreaks` array — the ad-break timeline. And critically, **Pluto parses and consumes that timeline in the app's own Java/Kotlin layer** (`StitcherSession.getAdBreaks()`). That single list drives the *entire* ad experience: the ad-break timeline, the ID3-tag beacon tracking, the pause/clickable-ad overlays, and the seek-blocking during breaks.

**How I confirmed it:** a filtering-log export from AdGuard Premium (running on PC) showed exactly how it suppresses Pluto's ads — one surgical rule:

`||pluto.tv/*/session.json$jsonprune=$.adBreaks.*`

AdGuard simply strips the `adBreaks` array out of the session response on the wire, and the ads vanish — no crash, clean playback. That was the proof: the ad video is **NOT irreversibly baked in.** It's client-assembled from `adBreaks`. Remove the schedule, remove the ads.

Which means we can reproduce that exact prune **inside the app, in bytecode** — no proxy, no cert install, no DNS required for on-demand.

**The contrast that matters for the roadmap:** Prime Video does its SSAI ad decisioning down in a sealed *native* engine (libignite / MediaPipelineBackend), below the Java line — unreachable in bytecode, DNS-only. Pluto does it in *reachable Java*. Same disease, but Pluto leaves the door open. **If an SSAI app parses its ad-break timeline in Java, it's beatable in bytecode. If it's sealed in native code, it isn't.** That's the single most useful rule I've picked up on this whole project.

---

What the Patch Does

Four hooks — but one of them does the heavy lifting:

**Hook 1 — BeaconTracker.fire(String, List)** (`return-void`)

The choke point for SSAI tracking beacons (impressions, quartiles, completes). Silences ad analytics with zero risk to the playback pipeline.

**Hook 2 — PauseAdsImageBinder.showPauseAdImageAfterInactivity(Bitmap, List)** (`return-void`)

Pluto's pause-screen ad overlay — the full-screen ad shown when you pause. No-op'd at index 0 so the pause screen renders clean and the overlay is never fetched.

**Hook 3 — ClickableAdsBinder.bind(...)** (returns an already-disposed Disposable)

Kills the interactive/clickable ad overlays by never wiring the subscription graph — while still honoring the method's contract (the caller gets a valid, harmless Disposable), so nothing hangs.

**Hook 4 — StitcherSession.getAdBreaks() → empty list** (the keystone)

This is AdGuard's `$jsonprune=$.adBreaks.*` reproduced in bytecode. It forces the parsed ad-break timeline to be empty at the single choke point every consumer reads it through. The result: **no ad breaks are ever scheduled — no ad video, no markers, no overlays, and no beacons.** On-device it even silenced the ID3-tag beacon tracker that Hooks 1–3 don't fully cover, because that tracker is fed by the very same `adBreaks` list. One hook, entire ad subsystem starved.

The patch also ships with an **Override certificate pinning** hook — it's what made Pluto's HTTPS traffic inspectable during analysis, and it's harmless to leave enabled.

Testing / Results

Verified on my **Onn 4K Plus** (Android 14), Pluto **v5.66.0-leanback**: played multiple on-demand movies and shows — **playback loads fast, no delay whatsoever, ad markers gone, and zero tracking beacons fired.** Straight-through content on every title I tried.

Important — Scope: On-Demand vs Live TV

Being upfront about the boundary, because SSAI has one part even this can't fix:

- **On-demand (VOD) — fully suppressed.** The ad-break timeline is client-side, so emptying it removes the ads completely. This is the win.

- **Live TV (linear channels) — NOT removable.** Live ads occupy real broadcast wall-clock time in the linear feed — there's no "skip," because the ad literally IS the live stream at that moment. Same fundamental limit as any live broadcast.

**Optional DNS companion (belt-and-suspenders, NOT required):** unlike some apps, Pluto's pre-rolls do *not* need DNS — Hook 4 handles all on-demand ads in-app. But if you already run a DNS blocker and want Pluto's beacon/ad hosts gone at the network layer too, from the same AdGuard capture:

||sp.pluto.tv^

||v.fwmrm.net^

||spotxchange.com^

||tremorhub.com^

(Generic Google ad hosts like doubleclick / googlesyndication / adnxs are already covered by any standard list such as HaGeZi.)

---

Installation

**Add the repo to Morphe:**

https://morphe.software/add-source?github=ajstrick81/morphe-androidtv-patches

**Step by step:**

  1. Download the Pluto TV Android TV APK **v5.66.0-leanback** from APKMirror to your phone/tablet.
  2. Load the APK into Morphe Manager and select the **Pluto TV — Skip ads** patch (you can leave the certificate-pinning override on).
  3. Allow patching to complete, then save the patched APK (save it — don't auto-install; use the save option in the bottom-right corner).
  4. Uninstall the existing Pluto TV app from your Android TV device first (avoids a signature conflict).
  5. Install via one of these methods:- **Option A:** ADB streamed install — connect to the TV via ADB and run `adb install <patched_apk>`- **Option B:** Use "Send Files to TV" to transfer the patched APK, then install from a file manager
  6. (Optional) add the DNS rules above if you want the trackers gone too.
  7. Enjoy ad-free on-demand Pluto TV!

---

## What This Means Going Forward

The real prize here isn't just Pluto — it's a **reusable recon technique**. An AdGuard Premium filtering-log capture tells you *exactly* which request/rule kills the ads ($jsonprune, $replace, or a plain host block). If that mechanism maps onto something the app parses in its own Java layer, you can reproduce it in bytecode and cut the network out of the loop entirely. That's precisely how Hook 4 was found, and it should carry over to other client-side-SSAI apps....hopefully ;

It also sharpens the map for future targets: apps that parse their ad-break timeline in Java (Pluto) are beatable in-app; apps that bury it in a sealed native pipeline (Prime Video) are DNS-only. Knowing which is which up front saves a mountain of dead-end effort. And honestly — a free service with tens of thousands of on-demand titles, now ad-free? Hard to beat that.

---

Huge thanks to the Mod Team, to RookieEnough and hoo-dles for building such a great community around Morphe, and to everyone who has tested and given feedback. The debugging on this one was quick, but only because of everything the community has documented before it. Every lesson from this build is in the repo. See you in the next one!

reddit.com
u/Imaginary-Storage957 — 2 months ago
▲ 133 r/MorpheApp

Presenting - The Prime Video Android TV Patch for Onn 4K TV

Hey everyone!

Long time coming on this one. Prime Video ATV has been the most challenging and fascinating reverse engineering project I've tackled so far, and I want to document the full journey here because the architecture is unlike anything else in the streaming ecosystem. A lot of wrong turns, a lot of learning, and ultimately a clean result that I'm proud of.

Same disclaimer as always: I am not a coder. I work in healthcare and have used Claude extensively as a collaborative research and coding partner throughout this project. We've spent months analyzing bytecode, running PCAPdroid captures, DNS examination, dissecting DEX files, running logcat analysis, and building — then sometimes reverting — various approaches until we found what actually works cleanly. If AI-assisted patches aren't your thing, totally fair. But the methodology is sound and the result speaks for itself.

The Most Important Thing You Need to Know First

Prime Video ATV is architecturally unique among streaming apps, and understanding why is critical to understanding both the patch and its limitations.

Most streaming apps keep their ad logic in Java bytecode — IMA SDK calls, FreeWheel integrations, SSAI client code. You find the right class, inject a hook, done. Prime Video ATV does something completely different. The app is a shell. The appBootstrap.js bundled inside the APK is literally 0 bytes — a placeholder. The actual application logic, including ALL ad scheduling, is a WebAssembly (WASM) bundle downloaded fresh from Amazon's CDN every single session. Amazon calls this the Ignite/Megablast runtime.

This means you cannot patch the ad logic directly because it doesn't exist in the APK. What you CAN patch is the boundary between the WASM runtime and the standard Android media player (ExoPlayer/media3) that ultimately plays the video. That boundary is where our hook lives.

The Discovery That Made This Work

After extensive DEX analysis, logcat forensics, and PCAPdroid captures, we mapped the exact chain of events during an ad break:

Amazon WASM Runtime (downloaded at session start)
    ↓ pushes SSAI ad schedule as AdPlaybackState map
setAdPlaybackStates(ImmutableMap, Timeline)   ← THIS IS OUR HOOK
    ↓ ExoPlayer media3 receives ad groups
    ↓ ExoPlayer fetches ad segments from Akamai CDN
    ↓ Ads play

ServerSideAdInsertionMediaSource.setAdPlaybackStates() is the transfer point where the WASM runtime hands the ad schedule to ExoPlayer. It's in the unobfuscated androidx.media3 library — which means it's a stable target that survives APK updates as long as Amazon continues using media3. Our extension intercepts this map and calls withRemovedAdGroupCount(adGroupCount) to strip every ad group before ExoPlayer ever sees it.

What the Patch Does

Two hooks. No DNS rules required. No network configuration needed.

Hook 1 — media3 ServerSideAdInsertionMediaSource.setAdPlaybackStates()

The primary intercept. Called every time the WASM runtime pushes an ad schedule into ExoPlayer. Our Java extension iterates the AdPlaybackState map and removes all AdGroups via withRemovedAdGroupCount() before ExoPlayer processes them. ExoPlayer receives a clean map with no ad groups — no ads are scheduled, no segments are requested.

Hook 2 — ExoPlayer2 ServerSideAdInsertionMediaSource.setAdPlaybackStates()

The Google Mobile Ads SDK bundles its own copy of ExoPlayer2 alongside media3. This hook covers the same suppression for the ExoPlayer2 SSAI pipeline, ensuring both delivery paths are covered.

Logcat Confirmation — What's Actually Happening

After testing across 4 different movies in a single session, the logcat tells a clear story:

60 instances of:

[playback::machine] [0] not creating envelope refresher as no correlation id present on envelope

This is the WASM runtime trying to synchronize ad state with ExoPlayer and failing because our hook stripped the correlation IDs. This is the patch working.

16 instances of:

[playback] start_with_options ignored: requested src matches the currently loaded src

After failing to find correlation IDs, the WASM tries to restart playback to re-acquire the ad schedule. ExoPlayer ignores the restart because the source is already loaded. This is the patch working.

3 instances of:

Header visibility update rejected — conflicting lock found
active locks: [(sudo_header_controller, ...)]

The WASM's ad overlay UI (the "Ad 1 of 2" header) attempts to render and gets blocked by the player's own lock mechanism. The interactive cart/purchase overlay (InteractiveVideoAd) appears zero times across the entire session.

Zero contact with any ad CDN — Akamai, TERS SGAI stitching server, s.amazon-adsystem.com impression beacon, Surface-X ad orchestration scheduler. All absent from the entire session. The patch intercepts before any network request is made.

Important — Do NOT Run DNS Filtering Alongside This Patch

This is the lesson that cost the most sessions to learn. AGH (AdGuard Home) and AGP (AdGuard Pro) both cause problems with Prime Video ATV when active alongside the bytecode patch.

AGH adds DNS resolution latency that disrupts Prime Video's Ignite WASM session initialization. The session init fires dozens of concurrent DNS requests at launch — even small delays cause cascading failures that crash the app. We went through five versions of AGH rules trying to make this work before concluding that the app simply doesn't tolerate added DNS latency.

AGP is worse — its local VPN disrupts the ExoPlayer pipeline that our patch depends on. Running AGP alongside the patch produced MORE ads than the patch alone, because the VPN layer interfered with the very delivery chain we were intercepting.

The bytecode patch is self-contained. No network configuration needed. No DNS rules. No VPN. Just the patched APK.

The Belgium VPN Curiosity (Interesting Finding, Not a Recommendation)

One fascinating discovery during research: connecting via a Belgian VPN completely eliminates ads with no patch at all. Belgium's GDPR enforcement means Amazon's server sends a WASM bundle with no ad code whatsoever — the Ignite runtime simply never initializes an ad engine. Zero WASM activity, zero ad CDN contact, zero ads.

This is entirely server-side — Amazon makes this decision based on IP geolocation. Prime Video will detect your VPN and it might not work. I used NordVPN, cleared the cache, force stopped the app, and reopened it successfully.

The problem with connecting to Belgium is many of the US shows/movies you would enjoy are not available. It's a cool trick, but the content isn't as good.

Device Compatibility — Please Read Before Installing

This patch was built and validated specifically on Onn 4K Android TV (US account, ad-supported tier, v6.23.23+v15.5.0.70-armv7a). Here's honest guidance on other devices:

✅ Confirmed Working

  • Onn 4K Android TV — full test suite, multiple sessions, logcat verified

⚠️ Results May Vary

Fire TV devices (any model) The Fire TV version of Prime Video is a completely different app: com.amazon.firebat vs our target com.amazon.amazonvideo.livingroom. The package names are entirely different. While our DEX analysis confirmed the same setAdPlaybackStates method exists in the Fire TV build, several factors make success uncertain:

  • Fire TV uses the PyroCore framework for device integration which doesn't exist in the Android TV build
  • Amazon's server almost certainly sends a Fire TV-specific WASM bundle, not the Android TV bundle our patch was calibrated against
  • The WASM ad delivery chain on Fire TV may use different timing or callback patterns that our hook doesn't intercept at the right moment
  • Some users have reported sideloading the patched Android TV APK onto Fire TV with the patch APK taking priority over the system version — results vary by device and account

If you try it on Fire TV and it works, please report back — that data helps the whole community.

Nvidia Shield Android TV but uses arm64-v8a architecture vs our tested armeabi-v7a. The fingerprints target class names and method signatures that should be identical across architecture slices, but this combination is untested. Low risk to try, but unverified.

Google TV built-in smart TVs (Sony, TCL, Hisense, etc.) Prime Video on built-in Google TV smart TVs typically ships as a system app in a protected partition. Sideloading a patched version may conflict with the system version. Additionally Google TV's process management layer can interfere with how patched apps behave. Dedicated streaming dongles (Chromecast with Google TV, TiVo Stream 4K) have better odds since Prime Video isn't a system app on those.

Non-US accounts Amazon operates separate regional infrastructure. The SSAI stitching endpoints, ad CDN routing, and WASM bundle content all vary by region. The patch operates at the ExoPlayer layer which is globally consistent, but the specific WASM behavior may differ. Users in regions where Amazon's ad-supported tier hasn't launched will see no ads regardless of the patch.

❌ Not Compatible

Fire TV Stick specifically — different package name, different app, different architecture. The patch as written will have no effect. I've only come across one user that was able to implement it effectively but most people report issues either installing it or ads playing normally. Sorry, but I cannot create and maintain a patch for FireTV.

A Note on Account Flagging

One hard-learned lesson: Amazon's ad system responds to suppression patterns. If you aggressively block ad delivery over many sessions — especially using DNS blocking that causes failed network requests — Amazon's server-side logic can increase ad load in subsequent sessions to compensate for impression deficits.

The bytecode patch avoids this because it leaves no suspicious network fingerprint. From Amazon's servers, your session looks completely normal — requests are made, sessions are established — the interception happens entirely inside the local app process. This is the fundamental advantage of bytecode patching over DNS/network blocking for this specific app.

If you've been experimenting with aggressive DNS blocking and notice heavier than expected ads after installing this patch, try:

  1. Reset your advertising ID: Settings → Privacy → Ads → Reset advertising ID
  2. Deregister and re-register the device on amazon.com
  3. Clear Prime Video data: Settings → Apps → Prime Video → Storage → Clear Data

Give it a few fresh sessions before concluding the patch isn't working.

Installation

Add the repo to Morphe: https://morphe.software/add-source?github=ajstrick81/morphe-androidtv-patches

Step by step:

  1. Download Prime Video Android TV APK v6.23.23 (armv7a) from APKMirror to your phone or tablet. You want the .apkm bundle file.
  2. Load the APK into Morphe Manager and select the Skip ads patch
  3. Important: Disable the "Morphe patches" option before patching — leaving it enabled activates a package rename that will cause INSTALL_FAILED_UPDATE_INCOMPATIBLE
  4. Allow patching to complete, then save the patched APK using the bottom right corner save option (do not click install directly)
  5. Use Send Files to TV app to transfer the apk from your phone/tablet to the device
  6. Sign into your account and test
  7. Enjoy the ad-free TV shows/movies!

No DNS rules. No additional configuration. Just the patched APK.

What's Next

The same setAdPlaybackStates approach used here was already confirmed working in our Peacock patch. The methodology of finding the ExoPlayer SSAI boundary and intercepting the AdPlaybackState map before the player sees it is transferable to any app using media3 for server-side ad insertion. The full DEX analysis, logcat methodology, and PCAPdroid findings are all documented in the repo for anyone who wants to dig into the architecture.

Huge thanks to the Mod Team, and to RookieEnough and u/hoo-dles for building such a solid community around Morphe and for the reference implementations that helped shape the methodology here. Hoodles' Prime Video Mobile patch in particular — the FSM state intercept approach — was a major conceptual influence even though the ATV architecture ended up being completely different.

See you in the next one!

reddit.com
u/Imaginary-Storage957 — 2 months ago

New Patch for Tubi Android TV (v10.20.5000)

Hey everyone!

I'm back with a new patch, and this one was genuinely one of the most educational reverse engineering journeys I've been on. Tubi turned out to be far more architecturally complex than I would think, and the lessons learned here will likely apply to Fox Sports, Fox One, and other Fox-ecosystem apps going forward. It seemed to me being a free app, it would be easy. Well, I was proven wrong for sure!

Same disclaimer as always:

I am not a coder and/or programmer. I work in healthcare and have used Claude extensively to analyze bytecode, dissect smali, and write the patch code. This was a deep collaborative process involving dex file autopsies, WebView traffic analysis, AGH query log forensics, ART verifier debugging, and a lot of trial and error across many builds. If AI-assisted patches aren't your thing, totally fair — but it works, and that's what matters!

---

## The Big Discovery — Tubi is a Hybrid App

This is the most important finding of this entire project, and it's worth explaining because it affects anyone trying to patch Fox-ecosystem apps.

**Tubi on Android TV is not a native app in the traditional sense.** It's a shell around a full JavaScript single-page application hosted at `ott-androidtv.tubitv.com`, running inside a WebView powered by wendu/dsbridge. This SPA handles the **entire pre-roll ad lifecycle in JavaScript** — it requests the ad, plays it in a `<video>` element, tracks the impression, and only when the ad is finished does it call a JS→Native bridge command (`startNativePlayer`) to hand off to ExoPlayer for content playback.

This is why ExoPlayer initializes **after** the pre-roll, not before. Our logcat confirmed this exactly — `ExoPlayerImpl.Init` fires roughly 2 minutes after app launch, long after the pre-roll session has already ended.

**This is also why DNS rules I've written for Fox Sports and Fox One left some pre-rolls alive.** The pre-rolls were never reaching the Java/Kotlin native layer that the patches and the DNS rules were targeting. They were running in a sandboxed Chrome renderer process, driven entirely by JavaScript.

The same WebView-SPA hybrid architecture is used across Fox Corp's TV apps. This patch documents the interception technique that can be applied to all of them.

---

## What the Patch Does and Doesn't Do

Seven hooks targeting every ad delivery system in the APK:

**Hook 1 — FoxImaAdListeners.adEventListener** (`return-void`)

Every Google IMA ad event (AD_BREAK_STARTED, AD_STARTED, AD_PROGRESS, AD_COMPLETED, etc.) flows through this single method before reaching FoxPlayer. Silencing it means FoxPlayer never receives any ad event — no ad break lock, no ad rendering, no ad pod sequence. Primary suppression for mid-roll and post-roll ads.

**Hook 2 — FoxImaAdListeners.adsLoadedListener** (`return-void`)

Called when the IMA AdsManager finishes loading. This is the only place in the Fox IMA stack that calls `BaseManager.init()`, which primes the ad timeline and fires the initial AD_BREAK_STARTED for the pre-roll. Blocking it prevents the IMA session from ever being confirmed. Cold launch pre-roll suppression — works alongside Hook 1.

**Hook 3 — FoxPlayer.clearVodAds()** (extension call)

Amplification hook that nulls out the IMA StreamManager reference via reflection during content transitions, preventing stale IMA sessions from reactivating ad delivery after `clearVodAds()` is called.

**Hook 4 — ImagePauseAds.l(VideoApi, long)** (`return-void`)

Tubi's own pause ad system, completely separate from the IMA/DAI pipeline. When you pause playback, this method would launch a coroutine to fetch a display ad overlay from Tubi's ad server and render it on the pause screen. Blocking it at index 0 prevents the coroutine from ever being created — pause screen renders normally, overlay is simply never fetched.

**Hook 5 — FoxImaStreamIdLoader.requestVODDAIUrl()** (invoke onFailure + `return-void`)

Initiates the Google DAI stream request for VOD content. The DAI-stitched stream embeds ad segments directly into the HLS manifest before the player ever sees the URL — Hooks 1–4 can't remove segments that are physically baked into the stream. This hook replicates a DNS block for `dai.google.com` in bytecode: we call `onFailure("dai_blocked")` to trigger Tubi's fallback path, then return void to prevent the DAI request from going out. The fallback serves a clean, non-stitched stream.

**Hook 6 — FoxImaStreamIdLoader.requestImaStreamId()** (invoke onFailure + `return-void`)

Live stream equivalent of Hook 5. Same pattern, same result.

**Hook 7 — xo/C$c.shouldInterceptRequest(WebView, WebResourceRequest)** (pure smali)

The architectural keystone. `xo/C$c` is Tubi's `TubiWebClient` (R8-minified `TvWebFragment$TubiWebClient`), which extends `WebViewClient`. Android calls `shouldInterceptRequest()` for every network resource the WebView loads — scripts, XHR/fetch requests, tracking pixels, ad config calls.

The hook checks the request URL host against a list of known ad domains. If matched, it returns an empty `WebResourceResponse` (HTTP 200, empty body), silently dropping the request before it reaches the network. Non-ad requests fall through to the original code path unchanged.

This is implemented in **pure smali** with no Java extension dependency — lessons learned from a painful multi-build debugging arc involving ART verifier errors, missing extension classes, and namespace mismatches that are all documented in the repo comments for anyone who wants to learn from them.

**Blocked domains in Hook 7:**

- `dai.google.com` — Google DAI stream stitching

- `imasdk.googleapis.com` — IMA SDK JavaScript loader

- `doubleclick.net` — Google ad delivery

- `googletagmanager.com` — Ad tag manager

- `googlesyndication.com` — Google ad syndication

- `adrise.tv` — Tubi's own AdRise ad platform (`s.adrise.tv` = ad content, `license.adrise.tv` = ad DRM)

- `ads.production-public.tubi.io` — Tubi's ad config/orchestration server

***This apk patch DOES NOT block/reduce telemetry. There are other useful tools for that and I don't want to spend time digging into that***

## DNS Filtering Highly Recommended for Full Pre-Roll Suppression

This is where Tubi's architecture throws a curveball that no bytecode patch alone can fully solve.

Android's WebView has a documented limitation: **`shouldInterceptRequest` is not called for requests made by media elements.** This is by design in Android's security model. When the SPA's JavaScript sets the `src` of a `<video>` element to load the ad video from `s.adrise.tv`, Android's WebView never routes that request through `shouldInterceptRequest`. Hook 7 simply never sees it.

Hook 7 correctly blocks everything it *can* see — the IMA SDK script load, ad config fetches, tracking pixels. But the actual video file request bypasses it entirely via the media element exception.

**The solution is DNS-level filtering**, which operates at the network layer and applies equally to all processes and all request types, including media element requests from the WebView renderer.

Add the following rules to your DNS ad blocker of choice (Pi-hole, AdGuard Home, NextDNS, or similar):

||s.adrise.tv^

||ads.production-public.tubi.io^

||rainmaker.production-public.tubi.io^

||servedby.flashtalking.com^

||insightexpressai.com^

With these DNS rules in place alongside the patch, pre-roll suppression on my Onn 4K TV was confirmed clean across multiple content types that were previously triggering ads. **The patch handles everything in the native Java layer; the DNS rules handle the WebView media layer. You need both for complete suppression.**

If you don't have a DNS-level ad blocker on your network, the patch alone will still suppress mid-rolls, post-rolls, pause ads, and the IMA DAI pipeline — but some pre-rolls served via Tubi's AdRise platform may still appear.

---

## Installation

**Add the repo to Morphe:**

`https://morphe.software/add-source?github=ajstrick81/morphe-androidtv-patches`

**Step by step:**

  1. Download Tubi Android TV APK v10.20.5000 from APKMirror to your phone/tablet
  2. Load the APK into Morphe Manager and select the Tubi patch
  3. Allow patching to complete, then save the patched APK (don't click install — save using the bottom right corner option)
  4. Uninstall the existing Tubi app from your Android TV device first
  5. Install via one of these methods:- **Option A:** ADB streamed install — connect phone to TV via ADB and run `adb install <patched_apk>`- **Option B:** Use "Send Files to TV" app to transfer the patched APK to the device, then install from a file manager
  6. Add the DNS filtering rules above to your network-level ad blocker
  7. Enjoy ad-free Tubi on Android TV!

---

## What This Means for Fox Sports and Fox One

Why these are my next target??? Because we now understand that Fox Corp uses the same WebView-SPA hybrid architecture across all their apps, the interception technique from Hook 7 — combined with DNS filtering for Fox's ad domains — should be directly applicable to Fox Sports and Fox One. Those patches are on the roadmap. Also, with the World Cup starting in a few weeks I'm sure there will be a lot of people using these apps for their viewing pleasure.

---

As always, huge thanks to the Mod Team, to RookieEnough and Hoodles for building such a great community around Morphe, and to everyone who has tested and given feedback. The debugging arc on this one was long, but every hard lesson is now documented in the repo for the community. See you in the next one!

reddit.com
u/Imaginary-Storage957 — 3 months ago
▲ 150 r/MorpheApp

Introducing an Android TV Morphe Patch for HBO Max

Hey Everyone!

I'm back with another patch for the Android TV community, and this one was quite the journey! After successfully patching Paramount+ and helping move the Disney+ patch to an Android TV-focused repo, I decided to take on HBO Max — and with a lot of help from AI, we got there!

First, the same disclaimer as before: I am not a coder. I work in healthcare and have used Claude extensively to problem solve, analyze bytecode, and write the patch code. This was a deep collaborative process involving dex file autopsies, smali analysis, fingerprint matching, and a lot of trial and error. If AI-assisted patches aren't your thing, totally fair — but it works, and that's what matters!

What the patch does:

The HBO Max patch suppresses six distinct ad delivery systems built into the APK:

  • Bolt nonlinear overlay ads
  • BoltDynamicAdFetcher coroutine ad result
  • SsaiInfoTimelineBuilder SSAI linear ad timeline registration (VOD/movies)
  • The synthetic accessor that closes the lambda call path
  • GenerateLiveTimelineEntries for live and episodic TV prerolls
  • Nowtilus SSAI plugin initialization (MediaMelon CDN-level ad stitching)

Important — free tier limitation:

If you are on Max's free ad-supported tier, you will still see some ads. These are baked directly into the HLS/DASH video stream at WBD's origin CDN before the content ever reaches your device. They are structurally impossible to remove from the client side — no DNS block, no APK patch, and no client-side intercept can touch them because they are literally encoded into the video segments themselves. There is no workaround for this. The good news is that these remaining ads are skippable using your remote, so you can fast forward right past them.

This is NOT an app unlocker.

I want to be crystal clear about this: the patch does not grant access by unlocking premium features like 4k content, bypassing authentication, or giving you a subscription for free. You must have a valid Max account and active subscription to use the app. All this patch does is suppress the ad delivery systems for subscribers who prefer an ad-free experience. Please do not post asking how to get free access to content — that is not what this is.

Add: https://morphe.software/add-source?github=ajstrick81/morphe-androidtv-patches

Quick step by step:

  1. Download the specified Android TV APK bundle version here: HBO Max (Android TV) to your phone/tablet
  2. Load the apkm file into Morphe for patching (select "No, I already have an APK")
  3. Allow the code to do its thing and then save the new patched APK file (don't click install — save in the bottom right corner)
  4. Delete the old APK from your Android TV device before installing the new one
  5. Option 1: Connect your phone to your Android TV device via ADB and perform a streamed install
  6. Option 2: Use the "Send Files to TV" app to transfer the patched APK to the Android TV device
  7. Enjoy mostly ad-free Max on Android TV!

Huge thanks to RookieEnough and Hoodles for being the inspiration and for building such a great community around Morphe. And of course thank you to everyone who has tested and given feedback. See you in the next one!

reddit.com
u/Imaginary-Storage957 — 3 months ago
▲ 307 r/MorpheApp

Introducing Android TV Morphe Patches for Disney+ and Paramount+

Hey Everyone!

I'm a huge fan of this community and all the hard work the developers have put into making Morphe an awesome success. I wanted to see if I could contribute in some way, and it turns out that I could learn from the best (RookieEnough and Hoodles!) and give back to the community.

I would like to take a moment to acknowledge the fact that I am not a coder. I work in healthcare and have a decent idea of how things work, but I am in no way experienced in bytecode patching.

With that said, here's a fair warning. I've deployed Claude to help me build my Github Repo. I have used AI extensively to problem solve and write code for the Paramount+ patch. I know it's not what everyone on Reddit wants, but hey your choice - an AI generated patch for ad-free Paramount+, pay for Paramount Premium, or Paramount+ with ads. I know which one I would choose.

Please note that the Disney+ patch code is NOT my work. All credit goes to RookieEnough's hard work in creating the GitHub repo that patches that apk. I simply thought it would be helpful to move the code to a place that is exclusive to Android TV patches. I am taking NO CREDIT for the Disney+ patch and I am not trying to steal his work. You can see my previous post that also outlines where the idea to patch Android TV apks started: https://www.reddit.com/r/MorpheApp/comments/1t77vw2/disney_android_tv/

With that out of the way, here's the repo link you can add to your Morphe app: https://github.com/ajstrick81/morphe-androidtv-patches/tree/main

Here's a quick step by step:

  1. Download the specified Android TV apk bundle version listed in the repo from ApkMirror.com to your phone/tablet
  2. Load the apkm file into Morphe for patching (select "No, I already have an APK")
  3. Allow the code to do it's thing and then save the new patched apk file (Don't click install, instead save in the bottom right corner)
  4. Delete the old apk from your Android TV device before installing the new one
  5. Option 1: Connect your phone to your Android TV device via ADB and perform a streamed install to the device
  6. Option 2: Use the "Send Files to TV" app to transfer the patched apk to the Android TV device
  7. Enjoy ad-free TV shows and movies on each platform!
reddit.com
u/Imaginary-Storage957 — 3 months ago