From b27b2b71c0ce33d21c6ef35c068834b04e82bf60 Mon Sep 17 00:00:00 2001
From: Marijn Suijten <marijns95@gmail.com>
Date: Sat, 24 Jul 2021 23:54:30 +0200
Subject: [PATCH] ndk-glue: Move native activity behind lock and remove it in
 `onDestroy`

`NativeActivity` is now behind a lock, and will be removed as soon as
`onDestroy` is called. Due to the async nature of events, make sure to
hold on to the lock received from `native_activity()` _beforehand_ if
you wish to use it during handling of `Event::Destroy`.
---
 ndk-examples/examples/looper.rs |  3 +--
 ndk-glue/CHANGELOG.md           |  5 +++++
 ndk-glue/src/lib.rs             | 24 +++++++++++++++---------
 3 files changed, 21 insertions(+), 11 deletions(-)

diff --git a/ndk-examples/examples/looper.rs b/ndk-examples/examples/looper.rs
index bd11b6b8..180c7082 100644
--- a/ndk-examples/examples/looper.rs
+++ b/ndk-examples/examples/looper.rs
@@ -154,6 +154,5 @@ fn main() {
     }
 
     // Stop the activity
-    #[allow(deprecated)]
-    ndk_glue::native_activity().finish()
+    ndk_glue::native_activity().unwrap().finish()
 }
diff --git a/ndk-glue/CHANGELOG.md b/ndk-glue/CHANGELOG.md
index 7e107e95..f4c96e06 100644
--- a/ndk-glue/CHANGELOG.md
+++ b/ndk-glue/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Unreleased
 
+- **Breaking:** `NativeActivity` is now behind a lock, and will be removed as soon as `onDestroy`
+  is called. Due to the async nature of events, make sure to hold on to the lock received from
+  `native_activity()` _beforehand_ if you wish to use it during handling of `Event::Destroy`. The
+  lock should be released to allow `onDestroy` to return.
+
 # 0.7.0 (2022-07-24)
 
 - **Breaking:** Provide a `LockReadGuard` newtype around `NativeWindow`/`InputQueue` to hide the underlying lock implementation. (#288)
diff --git a/ndk-glue/src/lib.rs b/ndk-glue/src/lib.rs
index 7564a498..60a0d6ff 100644
--- a/ndk-glue/src/lib.rs
+++ b/ndk-glue/src/lib.rs
@@ -51,21 +51,20 @@ pub fn android_log(level: Level, tag: &CStr, msg: &CStr) {
     }
 }
 
+static NATIVE_ACTIVITY: Lazy<RwLock<Option<NativeActivity>>> = Lazy::new(Default::default);
 static NATIVE_WINDOW: Lazy<RwLock<Option<NativeWindow>>> = Lazy::new(Default::default);
 static INPUT_QUEUE: Lazy<RwLock<Option<InputQueue>>> = Lazy::new(Default::default);
 static CONTENT_RECT: Lazy<RwLock<Rect>> = Lazy::new(Default::default);
 static LOOPER: Lazy<Mutex<Option<ForeignLooper>>> = Lazy::new(Default::default);
 
-static mut NATIVE_ACTIVITY: Option<NativeActivity> = None;
-
 /// This function accesses a `static` variable internally and must only be used if you are sure
-/// there is exactly one version of `ndk_glue` in your dependency tree.
+/// there is exactly one version of [`ndk_glue`][crate] in your dependency tree.
 ///
 /// If you need access to the `JavaVM` through [`NativeActivity::vm()`] or Activity `Context`
 /// through [`NativeActivity::activity()`], please use the [`ndk_context`] crate and its
 /// [`ndk_context::android_context()`] getter to acquire the `JavaVM` and `Context` instead.
-pub fn native_activity() -> &'static NativeActivity {
-    unsafe { NATIVE_ACTIVITY.as_ref().unwrap() }
+pub fn native_activity() -> Option<LockReadGuard<NativeActivity>> {
+    LockReadGuard::from_wrapped_option(NATIVE_ACTIVITY.read())
 }
 
 pub struct LockReadGuard<T: ?Sized + 'static>(MappedRwLockReadGuard<'static, T>);
@@ -182,6 +181,11 @@ pub enum Event {
     SaveInstanceState,
     Pause,
     Stop,
+    /// The native activity will be stopped and destroyed after this event.
+    /// Due to the async nature of these events, make sure to hold on to the
+    /// lock received from [`native_activity()`] _beforehand_ if you wish to use
+    /// it during handling of [`Event::Destroy`]. The lock should be released to
+    /// allow `onDestroy` to return.
     Destroy,
     ConfigChanged,
     LowMemory,
@@ -249,7 +253,7 @@ pub unsafe fn init(
 
     let activity = NativeActivity::from_ptr(activity);
     ndk_context::initialize_android_context(activity.vm().cast(), activity.activity().cast());
-    NATIVE_ACTIVITY.replace(activity);
+    NATIVE_ACTIVITY.write().replace(activity);
 
     let mut logpipe: [RawFd; 2] = Default::default();
     libc::pipe(logpipe.as_mut_ptr());
@@ -334,6 +338,9 @@ unsafe extern "C" fn on_stop(activity: *mut ANativeActivity) {
 unsafe extern "C" fn on_destroy(activity: *mut ANativeActivity) {
     wake(activity, Event::Destroy);
     ndk_context::release_android_context();
+    let mut native_activity_guard = NATIVE_ACTIVITY.write();
+    let native_activity = native_activity_guard.take().unwrap();
+    assert_eq!(native_activity.ptr().as_ptr(), activity);
 }
 
 unsafe extern "C" fn on_configuration_changed(activity: *mut ANativeActivity) {
@@ -407,10 +414,9 @@ unsafe extern "C" fn on_input_queue_destroyed(
 ) {
     wake(activity, Event::InputQueueDestroyed);
     let mut input_queue_guard = INPUT_QUEUE.write();
-    assert_eq!(input_queue_guard.as_ref().unwrap().ptr().as_ptr(), queue);
-    let input_queue = InputQueue::from_ptr(NonNull::new(queue).unwrap());
+    let input_queue = input_queue_guard.take().unwrap();
+    assert_eq!(input_queue.ptr().as_ptr(), queue);
     input_queue.detach_looper();
-    input_queue_guard.take();
 }
 
 unsafe extern "C" fn on_content_rect_changed(activity: *mut ANativeActivity, rect: *const ARect) {