Skip to content

Commit 89aeb91

Browse files
author
alex-hermes-agent
committed
chore(v1.22): FileLogger flight recorder — ROM filters logcat, gate/overlay/force-stop/fuse events now logged to app external dir
1 parent 1252faf commit 89aeb91

4 files changed

Lines changed: 79 additions & 11 deletions

File tree

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ android {
1515
applicationId = "com.alexk.tvlock"
1616
minSdk = 21
1717
targetSdk = 34
18-
versionCode = 22
19-
versionName = "1.21"
18+
versionCode = 23
19+
versionName = "1.22"
2020
}
2121

2222
if (!releaseKeystore.isNullOrBlank()) {
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.alexk.tvlock
2+
3+
import android.content.Context
4+
import android.os.SystemClock
5+
import java.io.File
6+
import java.io.FileWriter
7+
8+
/**
9+
* File-based event log.
10+
*
11+
* Why: the Rockteck ROM filters third-party apps' logcat output entirely —
12+
* Log.i/w/e from LockPollService never reach `adb logcat`, so field races
13+
* (fast-button press) were unmeasurable. This appends to
14+
* <externalFiles>/tvlock.log, which adb shell can read without root.
15+
*
16+
* Kept deliberately cheap: one line per event, monotonic clock + wall time,
17+
* file trimmed when it grows past 256 KB. Every gate-lifecycle decision point
18+
* writes here; treat it as the flight recorder.
19+
*/
20+
object FileLogger {
21+
22+
@Volatile private var writer: FileWriter? = null
23+
@Volatile private var logFile: File? = null
24+
25+
fun init(context: Context) {
26+
try {
27+
val dir = context.getExternalFilesDir(null) ?: context.filesDir
28+
logFile = File(dir, "tvlock.log")
29+
if ((logFile?.length() ?: 0L) > TRIM_AT) logFile?.delete()
30+
writer = FileWriter(logFile, true)
31+
android.util.Log.i("TVLOCK", "file logger at ${logFile?.absolutePath}")
32+
} catch (e: Exception) {
33+
android.util.Log.e("TVLOCK", "file logger init failed", e)
34+
}
35+
}
36+
37+
fun log(msg: String) {
38+
android.util.Log.i("TVLOCK", msg) // still emit in case logcat works on other ROMs
39+
val w = writer ?: return
40+
try {
41+
synchronized(w) {
42+
w.write("${System.currentTimeMillis()} up=${SystemClock.elapsedRealtime()} $msg\n")
43+
w.flush()
44+
}
45+
val f = logFile
46+
if (f != null && f.length() > TRIM_AT) {
47+
synchronized(w) {
48+
runCatching { w.close() }
49+
f.delete()
50+
writer = FileWriter(f, true).also { writer = it }
51+
}
52+
}
53+
} catch (_: Exception) { /* never break the lock over logging */ }
54+
}
55+
56+
private const val TRIM_AT = 256L * 1024
57+
}

app/src/main/java/com/alexk/tvlock/GateOverlay.kt

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,15 @@ object GateOverlay {
4949
try {
5050
wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
5151
val root = object : android.widget.FrameLayout(context) {
52-
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean = true
52+
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
53+
// dedicated launch buttons arrive here if the ROM dispatches
54+
// to the focused window; if the child still reaches YouTube,
55+
// these lines prove the interception either does or doesn't happen
56+
if (keyCode == 268 || keyCode == KeyEvent.KEYCODE_MENU) {
57+
FileLogger.log("overlay swallowed key $keyCode")
58+
}
59+
return true
60+
}
5361
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean = true
5462
override fun onTouchEvent(event: MotionEvent): Boolean = true
5563
}
@@ -84,9 +92,9 @@ object GateOverlay {
8492
root.requestFocus()
8593
view = root
8694
shownAt = now
87-
android.util.Log.i("TVLOCK", "blackout overlay shown")
95+
FileLogger.log("overlay SHOW")
8896
} catch (e: Exception) {
89-
android.util.Log.e("TVLOCK", "overlay show FAILED", e)
97+
FileLogger.log("overlay show FAILED: ${e.javaClass.simpleName}: ${e.message}")
9098
view = null
9199
}
92100
}
@@ -96,9 +104,9 @@ object GateOverlay {
96104
view = null
97105
try {
98106
wm?.removeView(v)
99-
android.util.Log.i("TVLOCK", "blackout overlay hidden")
107+
FileLogger.log("overlay HIDE (was up ${System.currentTimeMillis() - shownAt}ms)")
100108
} catch (e: Exception) {
101-
android.util.Log.e("TVLOCK", "overlay hide FAILED", e)
109+
FileLogger.log("overlay hide FAILED: ${e.javaClass.simpleName}: ${e.message}")
102110
}
103111
}
104112
}

app/src/main/java/com/alexk/tvlock/LockPollService.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ class LockPollService : Service() {
4848
override fun onCreate() {
4949
super.onCreate()
5050
instance = this
51+
FileLogger.init(this)
52+
FileLogger.log("service onCreate")
5153
handler = Handler(Looper.getMainLooper())
5254
usm = getSystemService(USAGE_STATS_SERVICE) as UsageStatsManager
5355
LockNotification.makeChannel(this)
@@ -98,7 +100,7 @@ class LockPollService : Service() {
98100
// Hard cap: the blackout must never outlive gate logic under any
99101
// circumstance, even a bug — an unremovable black screen is a brick.
100102
if (GateOverlay.isShown && now - GateOverlay.shownAt > OVERLAY_MAX_MS) {
101-
android.util.Log.w("TVLOCK", "overlay hard cap — forcing hide")
103+
FileLogger.log("overlay HARD CAP 10s — forcing hide (hold=$blackoutHold gateUp=$gateUp top=$t)")
102104
blackoutHold = false
103105
GateOverlay.hide()
104106
}
@@ -122,6 +124,7 @@ class LockPollService : Service() {
122124
// the gate never confirmed by the watchdog deadline, retry
123125
// raising it — the overlay keeps the screen dead meanwhile.
124126
if (now - gateRaisedAt > GATE_CONFIRM_MS) {
127+
FileLogger.log("hold: gate retry (top=$t launcher-state)")
125128
targetPkg?.let { raiseGate(it, now) }
126129
}
127130
return
@@ -162,7 +165,7 @@ class LockPollService : Service() {
162165
// evTs = the event's own timestamp; now-evTs = how late UsageStats
163166
// surfaced it to us. This is the detection gap the overlay has to
164167
// beat on a fast dedicated-button press.
165-
android.util.Log.i("TVLOCK", "detect lag=${now - evTs}ms pkg=$t")
168+
FileLogger.log("DETECT lag=${now - evTs}ms pkg=$t")
166169
raiseGate(t, now)
167170
}
168171
}
@@ -199,11 +202,11 @@ class LockPollService : Service() {
199202
am.javaClass.getMethod("forceStopPackage", String::class.java, Int::class.java)
200203
.invoke(am, t, 0)
201204
blackoutHold = true
202-
android.util.Log.i("TVLOCK", "force-stopped $t under blackout — holding")
205+
FileLogger.log("force-stop $t OK — blackout HOLDING")
203206
} catch (e: Exception) {
204207
// hidden API blocked on this ROM (likely on API 34) — the
205208
// blackout + gate still protect; this is best-effort only
206-
android.util.Log.w("TVLOCK", "force-stop unavailable: ${e.javaClass.simpleName}")
209+
FileLogger.log("force-stop DENIED: ${e.javaClass.simpleName}")
207210
}
208211
}
209212
}, 500)

0 commit comments

Comments
 (0)