Skip to content

fix(android): Fix JNI signature mismatch causing SIGSEGV on Android 11+ #542

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

0xbrayo
Copy link
Member

@0xbrayo 0xbrayo commented Aug 10, 2025

  • Fixed initialize() function to return jstring instead of void to match Java signature
  • Changed jstring_to_string() to return Option instead of panicking on error
  • Updated all call sites to handle the Option return type safely

This resolves the crash on newer Android versions where JNI signature mismatch was causing segmentation faults during app initialization.


Important

Fixes JNI signature mismatch in initialize() and improves error handling in jstring_to_string() to prevent crashes on Android 11+.

  • Behavior:
    • initialize() in mod.rs now returns jstring instead of void to match Java signature, fixing crash on Android 11+.
    • jstring_to_string() in mod.rs now returns Option<String> instead of panicking, handling conversion errors gracefully.
  • Call Sites:
    • Updated all call sites in mod.rs to handle Option return type from jstring_to_string() safely, preventing potential crashes.

This description was created by Ellipsis for 04f5bb7. You can customize this summary. It will automatically update as commits are pushed.

Copy link

@ellipsis-dev ellipsis-dev bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Changes requested ❌

Reviewed everything up to 04f5bb7 in 1 minute and 21 seconds. Click for details.
  • Reviewed 111 lines of code in 1 files
  • Skipped 0 files when reviewing.
  • Skipped posting 5 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. aw-server/src/android/mod.rs:144
  • Draft comment:
    Note: The initialize() function’s signature has been updated to return a jstring, which now aligns with the Java side expectations. Consider using a thread-safe mechanism (like Once or an AtomicBool) for managing the INITIALIZED flag to prevent potential race conditions if initialize() is called concurrently.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 30% vs. threshold = 50% The first part about jstring return type is just describing what changed, not suggesting any improvements. The second part about thread safety is a valid concern since INITIALIZED is a global mutable static accessed in unsafe blocks. However, this appears to be Android JNI initialization code which typically runs once during app startup, so concurrent access is unlikely in practice. I may be underestimating the thread safety risks. Even if initialization typically happens once, there's no guarantee against concurrent calls. While thread safety is a valid concern, the comment doesn't provide strong evidence that this is actually problematic in the Android JNI context. The initialization pattern shown is fairly common in JNI code. The comment should be deleted as it's partly descriptive of changes rather than suggesting fixes, and the thread safety suggestion, while technically valid, lacks strong evidence that it's a real issue in this context.
2. aw-server/src/android/mod.rs:175
  • Draft comment:
    Observation: In setDataDir, the conversion of the input JString is safely handled via match. Optionally, consider logging an error message in the None branch to assist with debugging when the conversion fails.
  • Reason this comment was not posted:
    Confidence changes required: 50% <= threshold 50% None
3. aw-server/src/android/mod.rs:202
  • Draft comment:
    Observation: In createBucket, the use of pattern matching on jstring_to_string improves robustness by avoiding panics on conversion errors. This pattern is consistently applied in the subsequent functions.
  • Reason this comment was not posted:
    Confidence changes required: 0% <= threshold 50% None
4. aw-server/src/android/mod.rs:227
  • Draft comment:
    Observation: Similarly, in the heartbeat function, pattern matching on jstring_to_string for both bucket_id and event ensures that malformed JNI strings are gracefully handled. Again, consider logging these failures for easier troubleshooting.
  • Reason this comment was not posted:
    Confidence changes required: 50% <= threshold 50% None
5. aw-server/src/android/mod.rs:261
  • Draft comment:
    Observation: In getEvents, the pattern match on jstring_to_string is consistent with the other functions, which improves error handling and avoids panics if the JNI string conversion fails.
  • Reason this comment was not posted:
    Confidence changes required: 0% <= threshold 50% None

Workflow ID: wflow_j2Ge5B5xpTvubOSb

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

unsafe fn jstring_to_string(env: &JNIEnv, string: JString) -> String {
let c_str = CStr::from_ptr(env.get_string(string).expect("invalid string").as_ptr());
String::from(c_str.to_str().unwrap())
unsafe fn jstring_to_string(env: &JNIEnv, string: JString) -> Option<String> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: In the new implementation of jstring_to_string (lines 85–95), consider using the ? operator to simplify the error handling instead of nested matches. For example, you could write:

let java_str = env.get_string(string).ok()?;
let c_str = CStr::from_ptr(java_str.as_ptr());
return c_str.to_str().ok().map(|s| s.to_owned());

- Fixed initialize() function to return jstring instead of void to match Java signature
- Changed jstring_to_string() to return Option<String> instead of panicking on error
- Updated all call sites to handle the Option return type safely

This resolves the crash on newer Android versions where JNI signature mismatch was causing segmentation faults during app initialization.
Copy link

codecov bot commented Aug 11, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 69.76%. Comparing base (656f3c9) to head (ea88658).
⚠️ Report is 8 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #542      +/-   ##
==========================================
- Coverage   70.81%   69.76%   -1.06%     
==========================================
  Files          51       54       +3     
  Lines        2916     3152     +236     
==========================================
+ Hits         2065     2199     +134     
- Misses        851      953     +102     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@@ -136,7 +137,7 @@ pub mod android {
pub unsafe extern "C" fn Java_net_activitywatch_android_RustInterface_initialize(
env: JNIEnv,
_: JClass,
) {
) -> jstring {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there no way to catch these (so they match) programatically?

dirs::set_android_data_dir(&path);
}
None => {
// Failed to convert string - do nothing and return

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it make sense to debug log? Error log or smth?

What happens if you can't set the data dir? Does the rest handle that somewhat sanely?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, a debug log makes more sense, here. Will do.

let c_str = CStr::from_ptr(env.get_string(string).expect("invalid string").as_ptr());
String::from(c_str.to_str().unwrap())
unsafe fn jstring_to_string(env: &JNIEnv, string: JString) -> Option<String> {
let java_str = env.get_string(string).ok()?;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, did you find out how these can fail?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only figured out they were failing by using a lot of trace logs. Then updated them didn't investigate much about how exactly they were failing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants