Files
philips-tv-speicher-voll/diagnostics/epg-analysis.md

17 KiB

Konsolidierte Analyse von EpgDatabase.db

Stand: 2026-08-25
Gerät: Philips 43PUS7394/12, TPM191E, Android TV 12
Firmware: TPM191E_R.211.000.103.000

Ergebnis in einem Satz

/data/user/0/org.droidtv.channels/databases/EpgDatabase.db ist mit 1.742.053.376 Bytes der konkrete große Speicherverbraucher. Phase 4 hat die früher offene Verteilung direkt an einer hashidentischen, integritätsgeprüften Kopie entschieden: scheduletable enthält 3.071.250 Zeilen, alle zum Snapshot abgelaufen; Tabelle plus vier Indizes belegen rund 1.659 MiB. freelist_count=0, auto_vacuum=FULL. Die 1,62 GiB sind daher fast vollständig echte jahrelange Schedule-Altlast, nicht Freelist oder FVP. Details: phase4/epg-database-forensics.md.

Beweisgrade

  • BEWIESEN: direkt aus lokalem Manifest, DEX-Bytecode, SQL-Literal, Bugreport oder abgelegter Rohdiagnose.
  • SEHR WAHRSCHEINLICH: mehrere unabhängige Belege ergeben dieselbe Erklärung, eine Laufzeitvariable bleibt offen.
  • PLAUSIBEL: technisch passende Interpretation ohne direkten Beleg der Entwicklerabsicht oder konkreten Laufzeitwirkung.
  • NICHT VERIFIZIERBAR: mit dem derzeitigen lesenden Zugriff beziehungsweise den lokalen Artefakten nicht entscheidbar.

Diese Fassung konsolidiert ausschließlich Diagnose. Es wurde kein Cleanup, Delete, Rebuild, App-Reset oder sonstiger Reparaturpfad ausgeführt.

1. Datenbank und Schema

Eigentümer und Version

  • BEWIESEN: Paket org.droidtv.channels
  • BEWIESEN: Helper org.droidtv.channels.epg.bcepg.EpgDatabaseHelper
  • BEWIESEN: Superklasse android.database.sqlite.SQLiteOpenHelper
  • BEWIESEN: Dateiname EpgDatabase.db
  • BEWIESEN: Schemaversion 6, als Integerargument im Helper-Konstruktor

Physische Tabellen

EpgDatabaseHelper.createTables() legt an:

  • mappingtable
  • indextable
  • nowtable
  • nexttable
  • scheduletable
  • fvpscheduletable
  • fvpnownexttable
  • channelmapversiontable
  • fastipnownexttable

mappingtable, indextable, nowtable, nexttable, scheduletable und fastipnownexttable besitzen Integer-Primärschlüssel; mehrere davon sind AUTOINCREMENT. Die FVP-Tabellen verwenden den zusammengesetzten Primärschlüssel (channelid,starttime,endtime) und haben keine _id-Spalte.

BEWIESEN: Der URI-Name nownexttable bezeichnet keine zusätzliche physische Tabelle. Der Provider erzeugt dafür den Join:

indextable AS Dbit LEFT JOIN nowtable AS Now USING(presetid) LEFT JOIN nexttable AS Next USING(presetid)

Die vollständigen DDL-Literale und Indizes sind in diagnostics/deep-forensics/philips/epg-reverse-agent.md sowie epg-dex-context.txt dokumentiert.

2. Bisher gemessene Größe und Churn

Vorhandene lesende Rohdaten:

Objekt aktuelle Zeilen beobachtete IDs
indextable 358 1.267.806 bis 1.268.163
nowtable 195 4.431.681 bis 4.451.328
nexttable 162 4.026.589 bis 4.027.685
Join-Endpunkt nownexttable 358 keine eigene physische ID
fastipnownexttable 0 keine aktuellen Zeilen
  • BEWIESEN: Die hohen AUTOINCREMENT-Werte bei kleinen aktuellen Tabellen zeigen millionenfachen historischen Insert/Delete-Churn.
  • BEWIESEN: Diese Messung umfasst nicht scheduletable, fvpscheduletable und fvpnownexttable.
  • BEWIESEN: Eine unbeschränkte lesende Abfrage von antenna/scheduletable mit Projektion _id lief länger als 120 Sekunden und wurde clientseitig abgebrochen.
  • SEHR WAHRSCHEINLICH: Die Schedule-Menge ist sehr groß oder die Schedule-Abfrage leidet unter einer schwerwiegenden Provider-/SQLite-Latenz. Der Lauf beweist keine genaue Zeilenzahl und keinen konkreten Byteanteil.

Die frühere Kurzannahme „wenige aktuelle Zeilen, also fast alles Freelist“ ist damit nicht haltbar. Die kleinen Now/Next-Werte sagen nichts über Schedule/FVP aus.

3. Der Stale-Cleanup ist ein No-op

Aufrufkette

Beim Provider-Start:

  1. EpgProvider$1.run() synchronisiert Index und Kanalmapping.
  2. Es postet EpgProvider$deleteStaleDataRunnable nach exakt 120.000 ms.
  3. Die Logmeldung lautet Posting stale data deletion after 2 mins in onCreate.

Alternativer Alarmweg:

  1. EpgDatabaseHelper.setEpgStaleDataCleanUpAlarm() würde einen RTC-Alarm auf currentTimeMillis + 43.200.000 ms setzen.
  2. Empfänger: EpgStaleDataCleanupAlarmReceiver
  3. Action: org.droidtv.channels.epg.bcepg.EpgStaleDataCleanupAlarmReceiver.CLEAR_STALE_DATA
  4. Der Receiver ruft EpgInfoSingleton.cleanEpgStaleData() auf.
  5. EpgInfoSingleton$8.run() ruft den Provider-Delete-Endpunkt antenna/staledatacleanup auf.
  6. Der Provider postet dasselbe deleteStaleDataRunnable.

BEWIESEN: In den vier DEX-Dateien von channels.apk existiert kein direkter Invoke-Aufrufer für setEpgStaleDataCleanUpAlarm(). Der Receiver plant sich nicht erneut ein. Ein externer oder reflektiver Aufruf ist nicht belegt.

Inhalt des Runnables

EpgProvider$deleteStaleDataRunnable.run() macht bei definierter TV-Uhr:

  1. System.currentTimeMillis() lesen
  2. 1800 direkt subtrahieren
  3. bei FreeView zusätzlich 604800 direkt subtrahieren
  4. Deleting stale data for time <Grenzwert> protokollieren

BEWIESEN: Danach folgt keine Mutation. In der vollständigen Methode fehlen:

  • SQLiteDatabase.delete() oder execSQL()
  • ContentResolver.delete() oder applyBatch()
  • EpgProgramPump.deleteChannelProgrambytime()
  • EpgProgramPump.deleteScheduleProgramsByTime()
  • irgendein anderer sichtbarer Löschaufruf

Ist die Uhr nicht definiert, wird nur nach 30.000 ms erneut gepostet.

BEWIESEN: Der Boot-Log belegt exakt diesen No-op-Pfad, keine gelöschte Zeile:

  • Logzeit: 2026-08-25 11:11:48.657
  • ausgegebener Grenzwert: 1787649106837
  • Grenzwert als Lokalzeit: 2026-08-25 11:11:46.837

Die Differenz von rund 1,82 Sekunden passt zum direkt als Millisekunden subtrahierten Literal 1800.

Sichtbarer Einheitenfehler

  • BEWIESEN: Basis ist Millisekundenzeit, die Konstanten werden ohne Faktor 1.000 subtrahiert.
  • PLAUSIBEL: 1800 sollte 30 Minuten in Sekunden und 604800 sieben Tage in Sekunden bedeuten.
  • Selbst bei korrekter Einheit bliebe der aktuelle Pfad wirkungslos, weil kein Delete folgt.

Vorhandene, aber nicht verdrahtete Löschmethoden

  • EpgProgramPump.deleteChannelProgrambytime(long) löscht Now, Next und Schedule mit endtime < ? per Batch.
  • EpgProgramPump.deleteScheduleProgramsByTime(long) löscht Schedule mit endtime < ?.
  • EpgSyncThread.deleteScheduleProgramsByTime(long) delegiert an die vorige Methode.

BEWIESEN: Keine dieser Methoden hat in den vier DEX-Dateien einen direkten Caller. Das Cleanup-Runnable ruft keine davon auf.

4. Rebuild-Semantik

Der Provider org.droidtv.channels.epg.bcepg.EpgProvider ist im Manifest mit Authority org.droidtv.epgdvb und android:exported="true" eingetragen. Das Provider-Element deklariert keine eigene Read-/Write-Permission.

Die drei relevanten Endpunkte werden über ContentResolver.delete() aktiviert und sind deshalb mutierend. Sie sind hier nur dokumentiert:

Endpunkt Codepfad Wirkung
antenna/staledatacleanup deleteStaleDataRunnable aktuell nur Log/No-op
antenna/rebuilddb EpgProvider$rebuildDbRunnable Index leeren, Senderindex und Mapping abgleichen, selektiv EPG-Zeilen anpassen
antenna/rebuilddbonnewchannellist EpgProvider$rebuildDbOnNewChannelListRunnable EPG-Ereignis-/Index-/Versionszeilen leeren, Senderindex/Mapping neu aufbauen

EpgDatabaseHelper.dropTables() droppt entgegen seinem Namen keine Tabellen. Es führt SQLiteDatabase.delete(table,null,null) für Index, Now, Next, Schedule, beide FVP-Tabellen, Channelmap-Version und Fast-IP-Now/Next aus. mappingtable wird anschließend in syncOnChannelTableChange() separat neu aufgebaut.

BEWIESEN: Die Rebuild-Runnables führen kein explizites VACUUM aus.

Interne Rebuild-Aufrufer:

  • Senderlisten-Update-Ende und Installationsabbruch: regulärer rebuilddb
  • CHANNEL_INSTALL_COMPLETE: umfassender rebuilddbonnewchannellist

Das sind Lifecycle-Pfade der Senderinstallation, kein gefundener Benutzer-Reset.

5. Kein Philips-UI-Reset gefunden

Gefundene EPG-nahe Aktionen öffnen Guide, Planner, Now/Next, Aufnahmeliste oder Aufnahme-/Reminder-Dialoge. Keine Manifest-Activity und kein Service besitzt eine Reset-/Clear-/Rebuild-Aktion für die EPG-Datenbank.

Auch die Ressourcenquersuche fand keine Benutzerzeichenkette für „EPG-Datenbank zurücksetzen/löschen“.

Das Umschalten der TV-Guide-Quelle ist kein Ersatz. tvstrings.apk warnt ausdrücklich:

Wenn Sie zu TV-Guide-Informationen aus dem Internet wechseln, gehen alle Aufnahmen verloren, die Sie mit Informationen der Sendeanstalt aufgenommen haben. Alle Erinnerungen und programmierten Aufnahmen werden abgebrochen.

Die Gegenrichtung enthält eine entsprechende Warnung.

BEWIESEN: Ein Wechsel Broadcaster ↔ Internet kann Aufnahme-/Timerdaten beeinträchtigen und darf nicht als harmlose Speicherbereinigung behandelt werden.

6. Abhängigkeiten

Senderliste

  • Kanonische Quelle: android.media.tv.TvContract.Channels.CONTENT_URI
  • indextable spiegelt Preset-/Senderdaten einschließlich favoriteNumber.
  • mappingtable verbindet presetid mit svlid/svlrecid.
  • Rebuild-Runnables lesen die Senderliste und bauen EPG-Spiegel neu auf; sie löschen die kanonische TvContract.Channels-Tabelle nicht direkt.

Favoriten

  • Separater Provider: org.droidtv.tv.provider.favorite
  • Unter anderem FavoriteList, Mixed-, Terrestrial-, Cable- und Satellite-Listen
  • EpgDatabase.db speichert nur Spiegel-/Versionswerte.
  • Die Rebuild-Runnables enthalten keinen direkten Delete gegen den Favoritenprovider.

Aufnahmen, Erinnerungen und Timer

Persistenz außerhalb der EPG-Datenbank:

  • Authority org.droidtv.playtv.provider.recordremind
  • DB RecordingsRemindersList.db
  • RecordingsTable
  • RemindersTable
  • RecRemView

Diese Tabellen stehen weder im EPG-Schema noch in dropTables().

BEWIESEN: Ein EPG-Rebuild löscht diese externen Tabellen nicht direkt. EpgProgramPump korreliert Aufnahmen jedoch über EventId und ChannelId und aktualisiert Metadaten bei EPG-Ereignisänderungen.

NICHT VERIFIZIERBAR: Vollständige Nebenwirkungsfreiheit eines Rebuilds auf Event-ID-Verknüpfung, Darstellung, Folgeaufnahmen oder Scheduler. Externe Persistenz schützt vor direktem Tabellen-Delete, nicht zwingend vor Neuverknüpfungsproblemen.

7. auto_vacuum und Dateikompaktierung

  • BEWIESEN: In allen vier channels.apk-DEX-Dateien fehlen VACUUM, PRAGMA auto_vacuum, incremental_vacuum und wal_checkpoint.
  • BEWIESEN: Die App setzt den Modus nicht selbst und benutzt SQLiteOpenHelper/Plattform-SQLite.
  • BEWIESEN, aber nicht gerätespezifisch: Die AOSP-libsqlite-Buildkonfiguration nutzt mindestens im Android-11-Branch -DSQLITE_DEFAULT_AUTOVACUUM=1, also Default Full-Auto-Vacuum. Für Android 12 ist dieselbe Buildstruktur vorhanden.
  • SEHR WAHRSCHEINLICH: Der Philips-Vendor-Build könnte diese AOSP-Vorgabe übernommen haben.
  • NICHT VERIFIZIERBAR: Ob TP Vision die Option verändert hat, mit welchem Default die konkrete DB ursprünglich angelegt wurde und welcher Modus im DB-Header steht.

Folge: Fehlendes appseitiges VACUUM beweist nicht automatisch eine riesige Freelist. Bei aktivem Full-Auto-Vacuum kann SQLite freie Endseiten bei Transaktionsende zurückgeben. Umgekehrt kann eine alte, anders angelegte DB den Modus beibehalten.

Erforderliche lesende Metadaten für eine Entscheidung wären:

  • auto_vacuum
  • page_size
  • page_count
  • freelist_count
  • möglichst dbstat oder eine lesbare DB-Kopie

Diese Werte sind im unprivilegierten Providervertrag nicht als URI exponiert.

8. Query-Semantik und der Schedule-Hänger

antenna/scheduletable

Dieser URI fragt nicht direkt scheduletable ab, sondern:

indextable AS Dbit LEFT JOIN scheduletable AS Schedule USING(presetid)

Bei Projektion _id erzeugt der Provider Dbit.dbitindex AS _id. Es gibt kein Count und kein Limit. Der Client muss jede Join-Zeile entgegennehmen.

Darum kann der >120-s-Lauf durch sehr viele Schedule-Zeilen entstehen. Er kann keine genaue Anzahl oder Byteverteilung liefern.

Physischer Schedule-Pfad

antenna/scheduletablesync wird auf die physische scheduletable gemappt. Dieser Pfad vermeidet den Dbit-Join, besitzt aber keinen dokumentierten Count- oder Limit-Modus.

Die Tabelle hat Indizes auf endtime, starttime, presetid sowie (presetid,eventid). Enge zeitliche oder presetbezogene Selektionen sind deshalb die risikoärmste Möglichkeit, kleine Stichproben zu lesen.

FVP-Pfade

fvpscheduletable und fvpnownexttable haben keine _id; ihr Schlüssel ist (channelid,starttime,endtime). Der generische Provider-Projektionsrewriter ersetzt _id mit Dbit.dbitindex, was auf einer direkten FVP-Tabelle ungültig ist und den beobachteten Mappingfehler erklärt.

Spezialpfade wie fvpbackwardepg reichen Projektionen direkter an SQLite weiter. Ein Aggregate-Count könnte dort rein lesend funktionieren, ist aber nicht als offizieller Philips-Count-Vertrag implementiert.

Selection, Sortierung, Limit

  • GetNowNext, GetNowNextNoHidden, GetSchedule und getFastIpNowNextForChannel sind Provider-Kommandos im Selection-Feld, keine freien Where-Ausdrücke.
  • Normale Join-Selektionen werden an Whitespace zerlegt und Unterstriche werden in Punkte umgeschrieben.
  • sortOrder wird nur auf den Sonderwert PF_EVENT geprüft; dieser löst Sync aus und liefert keinen Cursor.
  • Interne Querypfade setzen eigene Sortierungen wie Dbit_presetnumber, Schedule_starttime oder starttime.
  • Der limit-Parameter der abschließenden 8-Argument-SQLiteDatabase.query() ist immer null.

BEWIESEN: Es gibt keinen generischen, legitimen LIMIT 1-Pfad und keinen eigenen Count-Endpunkt. Ein LIMIT im Sortstring wäre weder zuverlässig noch eine akzeptable Diagnosemethode.

Ein Aggregate-Ausdruck COUNT(*) AS row_count als Projektion ist je nach URI möglicherweise technisch ausführbar, wird aber auf Join-/generischen Pfaden umgeschrieben oder verworfen. Er ist daher ohne enges Timeout nicht belastbar. Eine eindeutige Schlüssel-Selection liefert garantiert höchstens einen Datensatz, zählt aber die Tabelle nicht.

9. Aktuelle Ursachenbewertung

Sicher

  • EpgDatabase.db belegt 1,62 GiB und ist der große konkrete Speicherverbraucher.
  • Historischer EPG-Churn liegt im Millionenbereich.
  • Der als Stale-Cleanup bezeichnete Kernpfad löscht nichts.
  • Schedule/FVP sind bisher nicht zuverlässig gezählt.
  • Der Schedule-Join ist unbeschränkt und lief länger als 120 Sekunden.
  • Appseitiges explizites VACUUM fehlt.

Sehr wahrscheinlich

  • Der No-op-Cleanup ermöglicht das langfristige Anwachsen abgelaufener Schedule-Daten.
  • Schedule und eventuell FVP enthalten einen erheblichen lebenden Datenbestand oder verursachen zumindest den dominanten Abfrageaufwand.
  • Die Philips-Speicherwarnung wird durch die große EPG-Datei ausgelöst beziehungsweise wesentlich verstärkt.

Offen

  • Lebende Bytes je Tabelle
  • Freelist-/Fragmentierungsanteil
  • tatsächlicher Auto-Vacuum-Modus
  • WAL-/Journalbeitrag
  • exakte Rebuild-Nebenwirkungen auf Aufnahme-/Timer-Verknüpfungen

10. Risikoarme weitere Strategie

Weiterhin ausschließlich lesend:

  1. Keine weitere unbeschränkte Ausgabe des scheduletable-Joins starten.
  2. Schedule nur über scheduletablesync in sehr engen, indexgestützten Zeit- oder Presetfenstern beproben und jeden Clientlauf kurz begrenzen.
  3. FVP nur über seine tatsächlichen Schlüsselspalten beziehungsweise geeignete Spezialpfade untersuchen; _id nicht verwenden.
  4. Aggregate-Projektionen nur als kurz begrenztes, rein lesendes Experiment auf Pfaden erwägen, die Projektionen statisch unverändert weiterreichen. Ein Fehlschlag darf nicht durch SQL-Injection umgangen werden.
  5. Nach Möglichkeit den DB-Header beziehungsweise auto_vacuum, page_count, freelist_count und page_size rein lesend beschaffen. Ohne diese Werte keine Aussage „überwiegend Freelist“ oder „überwiegend lebende Zeilen“ treffen.
  6. Vor jeder späteren mutierenden Maßnahme Senderlisten-, Favoriten-, Aufnahme- und Timer-Abhängigkeiten erneut prüfen und eine ausdrückliche Freigabe einholen.

Nicht ohne Freigabe ausführen:

  • staledatacleanup, rebuilddb oder rebuilddbonnewchannellist
  • App-Daten-/Cache-Löschung
  • Guide-Quellenwechsel
  • Datenbank-, Datei- oder Paketmanipulation

Belege

  • diagnostics/deep-forensics/philips/epg-reverse-agent.md
  • diagnostics/deep-forensics/philips/epg-dex-context.txt
  • diagnostics/deep-forensics/philips/epg-runnable-context.txt
  • diagnostics/deep-forensics/philips/epg-cleanup-context.txt
  • diagnostics/deep-forensics/philips/epg-stale-context.txt
  • diagnostics/deep-forensics/philips/epg-recording-deps-context.txt
  • diagnostics/deep-forensics/philips/epg-delete-rebuild-string-context.txt
  • diagnostics/deep-forensics/philips/channels-manifest.txt
  • diagnostics/deep-forensics/philips/tvstrings-epg-matches.txt
  • diagnostics/raw/storage/epg-content-evidence.txt
  • diagnostics/bugreport/extracted/files/01790__bugreport-PH9M_EA_5599-STT2.230526.001-2026-08-25-13-26-44.txt