btsnoop_parser 1.0.0

Rust parser for the btsnoop format
Documentation
use crate::data::ATTCommand;
use crate::parse_btsnoop_file;
use jni::sys::{JNI_VERSION_1_6, jint};
use jni::{JNIEnv, NativeMethod};
use std::ffi::c_void;

#[unsafe(no_mangle)]
unsafe extern "system" fn log_impl(
    mut env: JNIEnv,
    _class: jni::objects::JClass,
    text: jni::objects::JString,
) {
    let tag = env.new_string("BTSnoopParser").unwrap();
    let log_class = env.find_class("android/util/Log").unwrap();

    env.call_static_method(
        log_class,
        "i",
        "(Ljava/lang/String;Ljava/lang/String;)I",
        &[
            jni::objects::JValue::Object(&tag),
            jni::objects::JValue::Object(&text),
        ],
    )
    .unwrap();
}

#[unsafe(no_mangle)]
unsafe extern "system" fn parse_impl(
    #[allow(unused_mut)] mut env: JNIEnv,
    _class: jni::objects::JClass,
    file_bytes: jni::objects::JByteArray,
    write_and_hvn_only: jni::sys::jboolean,
    sort_by_timestamp: jni::sys::jboolean,
) -> jni::sys::jstring {
    let file_bytes = env.convert_byte_array(file_bytes).unwrap();
    let message = env.new_string("Parsing Bluetooth Snoop File").unwrap();
    unsafe {
        let env2 = env.unsafe_clone();
        log_impl(env2, _class, message);
    }

    match parse_btsnoop_file(file_bytes) {
        Ok(mut parsed_file) => {
            if write_and_hvn_only == jni::sys::JNI_TRUE {
                parsed_file.packets.retain(|packet| {
                    packet.att_header.as_ref().map(|f| f.command) == Some(ATTCommand::WriteCommand)
                        || packet.att_header.as_ref().map(|f| f.command)
                            == Some(ATTCommand::HandleValueNotification)
                });
            }
            if sort_by_timestamp == jni::sys::JNI_TRUE {
                parsed_file
                    .packets
                    .sort_by_key(|packet| packet.header.timestamp_milliseconds);
            }
            match serde_json::to_string(&parsed_file) {
                Ok(json) => match env.new_string(json) {
                    Ok(j) => j.into_raw(),
                    Err(_) => std::ptr::null_mut(),
                },
                Err(e) => {
                    let error_msg = format!("Serialization error: {}", e);
                    match env.new_string(error_msg) {
                        Ok(s) => s.into_raw(),
                        Err(_) => std::ptr::null_mut(),
                    }
                }
            }
        }
        Err(e) => {
            let error_msg = format!("Parse error: {}", e);
            match env.new_string(error_msg) {
                Ok(s) => s.into_raw(),
                Err(_) => std::ptr::null_mut(),
            }
        }
    }
}

#[unsafe(no_mangle)]
unsafe extern "system" fn JNI_OnLoad(vm: jni::JavaVM, _: c_void) -> jint {
    let mut env = match vm.get_env() {
        Ok(env) => env,
        Err(_) => return -1,
    };

    let class_name = "one/nullstring/btsnoop_parser/BTSnoopParser";
    let clazz = match env.find_class(class_name) {
        Ok(class) => class,
        Err(_) => return -1,
    };

    let methods = [
        NativeMethod {
            name: "log".into(),
            sig: "(Ljava/lang/String;)V".into(),
            fn_ptr: log_impl as *mut _,
        },
        NativeMethod {
            name: "parse".into(),
            sig: "([BZZ)Ljava/lang/String;".into(),
            fn_ptr: parse_impl as *mut _,
        },
    ];

    if let Err(_) = env.register_native_methods(clazz, &methods) {
        return -1;
    }

    JNI_VERSION_1_6 as jint
}