The open source OpenXR runtime
1// Copyright 2021-2024, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief Implementation of a callback collection for Android lifecycle events.
6 * @author Rylie Pavlik <rylie.pavlik@collabora.com>
7 * @ingroup aux_android
8 */
9
10#include "android_lifecycle_callbacks.h"
11
12#include "android_load_class.hpp"
13#include "org.freedesktop.monado.auxiliary.hpp"
14
15#include "xrt/xrt_config_android.h"
16#include "xrt/xrt_android.h"
17#include "util/u_logging.h"
18#include "util/u_generic_callbacks.hpp"
19
20#include "wrap/android.app.h"
21
22#include <memory>
23
24using wrap::android::app::Activity;
25using wrap::org::freedesktop::monado::auxiliary::ActivityLifecycleListener;
26using xrt::auxiliary::android::loadClassFromRuntimeApk;
27using xrt::auxiliary::util::GenericCallbacks;
28
29struct android_lifecycle_callbacks
30{
31 explicit android_lifecycle_callbacks(xrt_instance_android *xinst_android) : instance_android(xinst_android) {}
32 xrt_instance_android *instance_android;
33 GenericCallbacks<xrt_android_lifecycle_event_handler_t, enum xrt_android_lifecycle_event> callback_collection;
34 ActivityLifecycleListener listener{};
35};
36
37int
38android_lifecycle_callbacks_invoke(struct android_lifecycle_callbacks *alc, enum xrt_android_lifecycle_event event);
39
40/*!
41 * JNI functions
42 */
43
44static void
45on_activity_created(JNIEnv *env, jobject thiz, jlong native_callback_ptr, jobject activity)
46{
47 auto *alc = reinterpret_cast<android_lifecycle_callbacks *>(native_callback_ptr);
48 if (env->IsSameObject(activity, (jobject)xrt_instance_android_get_context(alc->instance_android))) {
49 android_lifecycle_callbacks_invoke(alc, XRT_ANDROID_LIVECYCLE_EVENT_ON_CREATE);
50 }
51}
52
53static void
54on_activity_started(JNIEnv *env, jobject thiz, jlong native_callback_ptr, jobject activity)
55{
56 auto *alc = reinterpret_cast<android_lifecycle_callbacks *>(native_callback_ptr);
57 if (env->IsSameObject(activity, (jobject)xrt_instance_android_get_context(alc->instance_android))) {
58 android_lifecycle_callbacks_invoke(alc, XRT_ANDROID_LIVECYCLE_EVENT_ON_START);
59 }
60}
61
62static void
63on_activity_resumed(JNIEnv *env, jobject thiz, jlong native_callback_ptr, jobject activity)
64{
65 auto *alc = reinterpret_cast<android_lifecycle_callbacks *>(native_callback_ptr);
66 if (env->IsSameObject(activity, (jobject)xrt_instance_android_get_context(alc->instance_android))) {
67 android_lifecycle_callbacks_invoke(alc, XRT_ANDROID_LIVECYCLE_EVENT_ON_RESUME);
68 }
69}
70
71static void
72on_activity_paused(JNIEnv *env, jobject thiz, jlong native_callback_ptr, jobject activity)
73{
74 auto *alc = reinterpret_cast<android_lifecycle_callbacks *>(native_callback_ptr);
75 if (env->IsSameObject(activity, (jobject)xrt_instance_android_get_context(alc->instance_android))) {
76 android_lifecycle_callbacks_invoke(alc, XRT_ANDROID_LIVECYCLE_EVENT_ON_PAUSE);
77 }
78}
79
80static void
81on_activity_stopped(JNIEnv *env, jobject thiz, jlong native_callback_ptr, jobject activity)
82{
83 auto *alc = reinterpret_cast<android_lifecycle_callbacks *>(native_callback_ptr);
84 if (env->IsSameObject(activity, (jobject)xrt_instance_android_get_context(alc->instance_android))) {
85 android_lifecycle_callbacks_invoke(alc, XRT_ANDROID_LIVECYCLE_EVENT_ON_STOP);
86 }
87}
88
89static void
90on_activity_save_instance_state(JNIEnv *env, jobject thiz, jlong native_callback_ptr, jobject activity)
91{}
92
93static void
94on_activity_destroyed(JNIEnv *env, jobject thiz, jlong native_callback_ptr, jobject activity)
95{
96 auto *alc = reinterpret_cast<android_lifecycle_callbacks *>(native_callback_ptr);
97 if (env->IsSameObject(activity, (jobject)xrt_instance_android_get_context(alc->instance_android))) {
98 android_lifecycle_callbacks_invoke(alc, XRT_ANDROID_LIVECYCLE_EVENT_ON_DESTROY);
99 }
100}
101
102static JNINativeMethod methods[] = {
103 {"nativeOnActivityCreated", "(JLandroid/app/Activity;)V", (void *)&on_activity_created},
104 {"nativeOnActivityStarted", "(JLandroid/app/Activity;)V", (void *)&on_activity_started},
105 {"nativeOnActivityResumed", "(JLandroid/app/Activity;)V", (void *)&on_activity_resumed},
106 {"nativeOnActivityPaused", "(JLandroid/app/Activity;)V", (void *)&on_activity_paused},
107 {"nativeOnActivityStopped", "(JLandroid/app/Activity;)V", (void *)&on_activity_stopped},
108 {"nativeOnActivitySaveInstanceState", "(JLandroid/app/Activity;)V", (void *)&on_activity_save_instance_state},
109 {"nativeOnActivityDestroyed", "(JLandroid/app/Activity;)V", (void *)&on_activity_destroyed},
110};
111
112#define CATCH_CLAUSES(ACTION, RET) \
113 catch (std::exception const &e) \
114 { \
115 U_LOG_E("Exception while " ACTION "! %s", e.what()); \
116 return RET; \
117 } \
118 catch (...) \
119 { \
120 U_LOG_E("Unknown exception while " ACTION "!"); \
121 return RET; \
122 }
123
124int
125android_lifecycle_callbacks_register_callback(struct android_lifecycle_callbacks *alc,
126 xrt_android_lifecycle_event_handler_t callback,
127 enum xrt_android_lifecycle_event event_mask,
128 void *userdata)
129{
130 try {
131 alc->callback_collection.addCallback(callback, event_mask, userdata);
132 return 0;
133 }
134 CATCH_CLAUSES("adding callback to collection", -1)
135}
136
137int
138android_lifecycle_callbacks_remove_callback(struct android_lifecycle_callbacks *alc,
139 xrt_android_lifecycle_event_handler_t callback,
140 enum xrt_android_lifecycle_event event_mask,
141 void *userdata)
142{
143 try {
144 return alc->callback_collection.removeCallback(callback, event_mask, userdata);
145 }
146 CATCH_CLAUSES("removing callback", -1)
147}
148
149int
150android_lifecycle_callbacks_invoke(struct android_lifecycle_callbacks *alc, enum xrt_android_lifecycle_event event)
151{
152 try {
153 return alc->callback_collection.invokeCallbacks(
154 event, [=](enum xrt_android_lifecycle_event event, xrt_android_lifecycle_event_handler_t callback,
155 void *userdata) { return callback(alc->instance_android, event, userdata); });
156 }
157 CATCH_CLAUSES("invoking callbacks", -1)
158}
159
160struct android_lifecycle_callbacks *
161android_lifecycle_callbacks_create(struct xrt_instance_android *xinst_android)
162{
163 try {
164 jni::init(xrt_instance_android_get_vm(xinst_android));
165 jobject context = (jobject)xrt_instance_android_get_context(xinst_android);
166 if (!jni::env()->IsInstanceOf(context, jni::Class(Activity::getTypeName()).getHandle())) {
167 // skip if context is not android.app.Activity
168 U_LOG_W("Context is not Activity, skip");
169 return nullptr;
170 }
171
172 auto clazz = loadClassFromRuntimeApk(context, ActivityLifecycleListener::getFullyQualifiedTypeName());
173 if (clazz.isNull()) {
174 U_LOG_E("Could not load class '%s' from package '%s'",
175 ActivityLifecycleListener::getFullyQualifiedTypeName(), XRT_ANDROID_PACKAGE);
176 return nullptr;
177 }
178
179 auto ret = std::make_unique<android_lifecycle_callbacks>(xinst_android);
180 // Teach the wrapper our class before we start to use it.
181 ActivityLifecycleListener::staticInitClass((jclass)clazz.object().getHandle());
182 jni::env()->RegisterNatives((jclass)clazz.object().getHandle(), methods,
183 sizeof(methods) / sizeof(methods[0]));
184
185 ret->listener = ActivityLifecycleListener::construct(ret.get());
186 ret->listener.registerCallback(Activity(context));
187 return ret.release();
188 }
189 CATCH_CLAUSES("creating callbacks structure", nullptr)
190}
191
192void
193android_lifecycle_callbacks_destroy(struct android_lifecycle_callbacks **ptr_callbacks)
194{
195 if (ptr_callbacks == nullptr) {
196 return;
197 }
198 struct android_lifecycle_callbacks *alc = *ptr_callbacks;
199 if (alc == nullptr) {
200 return;
201 }
202
203 alc->listener.unregisterCallback(Activity((jobject)xrt_instance_android_get_context(alc->instance_android)));
204 delete alc;
205 *ptr_callbacks = nullptr;
206}