Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
225 changes: 130 additions & 95 deletions alvr/system_info/src/android.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,45 @@
use alvr_common::warn;
use jni::{objects::JObject, sys::jobject, JNIEnv, JavaVM};
use jni::{
errors::Error,
objects::{JObject, JString, JValue, JValueOwned},
sys::jobject,
JNIEnv, JavaVM,
};
use std::net::{IpAddr, Ipv4Addr};

pub const MICROPHONE_PERMISSION: &str = "android.permission.RECORD_AUDIO";

// Will call a JNI method and handle any Java exceptions that might occur
fn env_call_method_handled<'local>(
env: &mut JNIEnv<'local>,
object: &JObject,
method: &str,
sig: &str,
args: &[JValue],
) -> Result<JValueOwned<'local>, Error> {
env.call_method(object, method, sig, args)
.map_err(|e| match e {
jni::errors::Error::JavaException => {
env.exception_describe().unwrap();
let jthorw = env.exception_occurred().unwrap();
env.exception_clear().unwrap();
let exception_to_jstring: JString = env
.call_method(jthorw, "toString", "()Ljava/lang/String;", &[])
.unwrap()
.l()
.unwrap()
.into();
let excp_string = env.get_string(&exception_to_jstring).unwrap();
let err = String::from("Java Exception Occured: ") + excp_string.to_str().unwrap();
// At this point even if we let this error propagate, the next unwrap will panic with only unclear "JavaException" literal,
// so we can panic here itself with more detailed JavaException reason message and let crate::backtrace::backtrace() be printed
// This can also be synced with server side in future if we have that feature
panic!("{err}");
}
_ => e,
})
}

pub fn vm() -> JavaVM {
unsafe { JavaVM::from_raw(ndk_context::android_context().vm().cast()).unwrap() }
}
Expand All @@ -28,25 +64,26 @@ pub fn try_get_permission(permission: &str) {

let mic_perm_jstring = env.new_string(permission).unwrap();

let permission_status = env
.call_method(
unsafe { JObject::from_raw(context()) },
"checkSelfPermission",
"(Ljava/lang/String;)I",
&[(&mic_perm_jstring).into()],
)
.unwrap()
.i()
.unwrap();
let permission_status = env_call_method_handled(
&mut env,
unsafe { &JObject::from_raw(context()) },
"checkSelfPermission",
"(Ljava/lang/String;)I",
&[(&mic_perm_jstring).into()],
)
.unwrap()
.i()
.unwrap();

if permission_status != 0 {
let string_class = env.find_class("java/lang/String").unwrap();
let perm_array = env
.new_object_array(1, string_class, mic_perm_jstring)
.unwrap();

env.call_method(
unsafe { JObject::from_raw(context()) },
env_call_method_handled(
&mut env,
unsafe { &JObject::from_raw(context()) },
"requestPermissions",
"([Ljava/lang/String;I)V",
&[(&perm_array).into(), 0.into()],
Expand Down Expand Up @@ -86,8 +123,9 @@ pub fn manufacturer_name() -> String {
fn get_system_service<'a>(env: &mut JNIEnv<'a>, service_name: &str) -> JObject<'a> {
let service_str = env.new_string(service_name).unwrap();

env.call_method(
unsafe { JObject::from_raw(context()) },
env_call_method_handled(
env,
unsafe { &JObject::from_raw(context()) },
"getSystemService",
"(Ljava/lang/String;)Ljava/lang/Object;",
&[(&service_str).into()],
Expand All @@ -103,18 +141,17 @@ pub fn local_ip() -> IpAddr {
let mut env = vm.attach_current_thread().unwrap();

let wifi_manager = get_system_service(&mut env, "wifi");
let wifi_info = env
.call_method(
wifi_manager,
"getConnectionInfo",
"()Landroid/net/wifi/WifiInfo;",
&[],
)
.unwrap()
.l()
.unwrap();
let ip_i32 = env
.call_method(wifi_info, "getIpAddress", "()I", &[])
let wifi_info = env_call_method_handled(
&mut env,
&wifi_manager,
"getConnectionInfo",
"()Landroid/net/wifi/WifiInfo;",
&[],
)
.unwrap()
.l()
.unwrap();
let ip_i32 = env_call_method_handled(&mut env, &wifi_info, "getIpAddress", "()I", &[])
.unwrap()
.i()
.unwrap();
Expand All @@ -133,18 +170,17 @@ pub fn set_wifi_lock(enabled: bool) {
let wifi_manager = get_system_service(&mut env, "wifi");

fn set_lock<'a>(env: &mut JNIEnv<'a>, lock: &JObject, enabled: bool) {
env.call_method(lock, "setReferenceCounted", "(Z)V", &[false.into()])
.unwrap();
env.call_method(
&lock,
env_call_method_handled(env, lock, "setReferenceCounted", "(Z)V", &[false.into()]).unwrap();
env_call_method_handled(
env,
lock,
if enabled { "acquire" } else { "release" },
"()V",
&[],
)
.unwrap();

let lock_is_aquired = env
.call_method(lock, "isHeld", "()Z", &[])
let lock_is_aquired = env_call_method_handled(env, &lock, "isHeld", "()Z", &[])
.unwrap()
.z()
.unwrap();
Expand All @@ -155,38 +191,38 @@ pub fn set_wifi_lock(enabled: bool) {
}

let wifi_lock_jstring = env.new_string("alvr_wifi_lock").unwrap();
let wifi_lock = env
.call_method(
&wifi_manager,
"createWifiLock",
"(ILjava/lang/String;)Landroid/net/wifi/WifiManager$WifiLock;",
&[
if get_api_level() >= 29 {
// Recommended for virtual reality since it disables WIFI scans
4 // WIFI_MODE_FULL_LOW_LATENCY
} else {
3 // WIFI_MODE_FULL_HIGH_PERF
}
.into(),
(&wifi_lock_jstring).into(),
],
)
.unwrap()
.l()
.unwrap();
let wifi_lock = env_call_method_handled(
&mut env,
&wifi_manager,
"createWifiLock",
"(ILjava/lang/String;)Landroid/net/wifi/WifiManager$WifiLock;",
&[
if get_api_level() >= 29 {
// Recommended for virtual reality since it disables WIFI scans
4 // WIFI_MODE_FULL_LOW_LATENCY
} else {
3 // WIFI_MODE_FULL_HIGH_PERF
}
.into(),
(&wifi_lock_jstring).into(),
],
)
.unwrap()
.l()
.unwrap();
set_lock(&mut env, &wifi_lock, enabled);

let multicast_lock_jstring = env.new_string("alvr_multicast_lock").unwrap();
let multicast_lock = env
.call_method(
wifi_manager,
"createMulticastLock",
"(Ljava/lang/String;)Landroid/net/wifi/WifiManager$MulticastLock;",
&[(&multicast_lock_jstring).into()],
)
.unwrap()
.l()
.unwrap();
let multicast_lock = env_call_method_handled(
&mut env,
&wifi_manager,
"createMulticastLock",
"(Ljava/lang/String;)Landroid/net/wifi/WifiManager$MulticastLock;",
&[(&multicast_lock_jstring).into()],
)
.unwrap()
.l()
.unwrap();
set_lock(&mut env, &multicast_lock, enabled);
}

Expand All @@ -204,9 +240,8 @@ pub fn get_battery_status() -> (f32, bool) {
&[(&intent_action_jstring).into()],
)
.unwrap();
let battery_intent = env
.call_method(
unsafe { JObject::from_raw(context()) },
let battery_intent = env_call_method_handled(&mut env,
unsafe { &JObject::from_raw(context()) },
"registerReceiver",
"(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;",
&[(&JObject::null()).into(), (&intent_filter).into()],
Expand All @@ -216,39 +251,39 @@ pub fn get_battery_status() -> (f32, bool) {
.unwrap();

let level_jstring = env.new_string("level").unwrap();
let level = env
.call_method(
&battery_intent,
"getIntExtra",
"(Ljava/lang/String;I)I",
&[(&level_jstring).into(), (-1).into()],
)
.unwrap()
.i()
.unwrap();
let level = env_call_method_handled(
&mut env,
&battery_intent,
"getIntExtra",
"(Ljava/lang/String;I)I",
&[(&level_jstring).into(), (-1).into()],
)
.unwrap()
.i()
.unwrap();
let scale_jstring = env.new_string("scale").unwrap();
let scale = env
.call_method(
&battery_intent,
"getIntExtra",
"(Ljava/lang/String;I)I",
&[(&scale_jstring).into(), (-1).into()],
)
.unwrap()
.i()
.unwrap();
let scale = env_call_method_handled(
&mut env,
&battery_intent,
"getIntExtra",
"(Ljava/lang/String;I)I",
&[(&scale_jstring).into(), (-1).into()],
)
.unwrap()
.i()
.unwrap();

let plugged_jstring = env.new_string("plugged").unwrap();
let plugged = env
.call_method(
&battery_intent,
"getIntExtra",
"(Ljava/lang/String;I)I",
&[(&plugged_jstring).into(), (-1).into()],
)
.unwrap()
.i()
.unwrap();
let plugged = env_call_method_handled(
&mut env,
&battery_intent,
"getIntExtra",
"(Ljava/lang/String;I)I",
&[(&plugged_jstring).into(), (-1).into()],
)
.unwrap()
.i()
.unwrap();

(level as f32 / scale as f32, plugged > 0)
}