From e6e980de3344679b35951de134409e86c6637c1a Mon Sep 17 00:00:00 2001 From: LibretroAdmin Date: Fri, 30 Oct 2026 14:09:25 +0100 Subject: [PATCH] android: Play Store builds deliver cores through Play again play_feature_delivery_enabled() tested the isMetaQuestBuild method ID rather than the flag read through it. The ID resolves on every build, so feature delivery was off everywhere and Play Store builds offered the whole buildbot core list through the core downloader. The answer now comes from the build flags the frontend reads from Java at init, and is not cached until it has read them. The play_feature_delivery sample checks Play Store, Meta Quest and other builds with both method IDs resolved, as on a device. --- os/android/play_feature_delivery.c | 43 ++++++----------- .../play_feature_delivery_test.c | 47 ++++++++++++++++++- 2 files changed, 61 insertions(+), 29 deletions(-) diff --git a/os/android/play_feature_delivery.c b/os/android/play_feature_delivery.c index edbbcf215c..f1325fb68c 100644 --- a/os/android/play_feature_delivery.c +++ b/os/android/play_feature_delivery.c @@ -56,11 +56,10 @@ typedef struct retro_atomic_int_t name_hash; retro_atomic_int_t waiter; /* Cached result of play_feature_delivery_enabled_internal(): - * -1 = not yet queried, 0 = disabled, 1 = enabled. Queried + * -1 = not yet known, 0 = disabled, 1 = enabled. Queried * frequently, often in loops, so the fast path is a single - * acquire load with no lock. A cold race can at worst issue - * the (idempotent) Java query twice; both stores write the - * same value. */ + * acquire load with no lock. A cold race can at worst work + * the answer out twice; both stores write the same value. */ retro_atomic_int_t enabled_cached; } play_feature_delivery_state_t; @@ -247,26 +246,19 @@ static bool play_feature_delivery_get_core_name( return true; } -/* Returns true if current build utilises - * play feature delivery - * > Relies on a Java function call, and is - * therefore slow */ -static bool play_feature_delivery_enabled_internal(void) +/* 1 if the current build utilises play feature + * delivery, 0 if not, -1 while the frontend has + * not yet read the build flags from Java. + * > Play Store builds do, except the Meta Quest + * build, which uses the core downloader */ +static int play_feature_delivery_enabled_internal(void) { - JNIEnv *env = jni_thread_getenv(); - struct android_app *app = (struct android_app*)g_android; - bool enabled = false; + const struct android_app *app = (const struct android_app*)g_android; - if (!env || - !app || - !app->isPlayStoreBuild || - app->isMetaQuestBuild) - return false; + if (!app || !app->isPlayStoreBuild) + return -1; - CALL_BOOLEAN_METHOD(env, enabled, app->activity->clazz, - app->isPlayStoreBuild); - - return enabled; + return (app->is_play_store_build && !app->is_meta_quest_build) ? 1 : 0; } /* Returns true if current build utilises @@ -274,17 +266,12 @@ static bool play_feature_delivery_enabled_internal(void) bool play_feature_delivery_enabled(void) { play_feature_delivery_state_t* state = play_feature_delivery_get_state(); - - /* Calling Java functions is slow. We need to - * check Play Store build status frequently, - * often in loops, so rely on a cached global - * status flag instead dealing with Java - * interfaces */ int cached = retro_atomic_load_acquire_int(&state->enabled_cached); if (cached < 0) { - cached = play_feature_delivery_enabled_internal() ? 1 : 0; + if ((cached = play_feature_delivery_enabled_internal()) < 0) + return false; retro_atomic_store_release_int(&state->enabled_cached, cached); } diff --git a/samples/frontend/play_feature_delivery/play_feature_delivery_test.c b/samples/frontend/play_feature_delivery/play_feature_delivery_test.c index bde26b70a0..5817f2c991 100644 --- a/samples/frontend/play_feature_delivery/play_feature_delivery_test.c +++ b/samples/frontend/play_feature_delivery/play_feature_delivery_test.c @@ -3,7 +3,10 @@ * * The stub Java answers a download inside the call, on the calling * thread; a watchdog turns a download that never returns into a - * failure rather than a hang. Status updates for another core, or + * failure rather than a hang. Play Store builds deliver cores through + * Play, every other build (the Meta Quest one included) through the + * core downloader, and the answer is not settled before the frontend + * has read the build flags. Status updates for another core, or * for a transaction that has ended, are dropped; and the status a * poller reads is always one whole update, with Java's thread, the * downloads and the poller all running at once. */ @@ -74,6 +77,46 @@ static ANativeActivity test_activity; static struct android_app test_app; struct android_app *g_android = &test_app; +/* ---- which builds deliver cores through Play ---------------------- */ + +static void check_build(bool play_store, bool quest, bool want) +{ + bool got; + retro_atomic_store_release_int( + &play_feature_delivery_state.enabled_cached, -1); + test_app.is_play_store_build = play_store; + test_app.is_meta_quest_build = quest; + got = play_feature_delivery_enabled(); + CHECK(got == want, "play_store %d quest %d: delivery %s, want %s", + play_store, quest, got ? "on" : "off", want ? "on" : "off"); + /* Asked again, from the cache. */ + CHECK(play_feature_delivery_enabled() == want, + "play_store %d quest %d: the cached answer changed", + play_store, quest); +} + +static void check_builds(void) +{ + /* Before the frontend has read the flags: off, and not remembered. */ + test_app.is_play_store_build = true; + CHECK(!play_feature_delivery_enabled(), + "delivery was on before the build flags were read"); + CHECK(retro_atomic_load_acquire_int( + &play_feature_delivery_state.enabled_cached) == -1, + "an answer given before the build flags were read was cached"); + + /* Both Java methods resolve on every build, as on a device. */ + test_app.isPlayStoreBuild = (jmethodID)&test_activity; + test_app.isMetaQuestBuild = (jmethodID)&test_app; + + check_build(true, false, true); /* Play Store */ + check_build(true, true, false); /* Meta Quest */ + check_build(false, false, false); /* everything else */ + + retro_atomic_store_release_int( + &play_feature_delivery_state.enabled_cached, -1); +} + /* ---- the test ------------------------------------------------------ */ static retro_atomic_int_t returned; @@ -145,6 +188,8 @@ int main(void) test_app.activity = &test_activity; test_app.downloadCore = (jmethodID)&test_app; + check_builds(); + t = sthread_create(do_download, NULL); for (i = 0; i < 2000 && !retro_atomic_load_acquire_int(&returned); i++) {