1+ package com.mapbox.maps.testapp.examples
2+
3+ import android.os.Handler
4+ import android.os.HandlerThread
5+ import android.os.Message
6+ import android.os.Process
7+ import android.util.Log
8+ import com.mapbox.maps.testapp.examples.Watchdog.TIME_TO_FIRST_TRIGGER
9+ import com.mapbox.maps.testapp.examples.Watchdog.TIME_TO_SUBSEQUENT_TRIGGER
10+ import com.mapbox.maps.testapp.examples.Watchdog.reschedule
11+ import com.mapbox.maps.testapp.examples.Watchdog.stop
12+
13+ /* *
14+ * A simple watchdog that will trigger a [Process.SIGNAL_QUIT] signal if [reschedule] is not called
15+ * within [TIME_TO_FIRST_TRIGGER] milliseconds and continues to do so every
16+ * [TIME_TO_SUBSEQUENT_TRIGGER] until [reschedule] or [stop] is called.
17+ */
18+ object Watchdog {
19+ var enabled = false
20+ set(value) {
21+ if (field == value) return
22+ field = value
23+ if (value) {
24+ // Start the watchdog thread when enabled to avoid unnecessary overhead
25+ watchdogHandlerThread = HandlerThread (TAG ).apply { start() }
26+ watchdogHandler = Handler (watchdogHandlerThread!! .looper)
27+ } else {
28+ // Stop the watchdog thread and free properties when disabled to avoid unnecessary overhead
29+ stop()
30+ watchdogHandlerThread?.quit()
31+ watchdogHandler = null
32+ watchdogHandlerThread = null
33+ }
34+ }
35+
36+ private var watchdogHandlerThread: HandlerThread ? = null
37+ private var watchdogHandler: Handler ? = null
38+ private var currentCounter = 0
39+
40+ private val quitSignalTask: () -> Unit = {
41+ Log .w(TAG , " (${currentCounter++ } ) Task not rescheduled on time. Triggering SIGNAL_QUIT." )
42+ // Send a quit signal to the current process to write a thread dump to `/data/anr/`.
43+ Process .sendSignal(Process .myPid(), Process .SIGNAL_QUIT )
44+ scheduleQuitSignalTask()
45+ }
46+
47+ private fun scheduleQuitSignalTask () {
48+ if (currentCounter >= MAX_CONSECUTIVE_TRIGGERS ) {
49+ Log .w(
50+ TAG ,
51+ " Max consecutive triggers ($currentCounter ) reached. Not scheduling another trigger."
52+ )
53+ return
54+ }
55+ if (enabled) {
56+ watchdogHandler?.let {
57+ it.sendMessageDelayed(Message .obtain(it, quitSignalTask), TIME_TO_SUBSEQUENT_TRIGGER )
58+ }
59+ }
60+ }
61+
62+ fun reschedule () {
63+ if (enabled) {
64+ stop()
65+ watchdogHandler?.let {
66+ it.sendMessageDelayed(Message .obtain(it, quitSignalTask), TIME_TO_FIRST_TRIGGER )
67+ }
68+ }
69+ }
70+
71+ fun stop () {
72+ // Cancel all pending tasks
73+ watchdogHandler?.removeCallbacksAndMessages(null )
74+ currentCounter = 0
75+ }
76+
77+ private const val TAG = " Watchdog"
78+
79+ /* *
80+ * The amount of time that need to pass before the watchdog triggers the first time.
81+ * That is, if [reschedule] is not called within this time, the watchdog task will trigger.
82+ * Unit is milliseconds.
83+ */
84+ private const val TIME_TO_FIRST_TRIGGER : Long = 50L
85+
86+ /* *
87+ * The amount of time that the task will wait before running again.
88+ * Unit is milliseconds.
89+ */
90+ private const val TIME_TO_SUBSEQUENT_TRIGGER : Long = 100L
91+
92+ private const val MAX_CONSECUTIVE_TRIGGERS = 5
93+ }
0 commit comments