From 9375824235f08e41ff0570a44fda35af4b3702f3 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Fri, 26 Apr 2024 14:12:23 -0700 Subject: [PATCH 1/3] Mild logging cleanup. --- native-activity/app/src/main/cpp/main.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/native-activity/app/src/main/cpp/main.cpp b/native-activity/app/src/main/cpp/main.cpp index 76ed5f637..f1e8ff0b5 100644 --- a/native-activity/app/src/main/cpp/main.cpp +++ b/native-activity/app/src/main/cpp/main.cpp @@ -30,10 +30,13 @@ #include #include -#define LOGI(...) \ - ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__)) -#define LOGW(...) \ - ((void)__android_log_print(ANDROID_LOG_WARN, "native-activity", __VA_ARGS__)) +#define LOG_TAG "native-activity" + +#define _LOG(priority, fmt, ...) \ + ((void)__android_log_print((priority), (LOG_TAG), (fmt)__VA_OPT__(, ) __VA_ARGS__)) + +#define LOGW(fmt, ...) _LOG(ANDROID_LOG_WARN, (fmt)__VA_OPT__(, ) __VA_ARGS__) +#define LOGI(fmt, ...) _LOG(ANDROID_LOG_INFO, (fmt)__VA_OPT__(, ) __VA_ARGS__) /** * Our saved state data. From b9a0f76d36cd2436ea63ab73b18aa8be1d2b4322 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Tue, 30 Apr 2024 13:11:30 -0700 Subject: [PATCH 2/3] Remove pointless dynamic use of ASensorManager. This was only done so it could use ASensorManager_getInstanceForPackage on newer devices and ASensorManager_getInstance on older devices. We can just use ASensorManager_getInstance on all devices and spare the mess. --- native-activity/app/src/main/cpp/main.cpp | 46 +---------------------- 1 file changed, 1 insertion(+), 45 deletions(-) diff --git a/native-activity/app/src/main/cpp/main.cpp b/native-activity/app/src/main/cpp/main.cpp index f1e8ff0b5..23bac1dc3 100644 --- a/native-activity/app/src/main/cpp/main.cpp +++ b/native-activity/app/src/main/cpp/main.cpp @@ -269,50 +269,6 @@ static void engine_handle_cmd(struct android_app* app, int32_t cmd) { } } -/* - * AcquireASensorManagerInstance(void) - * Workaround ASensorManager_getInstance() deprecation false alarm - * for Android-N and before, when compiling with NDK-r15 - */ -#include -ASensorManager* AcquireASensorManagerInstance(android_app* app) { - if (!app) return nullptr; - - typedef ASensorManager* (*PF_GETINSTANCEFORPACKAGE)(const char* name); - void* androidHandle = dlopen("libandroid.so", RTLD_NOW); - auto getInstanceForPackageFunc = (PF_GETINSTANCEFORPACKAGE)dlsym( - androidHandle, "ASensorManager_getInstanceForPackage"); - if (getInstanceForPackageFunc) { - JNIEnv* env = nullptr; - app->activity->vm->AttachCurrentThread(&env, nullptr); - - jclass android_content_Context = env->GetObjectClass(app->activity->clazz); - jmethodID midGetPackageName = env->GetMethodID( - android_content_Context, "getPackageName", "()Ljava/lang/String;"); - auto packageName = - (jstring)env->CallObjectMethod(app->activity->clazz, midGetPackageName); - - const char* nativePackageName = - env->GetStringUTFChars(packageName, nullptr); - ASensorManager* mgr = getInstanceForPackageFunc(nativePackageName); - env->ReleaseStringUTFChars(packageName, nativePackageName); - app->activity->vm->DetachCurrentThread(); - if (mgr) { - dlclose(androidHandle); - return mgr; - } - } - - typedef ASensorManager* (*PF_GETINSTANCE)(); - auto getInstanceFunc = - (PF_GETINSTANCE)dlsym(androidHandle, "ASensorManager_getInstance"); - // by all means at this point, ASensorManager_getInstance should be available - assert(getInstanceFunc); - dlclose(androidHandle); - - return getInstanceFunc(); -} - /** * This is the main entry point of a native application that is using * android_native_app_glue. It runs in its own thread, with its own @@ -328,7 +284,7 @@ void android_main(struct android_app* state) { engine.app = state; // Prepare to monitor accelerometer - engine.sensorManager = AcquireASensorManagerInstance(state); + engine.sensorManager = ASensorManager_getInstance(); engine.accelerometerSensor = ASensorManager_getDefaultSensor( engine.sensorManager, ASENSOR_TYPE_ACCELEROMETER); engine.sensorEventQueue = ASensorManager_createEventQueue( From ce292cb9f835df71e7c43ba060182ff418648b73 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Tue, 30 Apr 2024 14:22:57 -0700 Subject: [PATCH 3/3] Move sensor handling to a callback. The apparent change in `minSdkVersion` here isn't actually changing much. The NDK hasn't supported ICS in a long time, and 21 is the minimum for modern NDKs (though I think this sample is still using an NDK that supports 19, it won't soon). The raise is needed for android_set_abort_message. --- native-activity/app/build.gradle | 2 +- native-activity/app/src/main/cpp/main.cpp | 81 ++++++++++++++----- .../ndkbuild/native-activity/app/build.gradle | 2 +- 3 files changed, 63 insertions(+), 22 deletions(-) diff --git a/native-activity/app/build.gradle b/native-activity/app/build.gradle index 571085fef..ee9dba04f 100644 --- a/native-activity/app/build.gradle +++ b/native-activity/app/build.gradle @@ -12,7 +12,7 @@ android { defaultConfig { applicationId = 'com.example.native_activity' - minSdkVersion 14 + minSdkVersion 21 targetSdkVersion 34 externalNativeBuild { cmake { diff --git a/native-activity/app/src/main/cpp/main.cpp b/native-activity/app/src/main/cpp/main.cpp index 23bac1dc3..10c03ebff 100644 --- a/native-activity/app/src/main/cpp/main.cpp +++ b/native-activity/app/src/main/cpp/main.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -35,9 +36,34 @@ #define _LOG(priority, fmt, ...) \ ((void)__android_log_print((priority), (LOG_TAG), (fmt)__VA_OPT__(, ) __VA_ARGS__)) +#define LOGE(fmt, ...) _LOG(ANDROID_LOG_ERROR, (fmt)__VA_OPT__(, ) __VA_ARGS__) #define LOGW(fmt, ...) _LOG(ANDROID_LOG_WARN, (fmt)__VA_OPT__(, ) __VA_ARGS__) #define LOGI(fmt, ...) _LOG(ANDROID_LOG_INFO, (fmt)__VA_OPT__(, ) __VA_ARGS__) +[[noreturn]] __attribute__((__format__(__printf__, 1, 2))) static void fatal( + const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + char* buf; + if (vasprintf(&buf, fmt, ap) < 0) { + android_set_abort_message("failed for format error message"); + } else { + android_set_abort_message(buf); + // Also log directly, since the default Android Studio logcat filter hides + // the backtrace which would otherwise show the abort message. + LOGE("%s", buf); + } + std::abort(); +} + +#define CHECK_NOT_NULL(value) \ + do { \ + if ((value) == nullptr) { \ + fatal("%s:%d:%s must not be null", __PRETTY_FUNCTION__, __LINE__, \ + #value); \ + } \ + } while (false) + /** * Our saved state data. */ @@ -64,6 +90,20 @@ struct engine { int32_t width; int32_t height; struct saved_state state; + + void CreateSensorListener(ALooper_callbackFunc callback) { + CHECK_NOT_NULL(app); + + sensorManager = ASensorManager_getInstance(); + if (sensorManager == nullptr) { + return; + } + + accelerometerSensor = ASensorManager_getDefaultSensor( + sensorManager, ASENSOR_TYPE_ACCELEROMETER); + sensorEventQueue = ASensorManager_createEventQueue( + sensorManager, app->looper, ALOOPER_POLL_CALLBACK, callback, this); + } }; /** @@ -269,6 +309,24 @@ static void engine_handle_cmd(struct android_app* app, int32_t cmd) { } } +int OnSensorEvent(int /* fd */, int /* events */, void* data) { + CHECK_NOT_NULL(data); + engine* engine = reinterpret_cast(data); + + CHECK_NOT_NULL(engine->accelerometerSensor); + ASensorEvent event; + while (ASensorEventQueue_getEvents(engine->sensorEventQueue, &event, 1) > 0) { + LOGI("accelerometer: x=%f y=%f z=%f", event.acceleration.x, + event.acceleration.y, event.acceleration.z); + } + + // From the docs: + // + // Implementations should return 1 to continue receiving callbacks, or 0 to + // have this file descriptor and callback unregistered from the looper. + return 1; +} + /** * This is the main entry point of a native application that is using * android_native_app_glue. It runs in its own thread, with its own @@ -284,11 +342,7 @@ void android_main(struct android_app* state) { engine.app = state; // Prepare to monitor accelerometer - engine.sensorManager = ASensorManager_getInstance(); - engine.accelerometerSensor = ASensorManager_getDefaultSensor( - engine.sensorManager, ASENSOR_TYPE_ACCELEROMETER); - engine.sensorEventQueue = ASensorManager_createEventQueue( - engine.sensorManager, state->looper, LOOPER_ID_USER, nullptr, nullptr); + engine.CreateSensorListener(OnSensorEvent); if (state->savedState != nullptr) { // We are starting with a previous saved state; restore from it. @@ -299,32 +353,19 @@ void android_main(struct android_app* state) { while (true) { // Read all pending events. - int ident; int events; struct android_poll_source* source; // If not animating, we will block forever waiting for events. // If animating, we loop until all events are read, then continue // to draw the next frame of animation. - while ((ident = ALooper_pollAll(engine.animating ? 0 : -1, nullptr, &events, - (void**)&source)) >= 0) { + while ((ALooper_pollAll(engine.animating ? 0 : -1, nullptr, &events, + (void**)&source)) >= 0) { // Process this event. if (source != nullptr) { source->process(state, source); } - // If a sensor has data, process it now. - if (ident == LOOPER_ID_USER) { - if (engine.accelerometerSensor != nullptr) { - ASensorEvent event; - while (ASensorEventQueue_getEvents(engine.sensorEventQueue, &event, - 1) > 0) { - LOGI("accelerometer: x=%f y=%f z=%f", event.acceleration.x, - event.acceleration.y, event.acceleration.z); - } - } - } - // Check if we are exiting. if (state->destroyRequested != 0) { engine_term_display(&engine); diff --git a/other-builds/ndkbuild/native-activity/app/build.gradle b/other-builds/ndkbuild/native-activity/app/build.gradle index a59eda4fa..38d641124 100644 --- a/other-builds/ndkbuild/native-activity/app/build.gradle +++ b/other-builds/ndkbuild/native-activity/app/build.gradle @@ -12,7 +12,7 @@ android { defaultConfig { applicationId = 'com.example.native_activity' - minSdkVersion 14 + minSdkVersion 21 targetSdkVersion 33 } sourceSets {