Skip to content

Commit 54adb8f

Browse files
authored
Merge pull request #2133 from keymapperorg/fix/1956-system-bridge-emergency-kill-timestamps
#1956 fix emergency kill timestamp source
2 parents a3f39ab + f4addba commit 54adb8f

1 file changed

Lines changed: 89 additions & 14 deletions

File tree

evdev/src/main/rust/evdev_manager/jni/src/evdev_jni_observer.rs

Lines changed: 89 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,25 @@ use jni::objects::{GlobalRef, JValue};
88
use jni::JavaVM;
99
use std::process;
1010
use 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

1225
pub 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

1932
impl 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

Comments
 (0)