Skip to content

Surface test-device whitelisting: document it, and report the advertising ID - #3

Open
nieny225 wants to merge 3 commits into
mainfrom
docs/test-device-whitelisting-and-ifa-caveat
Open

Surface test-device whitelisting: document it, and report the advertising ID#3
nieny225 wants to merge 3 commits into
mainfrom
docs/test-device-whitelisting-and-ifa-caveat

Conversation

@nieny225

@nieny225 nieny225 commented Aug 24, 2026

Copy link
Copy Markdown

Why

The demo README covers how a publisher points the sample at their own CloudX app — replace the app key and ad unit IDs in DemoConfig.cs, set the matching bundle identifier — and explains that bid requests are authorized per app key and bundle ID.

What it doesn't say is that test mode is server-controlled. A publisher who swaps the demo IDs for their own still has to register the device's advertising ID in the CloudX dashboard; there's no code change that does it.

The second half is the part that costs real debugging time: the advertising ID reads back as all zeros on an opted-out device. A zeroed ID is a well-formed UUID — it pastes into the dashboard without complaint and then matches nothing, so the failure is indistinguishable from a wrong dashboard entry. GeneralScreen.cs already resolves ATT before Initialize for exactly this class of reason, but nothing connected that to whitelisting.

What changed

Docs — a ### Test devices section in README.md, and the same point in the DemoConfig.cs header comment since that's the file a publisher actually opens to swap the IDs.

Code — the demo logs the advertising ID in full at startup ([CloudXUnityDemo] Advertising ID: …) from both entry points that call CloudXSdk.Initialize (GeneralScreen, FirstLookScreen), and carries a short verdict on the General screen's initialization status line.

Reading the advertising ID on each platform

Application.RequestAdvertisingIdentifierAsync is documented as "an advertising ID for iOS and UWP" — Unity dropped the Android implementation, and on Android the call returns false. So the resolve is split:

  • iOS keeps the Unity API.
  • Android asks Google Play services for AdvertisingIdClient.getAdvertisingIdInfo. That throws if called on the main thread, so it runs on a background worker attached to the JVM by hand; every AndroidJavaObject is disposed on that same thread and only strings and bools cross back, behind a volatile flag written last. The existing Resolve() coroutine polls that flag exactly as the ATT gate polls its own.

Splitting the platforms is also what makes the advice correct, which one shared call could not be. A zeroed ID means ATT was declined on iOS and ad personalization is off on Android — different settings, different screens — and the union of both was wrong on either. Android also has a case iOS does not: a real ID with limit ad tracking on, which is registerable but still bids do-not-track, so it reads as a dashboard problem and is not one.

Two supporting declarations, neither of which changes the merged manifest today:

  • play-services-ads-identifier reached the classpath only transitively, via the Google Mobile Ads plugin that just the First Look flow needs. Now declared in CloudXDemoDependencies.xml.
  • com.google.android.gms.permission.AD_ID likewise arrived only from that library's manifest. Declaring it stops the target-SDK-33+ requirement (this project targets 36) being something a publisher inherits by accident.

What it deliberately does not claim

It reports the health of the ID, never that the device is registered. No CloudX SDK exposes the resolved test flag — CloudXSdkConfiguration is an empty record here, and the Android and iOS counterparts are equally empty — so "ok" means the ID is worth registering, not that the dashboard entry took effect. Claiming otherwise would send people to debug their integration when their account is the problem.

A timeout now leaves the state unresolved rather than recording a failure, so a late answer still reaches the status line instead of being poisoned by a "timed out" error.

Test plan

  • Build to an Android device, confirm the logged ID matches adb shell settings get secure advertising_id
  • Turn off ad personalization (Settings → Google → Ads → Delete advertising ID), relaunch, confirm the zeroed verdict names the Android cause
  • On a pre-Android-12 device, opt out of ads personalization and confirm the limit-ad-tracking verdict
  • Confirm no IllegalStateException: Calling this from your main thread… and no ANR in logcat
  • Build to an iOS device, deny ATT (or reinstall), confirm the zeroed verdict names ATT
  • Confirm the status line stays readable in landscape, where it compacts to 300x28
  • Confirm the First Look screen logs the ID and its CloudX | AdMob status line is untouched

Compile-checked so far: Unity 6000.0.60f1 batch import (Editor target, 0 errors, no warnings in these files), plus the demo scripts compiled against the project's own assemblies with UNITY_ANDROID and with UNITY_IOS. Device runs above are still to do.

Notes

Found while using this demo as a reference integration. The equivalent lands in the CloudX Android, iOS and Unity sample apps so the four stay consistent.

Rebased onto main after #4 and #5, which renamed HomeScreen.csGeneralScreen.cs and added the First Look flow.

🤖 Generated with Claude Code

@nieny225 nieny225 changed the title Document test-device whitelisting and the IFA-non-zero caveat Surface test-device whitelisting: document it, and report the advertising ID Aug 25, 2026

@antonurankar-moloco antonurankar-moloco left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This solution doesn't support Android but only iOS.
Unity removed Android support from this API around Unity version 2020

nieny225 and others added 3 commits September 3, 2026 14:52
The demo explains how to swap in your own app key, ad unit IDs and bundle
identifier, but nothing says test mode is server-controlled — a publisher
replacing the demo IDs with their own has to register the device's advertising
ID in the CloudX dashboard as well.

Adds that, plus the part that is easy to lose an afternoon to: the advertising
ID reads back as all zeros until tracking consent is granted on the device (App
Tracking Transparency on iOS, ad-personalization consent on Android), so a
device can silently fail whitelisting for a reason that looks identical to a
wrong dashboard entry.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Qe7sVn74q6KtnJiRk5P2c
The README now tells a publisher the advertising ID has to be on the
dashboard's test-device list and that it reads back as all zeros without
tracking consent. Neither is checkable from the demo: nothing prints the ID,
and a zeroed one is invisible unless you already recognize the all-zeros UUID
on sight. It is well-formed, so it pastes into the dashboard without complaint
and then matches nothing -- which looks like a wrong dashboard entry rather
than a consent problem.

The demo now logs the ID in full at startup and carries a short usable/zeroed
verdict on the existing status line.

Deliberately not a new screen or a new scene control: the UI comes from
HomeScene.unity as serialized fields, with no scrollable area, and the
landscape reflow snapshots controls at Bind time -- a runtime-added label
would sit outside that and risk the rotated layout. The log is where this demo
already surfaces everything, having no on-screen log at all.

Application.RequestAdvertisingIdentifierAsync covers both platforms in one
call, so this needs no native plugin and no AdSupport link.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Qe7sVn74q6KtnJiRk5P2c
Application.RequestAdvertisingIdentifierAsync is documented as "an advertising
ID for iOS and UWP". Unity dropped the Android implementation, so on Android the
call returns false and the demo reported "unavailable (not available on this
platform)" -- on the platform where CloudX test-device whitelisting is most
often what someone is debugging.

Split the resolve by platform. iOS keeps the Unity API; Android asks Google Play
services for AdvertisingIdClient.getAdvertisingIdInfo, which throws if called on
the main thread, so it runs on a background worker attached to the JVM by hand.
Only strings and bools cross back to the Unity thread, behind a volatile flag
written last, and the coroutine polls that flag the way the ATT gate already
polls its own. A timeout now leaves the state unresolved rather than recording a
failure, so a late answer still reaches the status line.

Splitting the platforms is also what makes the advice correct. A zeroed ID means
ATT was declined on iOS and ad personalization is off on Android -- different
settings, different screens -- and the union of both was wrong on either. Android
adds a case iOS does not have: a real ID with limit ad tracking on, which is
registerable but still bids do-not-track, so it reads as a dashboard problem and
is not one.

play-services-ads-identifier only reached the classpath transitively through the
Google Mobile Ads plugin, which just the First Look flow needs, so declare it.
com.google.android.gms.permission.AD_ID likewise arrived only from that library's
manifest; declaring it changes nothing in the merged manifest but stops the
target-SDK-33+ requirement being something a publisher inherits by accident.

FirstLookScreen has its own CloudXSdk.Initialize, so it logs the ID as well.
@nieny225
nieny225 force-pushed the docs/test-device-whitelisting-and-ifa-caveat branch from 9cdb79c to 84037cf Compare September 3, 2026 06:59
@nieny225

nieny225 commented Sep 3, 2026

Copy link
Copy Markdown
Author

@antonurankar-moloco You're right, thanks — and the PR description was wrong about it too, since it claimed RequestAdvertisingIdentifierAsync was one cross-platform call. Unity's own reference now reads "an advertising ID for iOS and UWP", and on Android the call just returns false, so the demo was logging unavailable (not available on this platform) on the platform where test-device whitelisting is most often what someone is debugging.

Fixed in 84037cf by splitting the resolve. iOS keeps the Unity API; Android goes to AdvertisingIdClient.getAdvertisingIdInfo in Play services, on a background worker with a manual AndroidJNI.AttachCurrentThread because that call throws if it's made on the main thread. Only strings and bools come back to the Unity thread, behind a volatile flag written last, and the existing coroutine polls it the way the ATT gate already polls its own.

Splitting them also fixed the wording, which one shared call couldn't have: a zeroed ID means ATT was declined on iOS and ad personalization is off on Android, so the demo now names the right setting per platform. Android also gets a case iOS doesn't have — a real ID with limit ad tracking on, registerable but still bidding do-not-track.

Also declared play-services-ads-identifier and com.google.android.gms.permission.AD_ID explicitly. Both only reached the build transitively through the Google Mobile Ads plugin that just the First Look flow needs, so neither changes the merged manifest — it's so a publisher copying this sees the target-SDK-33+ requirement instead of inheriting it.

Branch is rebased onto main (past #4 and #5, so the hunks moved to GeneralScreen.cs and FirstLookScreen.cs now logs it too). Compile-checked for both player targets; device runs are still on the test plan.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

There’s an Android worker-thread error path that can leave the ad ID permanently unresolved, and the First Look advertising-ID log prefix currently doesn’t match what the README instructs users to search/copy.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR improves the Unity demo’s “test mode” troubleshooting by documenting server-controlled test-device whitelisting and surfacing the device advertising ID (plus a short “health” verdict) so publishers can correctly register test devices in the CloudX dashboard.

Changes:

  • Documented CloudX test-device behavior and the “all-zero” advertising ID trap in README.md and DemoConfig.cs.
  • Added a new DemoAdvertisingId utility and integrated it into both demo entry points to log the advertising ID at startup; General screen also appends a short verdict to the initialization status.
  • Declared Android dependencies/permissions needed to reliably read the advertising ID (play-services-ads-identifier + com.google.android.gms.permission.AD_ID).
File summaries
File Description
README.md Adds a “Test devices” section explaining whitelisting and zeroed ID behavior.
Assets/Scripts/GeneralScreen.cs Resolves + logs advertising ID before init; appends ad-ID verdict to init status.
Assets/Scripts/FirstLook/FirstLookScreen.cs Resolves + logs advertising ID before init for the First Look flow.
Assets/Scripts/Editor/CloudXDemoDependencies.xml Declares play-services-ads-identifier explicitly for the demo.
Assets/Scripts/DemoConfig.cs Adds header guidance about server-controlled test mode and zeroed IDs.
Assets/Scripts/DemoAdvertisingId.cs New demo-only cross-platform advertising ID resolver (Unity API on iOS, Play Services on Android).
Assets/Scripts/DemoAdvertisingId.cs.meta Meta for the new script asset.
Assets/Plugins/Android/AndroidManifest.xml Explicitly declares com.google.android.gms.permission.AD_ID for targetSdk 33+.
Review details

Files not reviewed (1)

  • Assets/Scripts/DemoAdvertisingId.cs.meta: Generated file
  • Files reviewed: 7/8 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

{
var worker = new Thread(() =>
{
AndroidJNI.AttachCurrentThread();
* "CloudX | AdMob" summary, so the verdict stays in the log.
*/
yield return DemoAdvertisingId.Resolve();
Log($"Advertising ID: {DemoAdvertisingId.Describe()}");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants