Surface test-device whitelisting: document it, and report the advertising ID - #3
Surface test-device whitelisting: document it, and report the advertising ID#3nieny225 wants to merge 3 commits into
Conversation
antonurankar-moloco
left a comment
There was a problem hiding this comment.
This solution doesn't support Android but only iOS.
Unity removed Android support from this API around Unity version 2020
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.
9cdb79c to
84037cf
Compare
|
@antonurankar-moloco You're right, thanks — and the PR description was wrong about it too, since it claimed Fixed in 84037cf by splitting the resolve. iOS keeps the Unity API; Android goes to 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 Branch is rebased onto main (past #4 and #5, so the hunks moved to |
There was a problem hiding this comment.
🟡 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.mdandDemoConfig.cs. - Added a new
DemoAdvertisingIdutility 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()}"); |
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.csalready resolves ATT beforeInitializefor exactly this class of reason, but nothing connected that to whitelisting.What changed
Docs — a
### Test devicessection inREADME.md, and the same point in theDemoConfig.csheader 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 callCloudXSdk.Initialize(GeneralScreen,FirstLookScreen), and carries a short verdict on the General screen's initialization status line.Reading the advertising ID on each platform
Application.RequestAdvertisingIdentifierAsyncis documented as "an advertising ID for iOS and UWP" — Unity dropped the Android implementation, and on Android the call returnsfalse. So the resolve is split:AdvertisingIdClient.getAdvertisingIdInfo. That throws if called on the main thread, so it runs on a background worker attached to the JVM by hand; everyAndroidJavaObjectis disposed on that same thread and only strings and bools cross back, behind avolatileflag written last. The existingResolve()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-identifierreached the classpath only transitively, via the Google Mobile Ads plugin that just the First Look flow needs. Now declared inCloudXDemoDependencies.xml.com.google.android.gms.permission.AD_IDlikewise 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 —
CloudXSdkConfigurationis 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
adb shell settings get secure advertising_idIllegalStateException: Calling this from your main thread…and no ANR in logcatCloudX | AdMobstatus line is untouchedCompile-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_ANDROIDand withUNITY_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
mainafter #4 and #5, which renamedHomeScreen.cs→GeneralScreen.csand added the First Look flow.🤖 Generated with Claude Code