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.
This commit is contained in:
LibretroAdmin authored and libretroadmin committed 2026-10-30 14:09:02 +01:00
1 parent 5ba7f8c8cb
commit e6e980de33
2 files changed
+61 -29

No files matched your search

+15 -28
View File
@@ -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);
}
@@ -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++)
{