Skip to content

feat: TTID pre-launch capture for cross-platform SDKs support#3635

Draft
sbarrio wants to merge 2 commits into
developfrom
sbarrio/RUM-16664/pre-launch-ttid-module
Draft

feat: TTID pre-launch capture for cross-platform SDKs support#3635
sbarrio wants to merge 2 commits into
developfrom
sbarrio/RUM-16664/pre-launch-ttid-module

Conversation

@sbarrio

@sbarrio sbarrio commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Builds on the work developed by @marco-saia-datadog here: #3371 and updates it so it properly works and reports TTID on Android on cross platform SDKs that include the new com.datadoghq:dd-sdk-android-rum-prelaunch module.

This PR adds three things:

  1. New AppLaunchPreInitCollector in dd-sdk-android-internal. It's a singleton that collects timing data before the SDK initializes — process start time, first Activity.onCreate, and first frame drawn. State transitions use atomic compare-and-swap (NOT_INSTALLED → IDLE → CAPTURING / CLAIMED → COMPLETE) so the collector and the SDK can't race on who drives startup.

  2. New dd-sdk-android-rum-prelaunch module with a single ContentProvider (AppLaunchCollectorProvider) that installs the collector automatically at process start. No customer code required.

  3. RumFeature.initRumAppStartupDetector() now checks collector state on init: if data is already captured, read it; if capture is in progress, subscribe; if not installed or the SDK got there first, fall back to the existing RumAppStartupDetector flow unchanged.

RumFirstDrawTimeReporter and WindowCallbacksRegistry are also moved to dd-sdk-android-internal, since both paths need them now. The originals in dd-sdk-android-rum are deleted.

On top of Marco's work, this PR fixes two issues that prevented the feature from working correctly:

1. TTID/TTFD events not assigned to a view

The app start and TTID events were being sent before the first RUM view was started, so they weren't attached to any view in the session. The fix defers those events via a pendingPreLaunchAction that is dispatched on the main thread after GlobalRumMonitor.registerIfAbsent() runs, ensuring the view is already open when the events arrive.

2. Memory leak in RumFirstDrawTimeReporterImpl

When subscribing to first-frame events for an activity that never calls setContentView() (e.g. an interstitial that just calls startActivity + finish()), WindowCallbacksRegistry wraps the Activity's Window.Callback and stores it in a WeakHashMap<Activity, WindowCallback>. Because Activity itself implements Window.Callback, WindowCallback ends up holding a strong reference back to the map key, preventing GC from ever collecting it. The WindowCallbackListener that would clean up this entry only fires on onContentChanged — which never happens if setContentView is never called. This caused NoLeakAssertionFailedError in all TTID auto-forwarding integration tests.

The fix registers an Application.ActivityLifecycleCallbacks alongside each WindowCallbackListener. If the Activity is destroyed before setContentView is called, the callback removes the listener and breaks the strong reference. Both the Activity and the listener are held as WeakReference inside the cleanup callback so the registration itself creates no new retention path.

Motivation

React Native and Flutter initialize the Datadog SDK from JS/Dart, well after the first activity has launched. TTID goes unreported for those SDKs unless you add native initialization (DdSdkNativeInitialization.initFromNative()), which means native Android code in a cross-platform project.

The collector sidesteps this. By the time the SDK starts, the timing data is already waiting.

Native Android apps are unaffected. If the SDK initializes before the first activity, it claims the collector and the existing RumAppStartupDetector path runs exactly as before.

Additional Notes

  • dd-sdk-android-rum-prelaunch is opt-in. Cross-platform SDKs depend on it; native apps don't.
  • RumFirstDrawTimeReporterImpl takes an injectable warnLogger lambda. RumFeature passes one that routes through sdkCore.internalLogger with Target.TELEMETRY + Target.USER. The pre-init path defaults to Log.w since there's no SDK available at that point.
  • API surfaces updated for both modules. No public API change.
  • Tested on the example React Native SDK example app here: [RUM 16664] Report TTID and TTFD on RN apps dd-sdk-reactnative#1336

Review checklist (to be filled by reviewers)

  • Feature or bugfix MUST have appropriate tests (unit, integration, e2e)
  • Make sure you discussed the feature or bugfix with the maintaining team in an Issue
  • Make sure each commit and the PR mention the Issue number (cf the CONTRIBUTING doc)

@sbarrio sbarrio changed the title Sbarrio/rum 16664/pre launch ttid module feat: TTID pre-launch capture for cross-platform SDKs support Jul 15, 2026
@sbarrio sbarrio force-pushed the sbarrio/RUM-16664/pre-launch-ttid-module branch from 29d98ba to 75a5402 Compare July 15, 2026 15:23
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.

2 participants