# Android Streaming App Hardening Audit

Use this reference when a mature Android TV/streaming app needs low-risk security, lint, backend, and playback hardening without changing UX.

## Sequence that worked

1. Start from a clean tree and recent log; treat handoff/fix plans as leads, not truth.
2. Run current baselines before editing:
   ```bash
   ./gradlew :app:lintDebug --console=plain
   ./gradlew :app:compileDebugKotlin :app:testDebugUnitTest --console=plain
   npm --prefix services/catalog-worker run typecheck
   npm --prefix services/catalog-worker run test:smoke
   ```
3. Group failures by family and patch one area at a time. Verify after each batch.
4. Commit only after full Android + service verification and an independent review.

## Android lint hardening patterns

- `MissingTvBanner`: add a simple TV banner drawable and reference it from the app manifest. This is low-risk and preserves UX.
- `UnsafeOptInUsageError` for Media3: add explicit `androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)` at the file or declaration using unstable APIs.
- `UnusedContentLambdaTargetStateParameter` in Compose animation code: suppress narrowly at the affected function if the API shape is intentional.
- `NewApi` clusters around crypto/Keystore/desugared Java APIs: decide whether the product can raise `minSdk` first. For Android TV apps, `minSdk 23` plus core-library desugaring can remove many legacy wrappers without behavior change, but document the support tradeoff.

Gradle shape:
```kotlin
android {
    defaultConfig { minSdk = 23 }
    compileOptions { isCoreLibraryDesugaringEnabled = true }
}

dependencies {
    coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:<current>")
}
```

## Network logging and secret hygiene

For Retrofit/OkHttp modules that may include API keys or bearer tokens, production logging must be off even if sanitized logging exists elsewhere:

```kotlin
val logging = HttpLoggingInterceptor().apply {
    level = if (BuildConfig.DEBUG) {
        HttpLoggingInterceptor.Level.BODY
    } else {
        HttpLoggingInterceptor.Level.NONE
    }
}
```

Then scan source/config diffs, not generated build artifacts, for added `api_key`, `token`, `Authorization`, `Log.`, and `HttpLoggingInterceptor.Level.BODY` patterns.

## Playback fallback correctness

Media3 player exceptions do not automatically prove the ViewModel's fallback path is wired. When hardening playback:

- Collect `playerManager.playbackState` or equivalent and trigger fallback when Media3 reports a real playback exception.
- Track a handled error key per active URL so the same player error does not loop fallback repeatedly.
- Reset `attemptedUrls`, failure counters, and handled-error keys on every new content/player initialization.
- Treat a stream's `resolvedUrl`, `debridUrl`, and original `url` as one identity set for retry/fallback decisions. A debrid failure may surface as one URL variant while selection logic compares another.
- If the product prioritizes debrid, enforce the hierarchy in the filter phase, not only by sort score. Exhaust cached/debrid candidates by resolution before allowing free/non-cached sources.

Regression tests should cover:
- First failure advances to the next candidate.
- A failed `debridUrl` marks the same stream attempted even if its original `url` differs.
- Lower-resolution debrid beats higher-resolution free source when debrid priority is promised.

## Catalog Worker / backend relay hardening

For Cloudflare Worker or similar catalog relays:

- Whitelist accepted query parameters before building cache keys. Unknown extras should not shard cache entries or leak through to providers.
- Normalize cache keys from effective params only.
- Put bounded timeouts around upstream provider fetches.
- Do not echo upstream/internal exception messages to clients. Return a generic public error such as `Upstream service error` and keep internals in logs only if they are secret-safe.
- Typecheck and smoke-test the worker separately from Android.

## Verification checklist

```bash
./gradlew :app:compileDebugKotlin :app:testDebugUnitTest :app:lintDebug --console=plain
npm --prefix services/catalog-worker run typecheck
npm --prefix services/catalog-worker run test:smoke
git diff --check
```

Run an independent reviewer after the first pass. If it finds fallback/source-order edge cases, fix them and rerun the same verification before commit.
