Skip to content

Commit

Permalink
feat: ✨ Add mDNS discovery support
Browse files Browse the repository at this point in the history
  • Loading branch information
zmerp committed Feb 8, 2024
1 parent eedf372 commit 4bc4ff1
Show file tree
Hide file tree
Showing 13 changed files with 402 additions and 229 deletions.
410 changes: 244 additions & 166 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion alvr/client_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ alvr_sockets.workspace = true
app_dirs2 = "2"
bincode = "1"
glyph_brush_layout = "0.2"
jni = "0.21"
mdns-sd = "0.10"
rand = "0.8"
serde = "1"
serde_json = "1"
jni = "0.21"

[target.'cfg(target_os = "android")'.dependencies]
android_logger = "0.13"
Expand Down
30 changes: 29 additions & 1 deletion alvr/client_core/src/c_api.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
opengl::{self, RenderViewInput},
ClientCoreEvent,
storage, ClientCoreEvent,
};
use alvr_common::{
debug, error,
Expand Down Expand Up @@ -131,6 +131,34 @@ pub unsafe extern "C" fn alvr_log_time(tag: *const c_char) {
error!("[ALVR NATIVE] {tag}: {:?}", Instant::now());
}

fn string_to_c_str(buffer: *mut c_char, value: &str) -> u64 {
let cstring = CString::new(value).unwrap();
if !buffer.is_null() {
unsafe {
ptr::copy_nonoverlapping(cstring.as_ptr(), buffer, cstring.as_bytes_with_nul().len());
}
}

cstring.as_bytes_with_nul().len() as u64
}

#[no_mangle]
pub extern "C" fn alvr_mdns_service(service_buffer: *mut c_char) -> u64 {
string_to_c_str(service_buffer, alvr_sockets::MDNS_SERVICE_TYPE)
}

// To make sure the value is correct, call after alvr_initialize()
#[no_mangle]
pub extern "C" fn alvr_hostname(hostname_buffer: *mut c_char) -> u64 {
string_to_c_str(hostname_buffer, &storage::Config::load().hostname)
}

// To make sure the value is correct, call after alvr_initialize()
#[no_mangle]
pub extern "C" fn alvr_protocol_id(protocol_buffer: *mut c_char) -> u64 {
string_to_c_str(protocol_buffer, &storage::Config::load().protocol_id)
}

/// On non-Android platforms, java_vm and constext should be null.
/// NB: context must be thread safe.
#[allow(unused_variables)]
Expand Down
2 changes: 1 addition & 1 deletion alvr/client_core/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ fn connection_pipeline(

proto_control_socket
.send(&ClientConnectionResult::ConnectionAccepted {
client_protocol_id: alvr_common::protocol_id(),
client_protocol_id: alvr_common::protocol_id_u64(),
display_name: platform::device_model(),
server_ip,
streaming_capabilities: Some(VideoStreamingCapabilities {
Expand Down
4 changes: 2 additions & 2 deletions alvr/client_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ pub fn initialize(
#[cfg(target_os = "android")]
platform::try_get_permission(platform::MICROPHONE_PERMISSION);
#[cfg(target_os = "android")]
platform::acquire_wifi_lock();
platform::set_wifi_lock(true);

EXTERNAL_DECODER.set(external_decoder);
*LIFECYCLE_STATE.write() = LifecycleState::Idle;
Expand All @@ -123,7 +123,7 @@ pub fn destroy() {
}

#[cfg(target_os = "android")]
platform::release_wifi_lock();
platform::set_wifi_lock(false);
}

pub fn resume() {
Expand Down
98 changes: 57 additions & 41 deletions alvr/client_core/src/platform/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ mod decoder;

pub use decoder::*;

use alvr_common::OptLazy;
use alvr_common::{warn, OptLazy};
use jni::{
objects::{GlobalRef, JObject},
sys::jobject,
Expand All @@ -12,8 +12,6 @@ use std::net::{IpAddr, Ipv4Addr};

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

static WIFI_LOCK: OptLazy<GlobalRef> = alvr_common::lazy_mut_none();

pub fn vm() -> JavaVM {
unsafe { JavaVM::from_raw(ndk_context::android_context().vm().cast()).unwrap() }
}
Expand Down Expand Up @@ -137,50 +135,68 @@ pub fn local_ip() -> IpAddr {
}

// This is needed to avoid wifi scans that disrupt streaming.
pub fn acquire_wifi_lock() {
let mut maybe_wifi_lock = WIFI_LOCK.lock();

if maybe_wifi_lock.is_none() {
let vm = vm();
let mut env = vm.attach_current_thread().unwrap();

let wifi_mode = 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
};

let wifi_manager = get_system_service(&mut env, "wifi");
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;",
&[wifi_mode.into(), (&wifi_lock_jstring).into()],
)
.unwrap()
.l()
.unwrap();
env.call_method(&wifi_lock, "acquire", "()V", &[]).unwrap();
// Code inspired from https://github.com/Meumeu/WiVRn/blob/master/client/application.cpp
pub fn set_wifi_lock(enabled: bool) {
let vm = vm();
let mut env = vm.attach_current_thread().unwrap();

*maybe_wifi_lock = Some(env.new_global_ref(wifi_lock).unwrap());
}
}
let wifi_manager = get_system_service(&mut env, "wifi");
let wifi_lock_jstring = env.new_string("alvr_wifi_lock").unwrap();

pub fn release_wifi_lock() {
if let Some(wifi_lock) = WIFI_LOCK.lock().take() {
let vm = vm();
let mut env = vm.attach_current_thread().unwrap();
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,
if enabled { "acquire" } else { "release" },
"()V",
&[],
)
.unwrap();

env.call_method(wifi_lock.as_obj(), "release", "()V", &[])
let lock_is_aquired = env
.call_method(lock, "isHeld", "()Z", &[])
.unwrap()
.z()
.unwrap();
// TODO: all JVM.call_method sometimes result in JavaExceptions, unwrap will only report Error as 'JavaException', ideally before unwrapping
// need to call JVM.describe_error() which will actually check if there is an exception and print error to stderr/logcat. Then unwrap.

// wifi_lock is dropped here
if lock_is_aquired != enabled {
warn!("Failed to set wifi lock: expected {enabled}, got {lock_is_aquired}");
}
}

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();
set_lock(&mut env, &wifi_lock, enabled);

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

pub fn get_battery_status() -> (f32, bool) {
Expand Down
2 changes: 1 addition & 1 deletion alvr/client_core/src/sockets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ impl AnnouncerSocket {

let mut packet = [0; 56];
packet[0..ALVR_NAME.len()].copy_from_slice(ALVR_NAME.as_bytes());
packet[16..24].copy_from_slice(&alvr_common::protocol_id().to_le_bytes());
packet[16..24].copy_from_slice(&alvr_common::protocol_id_u64().to_le_bytes());
packet[24..24 + hostname.len()].copy_from_slice(hostname.as_bytes());

Ok(Self { socket, packet })
Expand Down
6 changes: 3 additions & 3 deletions alvr/client_core/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,23 @@ fn config_path() -> PathBuf {

#[derive(Serialize, Deserialize)]
pub struct Config {
pub protocol_id: u64,
pub hostname: String,
pub protocol_id: String,
}

impl Default for Config {
fn default() -> Self {
let mut rng = rand::thread_rng();

Self {
protocol_id: alvr_common::protocol_id(),
hostname: format!(
"{}{}{}{}.client.alvr",
"{}{}{}{}.client",
rng.gen_range(0..10),
rng.gen_range(0..10),
rng.gen_range(0..10),
rng.gen_range(0..10),
),
protocol_id: alvr_common::protocol_id(),
}
}
}
Expand Down
12 changes: 7 additions & 5 deletions alvr/common/src/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,16 @@ pub fn is_stable() -> bool {
// Semver compatible versions will produce the same protocol ID. Protocol IDs are not ordered
// As a convention, encode/decode the protocol ID bytes as little endian.
// Only makor and
pub fn protocol_id() -> u64 {
let protocol_string = if ALVR_VERSION.pre.is_empty() {
pub fn protocol_id() -> String {
if ALVR_VERSION.pre.is_empty() {
ALVR_VERSION.major.to_string()
} else {
format!("{}-{}", ALVR_VERSION.major, ALVR_VERSION.pre)
};
}
}

hash_string(&protocol_string)
pub fn protocol_id_u64() -> u64 {
hash_string(&protocol_id())
}

// deprecated
Expand All @@ -44,5 +46,5 @@ pub fn is_version_compatible(other_version: &Version) -> bool {
format!("{}-{}", other_version.major, other_version.pre)
};

protocol_id() == hash_string(&protocol_string)
protocol_id_u64() == hash_string(&protocol_string)
}
2 changes: 2 additions & 0 deletions alvr/server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ bincode = "1"
bytes = "1"
chrono = "0.4"
fern = "0.6"
flume = "0.11"
futures = "0.3"
headers = "0.3"
hyper = { version = "0.14", features = [
Expand All @@ -37,6 +38,7 @@ hyper = { version = "0.14", features = [
"runtime",
"tcp",
] }
mdns-sd = "0.10"
profiling = { version = "1", optional = true }
reqwest = "0.11" # not used but webserver does not work without it. todo: investigate
rosc = "0.10"
Expand Down
4 changes: 2 additions & 2 deletions alvr/server/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,10 +382,10 @@ fn connection_pipeline(
ClientListAction::SetDisplayName(display_name),
);

if client_protocol_id != alvr_common::protocol_id() {
if client_protocol_id != alvr_common::protocol_id_u64() {
warn!(
"Trusted client is incompatible! Expected protocol ID: {}, found: {}",
alvr_common::protocol_id(),
alvr_common::protocol_id_u64(),
client_protocol_id,
);

Expand Down
Loading

0 comments on commit 4bc4ff1

Please sign in to comment.