While looking at #171, I noticed this separate soundness issue with the current implementation of AndroidAppWaker:
pub struct AndroidAppWaker {
// The looper pointer is owned by the android_app and effectively
// has a 'static lifetime, and the ALooper_wake C API is thread
// safe, so this can be cloned safely and is send + sync safe
looper: NonNull<ndk_sys::ALooper>,
}
That comment is not the full truth.
Although for most applications an AndroidApp will more-or-less appear like it has a 'static lifetime, it does not actually have a 'static lifetime.
An AndroidApp is passed to android_main when your NativeActivity starts up and will be dropped when your application returns from android_main - but that doesn't necessarily mean your process will exit, since Android's Activity lifecycle is not a process lifecycle and an application may run multiple activities.
Even without considering the theoretical possibility of trying to start more than one NativeActivity or GameActivity per process (difficult for multiple reasons) it would be possible for JNI to call into Rust after your android_main function has returned which could observe an invalid looper pointer if an AndroidAppWaker were stored somewhere with a 'static lifetime.
When we create an AndroidAppWaker we need to call ALooper_acquire() instead of assuming the pointer has a static lifetime.
The Clone implementation for AndroidAppWaker also needs to call ALooper_acquire()
There needs to then be a Drop implementation that call ALooper_release()
While looking at #171, I noticed this separate soundness issue with the current implementation of
AndroidAppWaker:That comment is not the full truth.
Although for most applications an
AndroidAppwill more-or-less appear like it has a'staticlifetime, it does not actually have a'staticlifetime.An
AndroidAppis passed toandroid_mainwhen yourNativeActivitystarts up and will be dropped when your application returns fromandroid_main- but that doesn't necessarily mean your process will exit, since Android'sActivitylifecycle is not a process lifecycle and an application may run multiple activities.Even without considering the theoretical possibility of trying to start more than one
NativeActivityorGameActivityper process (difficult for multiple reasons) it would be possible for JNI to call into Rust after yourandroid_mainfunction has returned which could observe an invalidlooperpointer if anAndroidAppWakerwere stored somewhere with a'staticlifetime.When we create an
AndroidAppWakerwe need to callALooper_acquire()instead of assuming the pointer has a static lifetime.The
Cloneimplementation forAndroidAppWakeralso needs to callALooper_acquire()There needs to then be a
Dropimplementation that callALooper_release()