@@ -8,12 +8,25 @@ use jni::objects::{GlobalRef, JValue};
88use jni:: JavaVM ;
99use std:: process;
1010use std:: sync:: { Arc , Mutex } ;
11+ use std:: time:: { Duration , Instant } ;
12+
13+ const EMERGENCY_KILL_HOLD_DURATION : Duration = Duration :: from_secs ( 10 ) ;
14+
15+ fn should_emergency_kill (
16+ down_time : Option < Instant > ,
17+ release_time : Instant ,
18+ threshold : Duration ,
19+ ) -> bool {
20+ down_time
21+ . and_then ( |pressed_at| release_time. checked_duration_since ( pressed_at) )
22+ . is_some_and ( |hold_duration| hold_duration >= threshold)
23+ }
1124
1225pub struct EvdevJniObserver {
1326 jvm : Arc < JavaVM > ,
1427 system_bridge : GlobalRef ,
1528 key_layout_map_manager : Arc < KeyLayoutMapManager > ,
16- power_button_down_time : Mutex < libc :: time_t > ,
29+ power_button_down_time : Mutex < Option < Instant > > ,
1730}
1831
1932impl std:: fmt:: Debug for EvdevJniObserver {
@@ -38,27 +51,21 @@ impl EvdevJniObserver {
3851 jvm,
3952 system_bridge,
4053 key_layout_map_manager,
41- power_button_down_time : Mutex :: new ( 0 ) ,
54+ power_button_down_time : Mutex :: new ( None ) ,
4255 }
4356 }
4457
4558 /// Handle power button emergency kill.
46- fn handle_power_button (
47- & self ,
48- ev_code : u32 ,
49- android_code : u32 ,
50- value : i32 ,
51- time_sec : libc:: time_t ,
52- ) {
59+ fn handle_power_button ( & self , ev_code : u32 , android_code : u32 , value : i32 ) {
5360 let mut time_guard = self . power_button_down_time . lock ( ) . unwrap ( ) ;
5461 // KEY_POWER scan code = 116
5562 if ev_code == 116 || android_code == android_codes:: AKEYCODE_POWER {
5663 if value == 1 {
57- * time_guard = time_sec ;
64+ * time_guard = Some ( Instant :: now ( ) ) ;
5865 } else if value == 0 {
5966 // Button up - check if held for 10+ seconds
60- let down_time = * time_guard;
61- if down_time > 0 && time_sec - down_time >= 10 {
67+ if should_emergency_kill ( * time_guard, Instant :: now ( ) , EMERGENCY_KILL_HOLD_DURATION )
68+ {
6269 // Must send log to Key Mapper for diagnostic purposes.
6370 warn ! ( "Emergency killing system bridge!" ) ;
6471 // Call BaseSystemBridge.onEmergencyKillSystemBridge() via JNI
@@ -72,7 +79,7 @@ impl EvdevJniObserver {
7279 }
7380 process:: exit ( 0 ) ;
7481 }
75- * time_guard = 0
82+ * time_guard = None
7683 }
7784 }
7885 }
@@ -173,7 +180,7 @@ impl EvdevJniObserver {
173180 } ;
174181
175182 // Handle power button emergency kill
176- self . handle_power_button ( ev_code, android_code, event. value , event . time . tv_sec ) ;
183+ self . handle_power_button ( ev_code, android_code, event. value ) ;
177184
178185 // Call BaseSystemBridge.onEvdevEvent() via JNI
179186
@@ -280,3 +287,71 @@ impl EvdevJniObserver {
280287 }
281288 }
282289}
290+
291+ #[ cfg( test) ]
292+ mod tests {
293+ use super :: { should_emergency_kill, EMERGENCY_KILL_HOLD_DURATION } ;
294+ use std:: time:: { Duration , Instant } ;
295+
296+ #[ test]
297+ fn no_down_event_never_triggers_emergency_kill ( ) {
298+ let release_time = Instant :: now ( ) ;
299+ assert ! ( !should_emergency_kill(
300+ None ,
301+ release_time,
302+ EMERGENCY_KILL_HOLD_DURATION
303+ ) ) ;
304+ }
305+
306+ #[ test]
307+ fn hold_duration_below_threshold_does_not_trigger_emergency_kill ( ) {
308+ let pressed_at = Instant :: now ( ) ;
309+ let release_time = pressed_at + Duration :: from_secs ( 9 ) ;
310+ assert ! ( !should_emergency_kill(
311+ Some ( pressed_at) ,
312+ release_time,
313+ EMERGENCY_KILL_HOLD_DURATION
314+ ) ) ;
315+ }
316+
317+ #[ test]
318+ fn hold_duration_at_threshold_triggers_emergency_kill ( ) {
319+ let pressed_at = Instant :: now ( ) ;
320+ let release_time = pressed_at + EMERGENCY_KILL_HOLD_DURATION ;
321+ assert ! ( should_emergency_kill(
322+ Some ( pressed_at) ,
323+ release_time,
324+ EMERGENCY_KILL_HOLD_DURATION
325+ ) ) ;
326+ }
327+
328+ #[ test]
329+ fn backward_time_jump_never_triggers_emergency_kill ( ) {
330+ let pressed_at = Instant :: now ( ) + Duration :: from_secs ( 5 ) ;
331+ let release_time = Instant :: now ( ) ;
332+ assert ! ( !should_emergency_kill(
333+ Some ( pressed_at) ,
334+ release_time,
335+ EMERGENCY_KILL_HOLD_DURATION
336+ ) ) ;
337+ }
338+
339+ #[ test]
340+ fn ignores_event_timestamp_drift_and_uses_monotonic_elapsed_time ( ) {
341+ // Simulate issue #1956: evdev event timestamps can drift/jump and suggest
342+ // a very long hold, even when real elapsed time is short.
343+ let fake_event_down_ts_sec = 1_000_i64 ;
344+ let fake_event_up_ts_sec = fake_event_down_ts_sec + 60 ;
345+ let event_timestamp_delta = fake_event_up_ts_sec - fake_event_down_ts_sec;
346+ assert ! ( event_timestamp_delta >= 10 ) ;
347+
348+ // Real elapsed time is still short (< 10s), so emergency kill must not trigger.
349+ let pressed_at = Instant :: now ( ) ;
350+ let release_time = pressed_at + Duration :: from_secs ( 2 ) ;
351+ assert ! ( !should_emergency_kill(
352+ Some ( pressed_at) ,
353+ release_time,
354+ EMERGENCY_KILL_HOLD_DURATION
355+ ) ) ;
356+ }
357+ }
0 commit comments