Skip to content

android app freeze @ manager.adapters().await? - minimal android app example available? #450

@m0rph3usX

Description

@m0rph3usX

Hi there, I am new to android apps compiled in rust, so perphaps it's a really silly question, but I didn't get any idea why the app freezes when calling manager.adapters().await?.
I believe it has to do something with missing android permissions, but I am not really sure.
I've granted all permissions in the android app settings:


"Location"
"Nearby devices"
"Notifications"
"Sensors"

Describe the bug
Compiling and starting android app -> the app hangs when calling following function:
manager.adapters().await? So my last log entry is "call....0" here's my code:

debug!("call...");
getAllPermissions(); 
debug!("call...0");
let manager = Manager::new().await?;
debug!("call...1");
let adapters = manager.adapters().await?; // <<<<< app hangs here
debug!("call...2");
let central = adapters.into_iter().nth(0).unwrap();
debug!("call...3");

Expected behavior
Progess this function and view available BLE devices.

Actual behavior

App freezes on :
let adapters = manager.adapters().await?

My android manifest.xml:

<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.nonpolynomial.btleplug.android">
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
</manifest>

I request permissions at runtime - I use this code here (not the best but I think it is working)

use jni::objects::{JObject, JValue};
use jni::{JNIEnv, JavaVM};
use ndk_context;

use once_cell::sync::OnceCell;

pub static JAVAVM: OnceCell<JavaVM> = OnceCell::new();

pub fn get_vm() -> JavaVM {
    unsafe { JavaVM::from_raw(ndk_context::android_context().vm().cast()).unwrap() }
}

fn get_activity_ptr() -> jni::sys::jobject {
    ndk_context::android_context().context().cast()
}

fn has_permissions(env: &JNIEnv, permissions: &[&str]) -> bool {
    for &permission in permissions {
        let perm_jstring = env.new_string(permission).unwrap();
        let perm_obj: JObject = perm_jstring.into();
        // Nutze das Activity-Objekt direkt im Methodenaufruf!
        let permission_status = env
            .call_method(
                unsafe { JObject::from(get_activity_ptr()) },
                "checkSelfPermission",
                "(Ljava/lang/String;)I",
                &[JValue::Object(perm_obj)],
            )
            .unwrap()
            .i()
            .unwrap();

        if permission_status != 0 {
            // debug!("{} not granted", permissions.join(", "));
            return false;
        }
    }
    // debug!("{} granted", permissions.join(", "));
    true
}

fn request_permissions(env: &JNIEnv, permissions: &[&str]) {
    debug!("request_permission {}", permissions.join(", "));
    
    let string_class = env.find_class("java/lang/String").unwrap();
    let default_string = env.new_string("").unwrap();
    let permissions_array = env.new_object_array(permissions.len() as i32, string_class, default_string).unwrap();

    for (i, &permission) in permissions.iter().enumerate() {
        let java_permission = env.new_string(permission).unwrap();
        env.set_object_array_element(permissions_array, i as i32, java_permission).unwrap();
    }

    env.call_method(
        unsafe { JObject::from(get_activity_ptr()) },
        "requestPermissions",
        "([Ljava/lang/String;I)V",
        &[
            JValue::Object(JObject::from(permissions_array)),
            JValue::Int(0),
        ],
    ).unwrap();

    debug!("request_permissions done");
}

pub fn ensure_permissions(env: &JNIEnv, permissions: &[&str]) {
    debug!("ensure_permissions {}", permissions.join(", "));

    request_permissions(env, permissions);

    // if !has_permissions(env, permissions) {
    //     request_permissions(env, permissions);
    // } else {
    //     debug!("{} already granted", permissions.join(", "));
    // }
}

pub fn getAllPermissions() {
    let vm = get_vm();
    let env = vm.attach_current_thread().unwrap();

    let permissions = [
        "android.permission.ACCESS_COARSE_LOCATION",
        "android.permission.ACCESS_FINE_LOCATION",
        "android.permission.BLUETOOTH",
        "android.permission.BLUETOOTH_ADMIN",
        "android.permission.BLUETOOTH_SCAN",
        "android.permission.BLUETOOTH_CONNECT",
    ];

    for perm in &permissions {
        ensure_permissions(&env, &[perm]);
    }
}

#[no_mangle]
pub extern "C" fn JNI_OnLoad(vm: jni::JavaVM, _res: *const std::os::raw::c_void) -> jni::sys::jint {
    let env = vm.get_env().unwrap();
    // jni_utils::init(&env).unwrap(); // ggf. entfernen, wenn nicht kompatibel
    // btleplug::platform::init(&env).unwrap(); // Nur falls du btleplug verwendest und es kompatibel ist!
    let _ = JAVAVM.set(vm);
    jni::JNIVersion::V6.into()
}

Additional context
my TOML:



[package]
name = "slint-rust-template"
version = "0.1.0"
edition = "2021"

# android -->
[lib]
crate-type = ["cdylib"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
slint = { version = "1.8.2", features = ["backend-android-activity-06"] }
anyhow = "1.0.98"
btleplug = "0.11.8"

futures = "0.3.31"
futures-util = "0.3.31"
tokio = { version = "1.12", features = ["full"] }
tokio-stream = "0.1.17"
uuid = "1.17.0"
once_cell = "1"
chrono = "0.4.41"
strum = "0.27.1"
strum_macros = "0.27.1"
#jni = "0.21.1"
jni = "0.19.0"
android-activity = "0.6.0"
ndk-context = "0.1.1"

#ndk-glue = "0.7.0"
#ndk = "0.9.0"
#ndk = "0.8"
ndk-glue = "0.7"

android_logger = "0.13"
log = "0.4"
android-manifest = "0.2.0"
#simplersble = "0.10.3"
#android-manifest = "0.2.0"
#jni-utils = "0.1.1"


[build-dependencies]
slint-build = "1.8.2"


[package.metadata.android]
# Specifies the package property of the manifest.
package = "com.foo.bar"

# Specifies the array of targets to build for.
build_targets = [ "armv7-linux-androideabi", "aarch64-linux-android", "i686-linux-android", "x86_64-linux-android" ]

# Path to your application's resources folder.
# If not specified, resources will not be included in the APK.
#resources = "path/to/resources_folder"

# Path to the folder containing your application's assets.
# If not specified, assets will not be included in the APK.
#assets = "path/to/assets_folder"

# Name for final APK file.
# Defaults to package name.
apk_name = "myapp"



# See https://developer.android.com/guide/topics/manifest/uses-sdk-element
#
# Defaults to a `min_sdk_version` of 23 and `target_sdk_version` of 30 (or lower if the detected NDK doesn't support this).
[package.metadata.android.sdk]
min_sdk_version = 23
target_sdk_version = 30
max_sdk_version = 29

# See https://developer.android.com/guide/topics/manifest/uses-feature-element
#
# Note: there can be multiple .uses_feature entries.
[[package.metadata.android.uses_feature]]
name = "android.hardware.vulkan.level"
required = true
version = 1

# See https://developer.android.com/guide/topics/manifest/uses-permission-element
#
# Note: there can be multiple .uses_permission entries.
[[package.metadata.android.uses_permission]]
name = "android.permission.ACCESS_COARSE_LOCATION"

[[package.metadata.android.uses_permission]]
name = "android.permission.ACCESS_FINE_LOCATION"
max_sdk_version = 30

[[package.metadata.android.uses_permission]]
name = "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"

[[package.metadata.android.uses_permission]]
name = "android.permission.BLUETOOTH"
max_sdk_version = 30

[[package.metadata.android.uses_permission]]
name = "android.permission.BLUETOOTH_ADMIN"
max_sdk_version = 30

[[package.metadata.android.uses_permission]]
name = "android.permission.BLUETOOTH_CONNECT"

[[package.metadata.android.uses_permission]]
name = "android.permission.BLUETOOTH_SCAN"





# See https://developer.android.com/guide/topics/manifest/application-element
[package.metadata.android.application]

# See https://developer.android.com/guide/topics/manifest/application-element#debug
#
# Defaults to false.
debuggable = false

# Defaults to the compiled artifact's name.
label = "myApp"

# See https://developer.android.com/guide/topics/manifest/application-element#extractNativeLibs
extract_native_libs = true

# See https://developer.android.com/guide/topics/manifest/application-element#usesCleartextTraffic
uses_cleartext_traffic = true

# See https://developer.android.com/guide/topics/manifest/meta-data-element
#
# Note: there can be several .meta_data entries.
# Note: the `resource` attribute is currently not supported.
[[package.metadata.android.application.meta_data]]
name = "com.samsung.android.vr.application.mode"
value = "vr_only"

# See https://developer.android.com/guide/topics/manifest/activity-element
[package.metadata.android.application.activity]

# See https://developer.android.com/guide/topics/manifest/activity-element#config
#
# Defaults to "orientation|keyboardHidden|screenSize".
config_changes = "orientation"

# See https://developer.android.com/guide/topics/manifest/activity-element#label
#
# Defaults to the application's label.
label = "Activity Name"

# See https://developer.android.com/guide/topics/manifest/activity-element#lmode
#
# Defaults to "standard".
launch_mode = "singleTop"

# See https://developer.android.com/guide/topics/manifest/activity-element#screen
#
# Defaults to "unspecified".
#orientation = "landscape"

# See https://developer.android.com/guide/topics/manifest/activity-element#exported
#
# Unset by default, or true when targeting Android >= 31 (S and up).
exported = true

# See https://developer.android.com/guide/topics/manifest/activity-element#resizeableActivity
#
# Defaults to true on Android >= 24, no effect on earlier API levels
resizeable_activity = false

# See https://developer.android.com/guide/topics/manifest/activity-element#always
always_retain_task_state = true

# See https://developer.android.com/guide/topics/manifest/meta-data-element
#
# Note: there can be several .meta_data entries.
# Note: the `resource` attribute is currently not supported.
[[package.metadata.android.application.activity.meta_data]]
name = "com.oculus.vr.focusaware"
value = "true"

# See https://developer.android.com/guide/topics/manifest/intent-filter-element
#
# Note: there can be several .intent_filter entries.
[[package.metadata.android.application.activity.intent_filter]]
# See https://developer.android.com/guide/topics/manifest/action-element
actions = ["android.intent.action.VIEW", "android.intent.action.WEB_SEARCH"]
# See https://developer.android.com/guide/topics/manifest/category-element
categories = ["android.intent.category.DEFAULT", "android.intent.category.BROWSABLE"]



# Set up reverse port forwarding through `adb reverse`, meaning that if the
# Android device connects to `localhost` on port `1338` it will be routed to
# the host on port `1338` instead. Source and destination ports can differ,
# see the `adb` help page for possible configurations.
[package.metadata.android.reverse_port_forward]
"tcp:1338" = "tcp:1338"



Metadata

Metadata

Assignees

No one assigned

    Labels

    androidIssues related to the android corebugSomething isn't workingquestionFurther information is requested

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions